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