xref: /haiku/src/apps/haikudepot/ui/FeaturedPackagesView.cpp (revision 4abe18063a98eec087e9e3a88276453ca0979dc4)
1 /*
2  * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 #include "FeaturedPackagesView.h"
8 
9 #include <stdio.h>
10 
11 #include <Catalog.h>
12 #include <Font.h>
13 #include <LayoutBuilder.h>
14 #include <Message.h>
15 #include <ScrollView.h>
16 #include <StringView.h>
17 #include <SpaceLayoutItem.h>
18 
19 #include "BitmapView.h"
20 #include "HaikuDepotConstants.h"
21 #include "MainWindow.h"
22 #include "MarkupTextView.h"
23 #include "MessagePackageListener.h"
24 #include "RatingView.h"
25 #include "ScrollableGroupView.h"
26 
27 
28 #undef B_TRANSLATION_CONTEXT
29 #define B_TRANSLATION_CONTEXT "FeaturedPackagesView"
30 
31 
32 static const rgb_color kLightBlack = (rgb_color){ 60, 60, 60, 255 };
33 
34 static BitmapRef sInstalledIcon(new(std::nothrow)
35 	SharedBitmap(RSRC_INSTALLED), true);
36 
37 
38 // #pragma mark - PackageView
39 
40 
41 class PackageView : public BGroupView {
42 public:
43 	PackageView()
44 		:
45 		BGroupView("package view", B_HORIZONTAL),
46 		fPackageListener(
47 			new(std::nothrow) OnePackageMessagePackageListener(this)),
48 		fSelected(false)
49 	{
50 		SetViewUIColor(B_LIST_BACKGROUND_COLOR);
51 		SetHighUIColor(B_LIST_ITEM_TEXT_COLOR);
52 		SetEventMask(B_POINTER_EVENTS);
53 
54 		// Featured icon package should be scaled to 64x64
55 		fIconView = new BitmapView("package icon view");
56 		fIconView->SetExplicitMinSize(BSize(64, 64));
57 
58 		fInstalledIconView = new BitmapView("installed icon view");
59 		fTitleView = new BStringView("package title view", "");
60 		fPublisherView = new BStringView("package publisher view", "");
61 
62 		// Title font
63 		BFont font;
64 		GetFont(&font);
65 		font_family family;
66 		font_style style;
67 		font.SetSize(ceilf(font.Size() * 1.8f));
68 		font.GetFamilyAndStyle(&family, &style);
69 		font.SetFamilyAndStyle(family, "Bold");
70 		fTitleView->SetFont(&font);
71 
72 		// Publisher font
73 		GetFont(&font);
74 		font.SetSize(std::max(9.0f, floorf(font.Size() * 0.92f)));
75 		font.SetFamilyAndStyle(family, "Italic");
76 		fPublisherView->SetFont(&font);
77 
78 		// Summary text view
79 		fSummaryView = new BTextView("package summary");
80 		fSummaryView->MakeSelectable(false);
81 		fSummaryView->MakeEditable(false);
82 		font = BFont(be_plain_font);
83 		rgb_color color = HighColor();
84 		fSummaryView->SetFontAndColor(&font, B_FONT_ALL, &color);
85 
86 		// Rating view
87 		fRatingView = new RatingView("package rating view");
88 
89 		fAvgRating = new BStringView("package average rating", "");
90 		fAvgRating->SetFont(&font);
91 
92 		fVoteInfo = new BStringView("package vote info", "");
93 		// small font
94 		GetFont(&font);
95 		font.SetSize(std::max(9.0f, floorf(font.Size() * 0.85f)));
96 		fVoteInfo->SetFont(&font);
97 		fVoteInfo->SetHighUIColor(HighUIColor());
98 
99 		BLayoutBuilder::Group<>(this)
100 			.Add(fIconView)
101 			.AddGroup(B_VERTICAL, 1.0f, 2.2f)
102 				.AddGroup(B_HORIZONTAL)
103 					.Add(fTitleView)
104 					.Add(fInstalledIconView)
105 					.AddGlue()
106 				.End()
107 				.Add(fPublisherView)
108 				.SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
109 			.End()
110 			.AddGlue(0.1f)
111 			.AddGroup(B_HORIZONTAL, 0.8f)
112 				.Add(fRatingView)
113 				.Add(fAvgRating)
114 				.Add(fVoteInfo)
115 			.End()
116 			.AddGlue(0.2f)
117 			.Add(fSummaryView, 2.0f)
118 
119 			.SetInsets(B_USE_WINDOW_INSETS)
120 		;
121 
122 		Clear();
123 	}
124 
125 	virtual ~PackageView()
126 	{
127 		fPackageListener->SetPackage(PackageInfoRef(NULL));
128 		delete fPackageListener;
129 	}
130 
131 	virtual void AllAttached()
132 	{
133 		for (int32 index = 0; index < CountChildren(); ++index) {
134 			ChildAt(index)->SetViewUIColor(ViewUIColor());
135 			ChildAt(index)->SetLowUIColor(ViewUIColor());
136 			ChildAt(index)->SetHighUIColor(HighUIColor());
137 		}
138 	}
139 
140 	virtual void MessageReceived(BMessage* message)
141 	{
142 		switch (message->what) {
143 			case B_MOUSE_WHEEL_CHANGED:
144 				Window()->PostMessage(message, Parent());
145 				break;
146 
147 			case MSG_UPDATE_PACKAGE:
148 			{
149 				uint32 changes = 0;
150 				if (message->FindUInt32("changes", &changes) != B_OK)
151 					break;
152 				UpdatePackage(changes, fPackageListener->Package());
153 				break;
154 			}
155 
156 			case B_COLORS_UPDATED:
157 			{
158 				if (message->HasColor(ui_color_name(B_LIST_ITEM_TEXT_COLOR)))
159 					_UpdateColors();
160 				break;
161 			}
162 		}
163 	}
164 
165 	virtual void MouseDown(BPoint where)
166 	{
167 		BRect bounds = Bounds();
168 		BRect parentBounds = Parent()->Bounds();
169 		ConvertFromParent(&parentBounds);
170 		bounds = bounds & parentBounds;
171 
172 		if (bounds.Contains(where) && Window()->IsActive()) {
173 			BMessage message(MSG_PACKAGE_SELECTED);
174 			message.AddString("name", PackageName());
175 			Window()->PostMessage(&message);
176 		}
177 	}
178 
179 	void SetPackage(const PackageInfoRef& package)
180 	{
181 		fPackageListener->SetPackage(package);
182 
183 		_SetIcon(package->Icon());
184 		_SetInstalled(package->State() == ACTIVATED);
185 
186 		fTitleView->SetText(package->Title());
187 
188 		BString publisher = package->Publisher().Name();
189 		fPublisherView->SetText(publisher);
190 
191 		BString summary = package->ShortDescription();
192 		fSummaryView->SetText(summary);
193 
194 		_SetRating(package->CalculateRatingSummary());
195 
196 		InvalidateLayout();
197 		Invalidate();
198 	}
199 
200 	void UpdatePackage(uint32 changeMask, const PackageInfoRef& package)
201 	{
202 		if ((changeMask & PKG_CHANGED_TITLE) != 0)
203 			fTitleView->SetText(package->Title());
204 		if ((changeMask & PKG_CHANGED_SUMMARY) != 0)
205 			fSummaryView->SetText(package->ShortDescription());
206 		if ((changeMask & PKG_CHANGED_RATINGS) != 0)
207 			_SetRating(package->CalculateRatingSummary());
208 		if ((changeMask & PKG_CHANGED_STATE) != 0)
209 			_SetInstalled(package->State() == ACTIVATED);
210 		if ((changeMask & PKG_CHANGED_ICON) != 0)
211 			_SetIcon(package->Icon());
212 	}
213 
214 	void Clear()
215 	{
216 		fPackageListener->SetPackage(PackageInfoRef(NULL));
217 
218 		fIconView->UnsetBitmap();
219 		fInstalledIconView->UnsetBitmap();
220 		fTitleView->SetText("");
221 		fPublisherView->SetText("");
222 		fSummaryView->SetText("");
223 		fRatingView->SetRating(-1.0f);
224 		fAvgRating->SetText("");
225 		fVoteInfo->SetText("");
226 	}
227 
228 	const char* PackageTitle() const
229 	{
230 		return fTitleView->Text();
231 	}
232 
233 	const char* PackageName() const
234 	{
235 		if (fPackageListener->Package().Get() != NULL)
236 			return fPackageListener->Package()->Name();
237 		else
238 			return "";
239 	}
240 
241 	void SetSelected(bool selected)
242 	{
243 		if (fSelected == selected)
244 			return;
245 		fSelected = selected;
246 
247 		_UpdateColors();
248 	}
249 
250 	void _UpdateColors()
251 	{
252 		color_which bgColor = B_LIST_BACKGROUND_COLOR;
253 		color_which textColor = B_LIST_ITEM_TEXT_COLOR;
254 
255 		if (fSelected) {
256 			bgColor = B_LIST_SELECTED_BACKGROUND_COLOR;
257 			textColor = B_LIST_SELECTED_ITEM_TEXT_COLOR;
258 		}
259 
260 		List<BView*, true> views;
261 
262 		views.Add(this);
263 		views.Add(fIconView);
264 		views.Add(fInstalledIconView);
265 		views.Add(fTitleView);
266 		views.Add(fPublisherView);
267 		views.Add(fSummaryView);
268 		views.Add(fRatingView);
269 		views.Add(fAvgRating);
270 		views.Add(fVoteInfo);
271 
272 		for (int32 i = 0; i < views.CountItems(); i++) {
273 			BView* view = views.ItemAtFast(i);
274 
275 			view->SetViewUIColor(bgColor);
276 			view->SetLowUIColor(bgColor);
277 			view->SetHighUIColor(textColor);
278 			view->Invalidate();
279 		}
280 
281 		BFont font(be_plain_font);
282 		rgb_color color = HighColor();
283 		fSummaryView->SetFontAndColor(&font, B_FONT_ALL, &color);
284 	}
285 
286 	void _SetRating(const RatingSummary& ratingSummary)
287 	{
288 		fRatingView->SetRating(ratingSummary.averageRating);
289 
290 		if (ratingSummary.ratingCount > 0) {
291 			BString avgRating;
292 			avgRating.SetToFormat("%.1f", ratingSummary.averageRating);
293 			fAvgRating->SetText(avgRating);
294 
295 			BString votes;
296 			votes.SetToFormat("%d", ratingSummary.ratingCount);
297 
298 			BString voteInfo(B_TRANSLATE("(%Votes%)"));
299 			voteInfo.ReplaceAll("%Votes%", votes);
300 
301 			fVoteInfo->SetText(voteInfo);
302 		} else {
303 			fAvgRating->SetText("");
304 			fVoteInfo->SetText("");
305 		}
306 	}
307 
308 	void _SetInstalled(bool installed)
309 	{
310 		if (installed) {
311 			fInstalledIconView->SetBitmap(sInstalledIcon,
312 				SharedBitmap::SIZE_16);
313 		} else
314 			fInstalledIconView->UnsetBitmap();
315 	}
316 
317 	void _SetIcon(const BitmapRef& icon)
318 	{
319 		if (icon.Get() != NULL) {
320 			fIconView->SetBitmap(icon, SharedBitmap::SIZE_64);
321 		} else
322 			fIconView->UnsetBitmap();
323 	}
324 
325 private:
326 	OnePackageMessagePackageListener* fPackageListener;
327 
328 	BitmapView*						fIconView;
329 	BitmapView*						fInstalledIconView;
330 
331 	BStringView*					fTitleView;
332 	BStringView*					fPublisherView;
333 
334 	BTextView*						fSummaryView;
335 
336 	RatingView*						fRatingView;
337 	BStringView*					fAvgRating;
338 	BStringView*					fVoteInfo;
339 
340 	bool							fSelected;
341 
342 	BString							fPackageName;
343 };
344 
345 
346 // #pragma mark - FeaturedPackagesView
347 
348 
349 FeaturedPackagesView::FeaturedPackagesView()
350 	:
351 	BView(B_TRANSLATE("Featured packages"), 0)
352 {
353 	BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
354 	SetLayout(layout);
355 
356 	fContainerView = new ScrollableGroupView();
357 	fContainerView->SetViewUIColor(B_LIST_BACKGROUND_COLOR);
358 	fPackageListLayout = fContainerView->GroupLayout();
359 
360 	BScrollView* scrollView = new BScrollView(
361 		"featured packages scroll view", fContainerView,
362 		0, false, true, B_FANCY_BORDER);
363 
364 	BScrollBar* scrollBar = scrollView->ScrollBar(B_VERTICAL);
365 	if (scrollBar != NULL)
366 		scrollBar->SetSteps(10.0f, 20.0f);
367 
368 	BLayoutBuilder::Group<>(this)
369 		.Add(scrollView, 1.0f)
370 	;
371 }
372 
373 
374 FeaturedPackagesView::~FeaturedPackagesView()
375 {
376 }
377 
378 
379 void
380 FeaturedPackagesView::AddPackage(const PackageInfoRef& package)
381 {
382 	// Find insertion index (alphabetical)
383 	int32 index = 0;
384 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
385 		PackageView* view = dynamic_cast<PackageView*>(item->View());
386 		if (view == NULL)
387 			break;
388 
389 		BString name = view->PackageName();
390 		if (name == package->Name()) {
391 			// Don't add packages more than once
392 			return;
393 		}
394 
395 		BString title = view->PackageTitle();
396 		if (title.Compare(package->Title()) < 0)
397 			index++;
398 	}
399 
400 	PackageView* view = new PackageView();
401 	view->SetPackage(package);
402 
403 	fPackageListLayout->AddView(index, view);
404 }
405 
406 
407 void
408 FeaturedPackagesView::RemovePackage(const PackageInfoRef& package)
409 {
410 	// Find the package
411 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
412 		PackageView* view = dynamic_cast<PackageView*>(item->View());
413 		if (view == NULL)
414 			break;
415 
416 		BString name = view->PackageName();
417 		if (name == package->Name()) {
418 			view->RemoveSelf();
419 			delete view;
420 			break;
421 		}
422 	}
423 }
424 
425 
426 void
427 FeaturedPackagesView::Clear()
428 {
429 	for (int32 i = fPackageListLayout->CountItems() - 1;
430 			BLayoutItem* item = fPackageListLayout->ItemAt(i); i--) {
431 		BView* view = dynamic_cast<PackageView*>(item->View());
432 		if (view != NULL) {
433 			view->RemoveSelf();
434 			delete view;
435 		}
436 	}
437 }
438 
439 
440 void
441 FeaturedPackagesView::SelectPackage(const PackageInfoRef& package,
442 	bool scrollToEntry)
443 {
444 	BString selectedName;
445 	if (package.Get() != NULL)
446 		selectedName = package->Name();
447 
448 	for (int32 i = 0; BLayoutItem* item = fPackageListLayout->ItemAt(i); i++) {
449 		PackageView* view = dynamic_cast<PackageView*>(item->View());
450 		if (view == NULL)
451 			break;
452 
453 		BString name = view->PackageName();
454 		bool match = (name == selectedName);
455 		view->SetSelected(match);
456 
457 		if (match && scrollToEntry) {
458 			// Scroll the view so that the package entry shows up in the middle
459 			fContainerView->ScrollTo(0,
460 				view->Frame().top
461 				- fContainerView->Bounds().Height() / 2
462 				+ view->Bounds().Height() / 2);
463 		}
464 	}
465 }
466 
467 
468 void
469 FeaturedPackagesView::CleanupIcons()
470 {
471 	sInstalledIcon.Unset();
472 }
473