1f545fe6aSStephan Aßmus /* 2f545fe6aSStephan Aßmus * Copyright 2013-214, Stephan Aßmus <superstippi@gmx.de>. 3d45104b1SJulian Harnath * Copyright 2017, Julian Harnath <julian.harnath@rwth-aachen.de>. 41f3909adSAndrew Lindesay * Copyright 2020, Andrew Lindesay <apl@lindesay.co.nz>. 5f545fe6aSStephan Aßmus * All rights reserved. Distributed under the terms of the MIT License. 6f545fe6aSStephan Aßmus */ 7f545fe6aSStephan Aßmus 8f545fe6aSStephan Aßmus #include "FeaturedPackagesView.h" 9f545fe6aSStephan Aßmus 109883929bSAndrew Lindesay #include <algorithm> 119883929bSAndrew Lindesay #include <vector> 12f545fe6aSStephan Aßmus 139883929bSAndrew Lindesay #include <Bitmap.h> 14f545fe6aSStephan Aßmus #include <Catalog.h> 15f545fe6aSStephan Aßmus #include <Font.h> 16f545fe6aSStephan Aßmus #include <LayoutBuilder.h> 179883929bSAndrew Lindesay #include <LayoutItem.h> 18f545fe6aSStephan Aßmus #include <Message.h> 19f545fe6aSStephan Aßmus #include <ScrollView.h> 20f545fe6aSStephan Aßmus #include <StringView.h> 21f545fe6aSStephan Aßmus #include <SpaceLayoutItem.h> 22f545fe6aSStephan Aßmus 23f545fe6aSStephan Aßmus #include "BitmapView.h" 24a9edb9bfSAndrew Lindesay #include "HaikuDepotConstants.h" 25f96d1f4dSAndrew Lindesay #include "Logger.h" 261f3c1ef1SStephan Aßmus #include "MainWindow.h" 276f6ad915SStephan Aßmus #include "MarkupTextView.h" 28f545fe6aSStephan Aßmus #include "MessagePackageListener.h" 299883929bSAndrew Lindesay #include "RatingUtils.h" 30f545fe6aSStephan Aßmus #include "RatingView.h" 31f545fe6aSStephan Aßmus #include "ScrollableGroupView.h" 329883929bSAndrew Lindesay #include "SharedBitmap.h" 33f545fe6aSStephan Aßmus 34f545fe6aSStephan Aßmus 35f545fe6aSStephan Aßmus #undef B_TRANSLATION_CONTEXT 36f545fe6aSStephan Aßmus #define B_TRANSLATION_CONTEXT "FeaturedPackagesView" 37f545fe6aSStephan Aßmus 38f545fe6aSStephan Aßmus 399883929bSAndrew Lindesay #define HEIGHT_PACKAGE 84.0f 409883929bSAndrew Lindesay #define SIZE_ICON 64.0f 419883929bSAndrew Lindesay #define X_POSITION_RATING 350.0f 429883929bSAndrew Lindesay #define X_POSITION_SUMMARY 500.0f 439883929bSAndrew Lindesay #define WIDTH_RATING 100.0f 449883929bSAndrew Lindesay #define Y_PROPORTION_TITLE 0.4f 459883929bSAndrew Lindesay #define Y_PROPORTION_PUBLISHER 0.7f 469883929bSAndrew Lindesay #define PADDING 8.0f 479883929bSAndrew Lindesay 489883929bSAndrew Lindesay 49a9edb9bfSAndrew Lindesay static BitmapRef sInstalledIcon(new(std::nothrow) 50a9edb9bfSAndrew Lindesay SharedBitmap(RSRC_INSTALLED), true); 51056d423cSStephan Aßmus 52f545fe6aSStephan Aßmus 53f545fe6aSStephan Aßmus // #pragma mark - PackageView 54f545fe6aSStephan Aßmus 55f545fe6aSStephan Aßmus 569883929bSAndrew Lindesay class StackedFeaturedPackagesView : public BView { 57f545fe6aSStephan Aßmus public: 589883929bSAndrew Lindesay StackedFeaturedPackagesView() 59f545fe6aSStephan Aßmus : 609883929bSAndrew Lindesay BView("stacked featured packages view", B_WILL_DRAW | B_FRAME_EVENTS), 613d869af5SStephan Aßmus fPackageListener( 62664372abSStephan Aßmus new(std::nothrow) OnePackageMessagePackageListener(this)), 639883929bSAndrew Lindesay fSelectedIndex(-1) 64f545fe6aSStephan Aßmus { 651f3c1ef1SStephan Aßmus SetEventMask(B_POINTER_EVENTS); 66f545fe6aSStephan Aßmus Clear(); 67f545fe6aSStephan Aßmus } 68f545fe6aSStephan Aßmus 699883929bSAndrew Lindesay 709883929bSAndrew Lindesay virtual ~StackedFeaturedPackagesView() 71f545fe6aSStephan Aßmus { 72f545fe6aSStephan Aßmus fPackageListener->SetPackage(PackageInfoRef(NULL)); 731f3909adSAndrew Lindesay fPackageListener->ReleaseReference(); 74f545fe6aSStephan Aßmus } 75f545fe6aSStephan Aßmus 769883929bSAndrew Lindesay // #pragma mark - message handling and events 77fa19dd44Slooncraz 78f545fe6aSStephan Aßmus virtual void MessageReceived(BMessage* message) 79f545fe6aSStephan Aßmus { 80f545fe6aSStephan Aßmus switch (message->what) { 81f545fe6aSStephan Aßmus case MSG_UPDATE_PACKAGE: 82d45104b1SJulian Harnath { 839883929bSAndrew Lindesay BString name; 84*fa5c8097SAndrew Lindesay if (message->FindString("name", &name) != B_OK) 85*fa5c8097SAndrew Lindesay HDINFO("expected 'name' key on package update message"); 869883929bSAndrew Lindesay else 879883929bSAndrew Lindesay _HandleUpdatePackage(name); 88d45104b1SJulian Harnath break; 89d45104b1SJulian Harnath } 90fa19dd44Slooncraz 91fa19dd44Slooncraz case B_COLORS_UPDATED: 92fa19dd44Slooncraz { 939883929bSAndrew Lindesay Invalidate(); 94fa19dd44Slooncraz break; 95fa19dd44Slooncraz } 961187e806SAndrew Lindesay 971187e806SAndrew Lindesay default: 981187e806SAndrew Lindesay BView::MessageReceived(message); 991187e806SAndrew Lindesay break; 100f545fe6aSStephan Aßmus } 101f545fe6aSStephan Aßmus } 102f545fe6aSStephan Aßmus 1039883929bSAndrew Lindesay 1041f3c1ef1SStephan Aßmus virtual void MouseDown(BPoint where) 1051f3c1ef1SStephan Aßmus { 1069883929bSAndrew Lindesay if (Window()->IsActive() && !IsHidden()) { 107dfbf4d38SStephan Aßmus BRect bounds = Bounds(); 108dfbf4d38SStephan Aßmus BRect parentBounds = Parent()->Bounds(); 109dfbf4d38SStephan Aßmus ConvertFromParent(&parentBounds); 110dfbf4d38SStephan Aßmus bounds = bounds & parentBounds; 1119883929bSAndrew Lindesay if (bounds.Contains(where)) { 1129883929bSAndrew Lindesay _MessageSelectIndex(_IndexOfY(where.y)); 1139883929bSAndrew Lindesay MakeFocus(); 1149883929bSAndrew Lindesay } 1159883929bSAndrew Lindesay } 1169883929bSAndrew Lindesay } 117dfbf4d38SStephan Aßmus 1189883929bSAndrew Lindesay 1199883929bSAndrew Lindesay virtual void KeyDown(const char* bytes, int32 numBytes) 1209883929bSAndrew Lindesay { 1219883929bSAndrew Lindesay char key = bytes[0]; 1229883929bSAndrew Lindesay 1239883929bSAndrew Lindesay switch (key) { 1249883929bSAndrew Lindesay case B_RIGHT_ARROW: 1259883929bSAndrew Lindesay case B_DOWN_ARROW: 1269883929bSAndrew Lindesay if (!IsEmpty() && fSelectedIndex != -1 1279883929bSAndrew Lindesay && fSelectedIndex < fPackages.size() - 1) { 1289883929bSAndrew Lindesay _MessageSelectIndex(fSelectedIndex + 1); 1299883929bSAndrew Lindesay } 1309883929bSAndrew Lindesay break; 1319883929bSAndrew Lindesay case B_LEFT_ARROW: 1329883929bSAndrew Lindesay case B_UP_ARROW: 1339883929bSAndrew Lindesay if (fSelectedIndex > 0) 1349883929bSAndrew Lindesay _MessageSelectIndex( fSelectedIndex - 1); 1359883929bSAndrew Lindesay break; 1369883929bSAndrew Lindesay case B_PAGE_UP: 1379883929bSAndrew Lindesay { 1389883929bSAndrew Lindesay BRect bounds = Bounds(); 1399883929bSAndrew Lindesay ScrollTo(0, fmaxf(0, bounds.top - bounds.Height())); 1409883929bSAndrew Lindesay break; 1419883929bSAndrew Lindesay } 1429883929bSAndrew Lindesay case B_PAGE_DOWN: 1439883929bSAndrew Lindesay { 1449883929bSAndrew Lindesay BRect bounds = Bounds(); 1459883929bSAndrew Lindesay float height = fPackages.size() * HEIGHT_PACKAGE; 1469883929bSAndrew Lindesay float maxScrollY = height - bounds.Height(); 1479883929bSAndrew Lindesay float pageDownScrollY = bounds.top + bounds.Height(); 1489883929bSAndrew Lindesay ScrollTo(0, fminf(maxScrollY, pageDownScrollY)); 1499883929bSAndrew Lindesay break; 1509883929bSAndrew Lindesay } 1519883929bSAndrew Lindesay default: 1529883929bSAndrew Lindesay BView::KeyDown(bytes, numBytes); 1539883929bSAndrew Lindesay break; 1549883929bSAndrew Lindesay } 1559883929bSAndrew Lindesay } 1569883929bSAndrew Lindesay 1579883929bSAndrew Lindesay 1589883929bSAndrew Lindesay /*! This method will send a message to the Window so that it can signal 1599883929bSAndrew Lindesay back to this and other views that a package has been selected. This 1609883929bSAndrew Lindesay method won't actually change the state of this view directly. 1619883929bSAndrew Lindesay */ 1629883929bSAndrew Lindesay 1639883929bSAndrew Lindesay void _MessageSelectIndex(int32 index) const 1649883929bSAndrew Lindesay { 1659883929bSAndrew Lindesay if (index != -1) { 1661f3c1ef1SStephan Aßmus BMessage message(MSG_PACKAGE_SELECTED); 1679883929bSAndrew Lindesay message.AddString("name", fPackages[index]->Name()); 1681f3c1ef1SStephan Aßmus Window()->PostMessage(&message); 1691f3c1ef1SStephan Aßmus } 1701f3c1ef1SStephan Aßmus } 1711f3c1ef1SStephan Aßmus 1729883929bSAndrew Lindesay 1739883929bSAndrew Lindesay virtual void FrameResized(float width, float height) 174f545fe6aSStephan Aßmus { 1759883929bSAndrew Lindesay BView::FrameResized(width, height); 176f545fe6aSStephan Aßmus 1779883929bSAndrew Lindesay // because the summary text will wrap, a resize of the frame will 1789883929bSAndrew Lindesay // result in all of the summary area needing to be redrawn. 179056d423cSStephan Aßmus 1809883929bSAndrew Lindesay BRect rectToInvalidate = Bounds(); 1819883929bSAndrew Lindesay rectToInvalidate.left = X_POSITION_SUMMARY; 1829883929bSAndrew Lindesay Invalidate(rectToInvalidate); 183f545fe6aSStephan Aßmus } 184f545fe6aSStephan Aßmus 1859883929bSAndrew Lindesay 1869883929bSAndrew Lindesay // #pragma mark - update / add / remove / clear data 1879883929bSAndrew Lindesay 1889883929bSAndrew Lindesay 189d45104b1SJulian Harnath void UpdatePackage(uint32 changeMask, const PackageInfoRef& package) 190d45104b1SJulian Harnath { 1919883929bSAndrew Lindesay // TODO; could optimize the invalidation? 1929883929bSAndrew Lindesay int32 index = _IndexOfPackage(package); 1939883929bSAndrew Lindesay if (index >= 0) { 1949883929bSAndrew Lindesay fPackages[index] = package; 1959883929bSAndrew Lindesay Invalidate(_RectOfIndex(index)); 196d45104b1SJulian Harnath } 1979883929bSAndrew Lindesay } 1989883929bSAndrew Lindesay 199d45104b1SJulian Harnath 200f545fe6aSStephan Aßmus void Clear() 201f545fe6aSStephan Aßmus { 202c4199192SAndrew Lindesay for (std::vector<PackageInfoRef>::iterator it = fPackages.begin(); 203c4199192SAndrew Lindesay it != fPackages.end(); it++) { 204c4199192SAndrew Lindesay (*it)->RemoveListener(fPackageListener); 205c4199192SAndrew Lindesay } 2069883929bSAndrew Lindesay fPackages.clear(); 2079883929bSAndrew Lindesay fSelectedIndex = -1; 2089883929bSAndrew Lindesay Invalidate(); 209f545fe6aSStephan Aßmus } 210f545fe6aSStephan Aßmus 2119883929bSAndrew Lindesay 2129883929bSAndrew Lindesay bool IsEmpty() const 2134b930ccaSStephan Aßmus { 2149883929bSAndrew Lindesay return fPackages.size() == 0; 2154b930ccaSStephan Aßmus } 2164b930ccaSStephan Aßmus 2179883929bSAndrew Lindesay 2189883929bSAndrew Lindesay void _HandleUpdatePackage(const BString& name) 21972992391SStephan Aßmus { 2209883929bSAndrew Lindesay int32 index = _IndexOfName(name); 2219883929bSAndrew Lindesay if (index != -1) 2229883929bSAndrew Lindesay Invalidate(_RectOfIndex(index)); 22372992391SStephan Aßmus } 22472992391SStephan Aßmus 2259883929bSAndrew Lindesay 2269883929bSAndrew Lindesay /*! This method will return true if the packageA is ordered before 2279883929bSAndrew Lindesay packageB. 2289883929bSAndrew Lindesay */ 2299883929bSAndrew Lindesay 2309883929bSAndrew Lindesay static bool _IsPackageBefore(const PackageInfoRef& packageA, 2319883929bSAndrew Lindesay const PackageInfoRef& packageB) 232664372abSStephan Aßmus { 2339883929bSAndrew Lindesay if (packageA.Get() == NULL || packageB.Get() == NULL) 2349883929bSAndrew Lindesay debugger("unexpected NULL reference in a referencable"); 2359883929bSAndrew Lindesay int c = packageA->Title().ICompare(packageB->Title()); 2369883929bSAndrew Lindesay if (c == 0) 2379883929bSAndrew Lindesay c = packageA->Name().Compare(packageB->Name()); 2389883929bSAndrew Lindesay return c < 0; 239fa19dd44Slooncraz } 240fa19dd44Slooncraz 2419883929bSAndrew Lindesay void AddPackage(const PackageInfoRef& package) 242fa19dd44Slooncraz { 2439883929bSAndrew Lindesay // fPackages is sorted and for this reason it is possible to find the 2449883929bSAndrew Lindesay // insertion point by identifying the first item in fPackages that does 2459883929bSAndrew Lindesay // not return true from the method '_IsPackageBefore'. 246fa19dd44Slooncraz 2474d2de4eaSAugustin Cavalier std::vector<PackageInfoRef>::iterator itInsertionPt 2489883929bSAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package, 2499883929bSAndrew Lindesay &_IsPackageBefore); 2509883929bSAndrew Lindesay 2519883929bSAndrew Lindesay if (itInsertionPt == fPackages.end() 2529883929bSAndrew Lindesay || package->Name() != (*itInsertionPt)->Name()) { 2539883929bSAndrew Lindesay int32 insertionIndex = 2549883929bSAndrew Lindesay std::distance<std::vector<PackageInfoRef>::const_iterator>( 2559883929bSAndrew Lindesay fPackages.begin(), itInsertionPt); 2569883929bSAndrew Lindesay if (fSelectedIndex >= insertionIndex) 2579883929bSAndrew Lindesay fSelectedIndex++; 2589883929bSAndrew Lindesay fPackages.insert(itInsertionPt, package); 2599883929bSAndrew Lindesay Invalidate(_RectOfIndex(insertionIndex) 2609883929bSAndrew Lindesay | _RectOfIndex(fPackages.size() - 1)); 261c4199192SAndrew Lindesay package->AddListener(fPackageListener); 2629883929bSAndrew Lindesay } 263fa19dd44Slooncraz } 264664372abSStephan Aßmus 265664372abSStephan Aßmus 2669883929bSAndrew Lindesay void RemovePackage(const PackageInfoRef& package) 267d45104b1SJulian Harnath { 2689883929bSAndrew Lindesay int32 index = _IndexOfPackage(package); 2699883929bSAndrew Lindesay if (index >= 0) { 2709883929bSAndrew Lindesay if (fSelectedIndex == index) 2719883929bSAndrew Lindesay fSelectedIndex = -1; 2729883929bSAndrew Lindesay if (fSelectedIndex > index) 2739883929bSAndrew Lindesay fSelectedIndex--; 274c4199192SAndrew Lindesay fPackages[index]->RemoveListener(fPackageListener); 2759883929bSAndrew Lindesay fPackages.erase(fPackages.begin() + index); 2769883929bSAndrew Lindesay if (fPackages.empty()) 2779883929bSAndrew Lindesay Invalidate(); 2789883929bSAndrew Lindesay else { 2799883929bSAndrew Lindesay Invalidate(_RectOfIndex(index) 2809883929bSAndrew Lindesay | _RectOfIndex(fPackages.size() - 1)); 2819883929bSAndrew Lindesay } 2829883929bSAndrew Lindesay } 2839883929bSAndrew Lindesay } 284d45104b1SJulian Harnath 285d45104b1SJulian Harnath 2869883929bSAndrew Lindesay // #pragma mark - selection and index handling 287d45104b1SJulian Harnath 288d45104b1SJulian Harnath 2899883929bSAndrew Lindesay void SelectPackage(const PackageInfoRef& package) 2909883929bSAndrew Lindesay { 2919883929bSAndrew Lindesay _SelectIndex(_IndexOfPackage(package)); 2929883929bSAndrew Lindesay } 2939883929bSAndrew Lindesay 2949883929bSAndrew Lindesay 2959883929bSAndrew Lindesay void _SelectIndex(int32 index) 2969883929bSAndrew Lindesay { 2979883929bSAndrew Lindesay if (index != fSelectedIndex) { 2989883929bSAndrew Lindesay int32 previousSelectedIndex = fSelectedIndex; 2999883929bSAndrew Lindesay fSelectedIndex = index; 3009883929bSAndrew Lindesay if (fSelectedIndex >= 0) 3019883929bSAndrew Lindesay Invalidate(_RectOfIndex(fSelectedIndex)); 3029883929bSAndrew Lindesay if (previousSelectedIndex >= 0) 3039883929bSAndrew Lindesay Invalidate(_RectOfIndex(previousSelectedIndex)); 3049883929bSAndrew Lindesay _EnsureIndexVisible(index); 3059883929bSAndrew Lindesay } 3069883929bSAndrew Lindesay } 3079883929bSAndrew Lindesay 3089883929bSAndrew Lindesay 3099883929bSAndrew Lindesay int32 _IndexOfPackage(PackageInfoRef package) const 3109883929bSAndrew Lindesay { 311c4199192SAndrew Lindesay std::vector<PackageInfoRef>::const_iterator it 312c4199192SAndrew Lindesay = std::lower_bound(fPackages.begin(), fPackages.end(), package, 313c4199192SAndrew Lindesay &_IsPackageBefore); 314c4199192SAndrew Lindesay 315c4199192SAndrew Lindesay return (it == fPackages.end() || (*it)->Name() != package->Name()) 316c4199192SAndrew Lindesay ? -1 : it - fPackages.begin(); 3179883929bSAndrew Lindesay } 3189883929bSAndrew Lindesay 3199883929bSAndrew Lindesay 3209883929bSAndrew Lindesay int32 _IndexOfName(const BString& name) const 3219883929bSAndrew Lindesay { 3229883929bSAndrew Lindesay // TODO; slow linear search. 3239883929bSAndrew Lindesay // the fPackages is not sorted on name and for this reason it is not 3249883929bSAndrew Lindesay // possible to do a binary search. 3259883929bSAndrew Lindesay for (uint32 i = 0; i < fPackages.size(); i++) { 3269883929bSAndrew Lindesay if (fPackages[i]->Name() == name) 3279883929bSAndrew Lindesay return i; 3289883929bSAndrew Lindesay } 3299883929bSAndrew Lindesay return -1; 3309883929bSAndrew Lindesay } 3319883929bSAndrew Lindesay 3329883929bSAndrew Lindesay 3339883929bSAndrew Lindesay // #pragma mark - drawing and rendering 3349883929bSAndrew Lindesay 3359883929bSAndrew Lindesay 3369883929bSAndrew Lindesay virtual void Draw(BRect updateRect) 3379883929bSAndrew Lindesay { 3389883929bSAndrew Lindesay SetHighUIColor(B_LIST_BACKGROUND_COLOR); 3399883929bSAndrew Lindesay FillRect(updateRect); 3409883929bSAndrew Lindesay 3419883929bSAndrew Lindesay int32 iStart = _IndexRoundedOfY(updateRect.top); 3429883929bSAndrew Lindesay 3439883929bSAndrew Lindesay if (iStart != -1) { 3449883929bSAndrew Lindesay int32 iEnd = _IndexRoundedOfY(updateRect.bottom); 3459883929bSAndrew Lindesay for (int32 i = iStart; i <= iEnd; i++) 3469883929bSAndrew Lindesay _DrawPackageAtIndex(updateRect, i); 3479883929bSAndrew Lindesay } 3489883929bSAndrew Lindesay } 3499883929bSAndrew Lindesay 3509883929bSAndrew Lindesay 3519883929bSAndrew Lindesay void _DrawPackageAtIndex(BRect updateRect, int32 index) 3529883929bSAndrew Lindesay { 3539883929bSAndrew Lindesay _DrawPackage(updateRect, fPackages[index], index, _YOfIndex(index), 3549883929bSAndrew Lindesay index == fSelectedIndex); 3559883929bSAndrew Lindesay } 3569883929bSAndrew Lindesay 3579883929bSAndrew Lindesay 3589883929bSAndrew Lindesay void _DrawPackage(BRect updateRect, PackageInfoRef pkg, int index, float y, 3599883929bSAndrew Lindesay bool selected) 3609883929bSAndrew Lindesay { 3619883929bSAndrew Lindesay if (selected) { 3629883929bSAndrew Lindesay SetLowUIColor(B_LIST_SELECTED_BACKGROUND_COLOR); 3639883929bSAndrew Lindesay FillRect(_RectOfY(y), B_SOLID_LOW); 364d45104b1SJulian Harnath } else { 3659883929bSAndrew Lindesay SetLowUIColor(B_LIST_BACKGROUND_COLOR); 366d45104b1SJulian Harnath } 3679883929bSAndrew Lindesay // TODO; optimization; the updateRect may only cover some of this? 3689883929bSAndrew Lindesay _DrawPackageIcon(updateRect, pkg, y, selected); 3699883929bSAndrew Lindesay _DrawPackageTitle(updateRect, pkg, y, selected); 3709883929bSAndrew Lindesay _DrawPackagePublisher(updateRect, pkg, y, selected); 3719883929bSAndrew Lindesay _DrawPackageRating(updateRect, pkg, y, selected); 3729883929bSAndrew Lindesay _DrawPackageSummary(updateRect, pkg, y, selected); 373d45104b1SJulian Harnath } 374d45104b1SJulian Harnath 375d45104b1SJulian Harnath 3769883929bSAndrew Lindesay void _DrawPackageIcon(BRect updateRect, PackageInfoRef pkg, float y, 3779883929bSAndrew Lindesay bool selected) 378d45104b1SJulian Harnath { 3799883929bSAndrew Lindesay BitmapRef icon = pkg->Icon(); 3809883929bSAndrew Lindesay 381d45104b1SJulian Harnath if (icon.Get() != NULL) { 3829883929bSAndrew Lindesay float inset = (HEIGHT_PACKAGE - SIZE_ICON) / 2.0; 3839883929bSAndrew Lindesay BRect sourceRect = BRect(0, 0, SIZE_ICON, SIZE_ICON); 3849883929bSAndrew Lindesay BRect targetRect = BRect(inset, y + inset, SIZE_ICON + inset, 3859883929bSAndrew Lindesay y + SIZE_ICON + inset); 3869883929bSAndrew Lindesay const BBitmap* bitmap = icon->Bitmap(SharedBitmap::SIZE_64); 3879883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA); 3889883929bSAndrew Lindesay DrawBitmap(bitmap, bitmap->Bounds(), targetRect, 3899883929bSAndrew Lindesay B_FILTER_BITMAP_BILINEAR); 390d45104b1SJulian Harnath } 3919883929bSAndrew Lindesay } 3929883929bSAndrew Lindesay 3939883929bSAndrew Lindesay 3949883929bSAndrew Lindesay void _DrawPackageTitle(BRect updateRect, PackageInfoRef pkg, float y, 3959883929bSAndrew Lindesay bool selected) 3969883929bSAndrew Lindesay { 3979883929bSAndrew Lindesay static BFont* sFont = NULL; 3989883929bSAndrew Lindesay 3999883929bSAndrew Lindesay if (sFont == NULL) { 4009883929bSAndrew Lindesay sFont = new BFont(be_plain_font); 4019883929bSAndrew Lindesay GetFont(sFont); 4029883929bSAndrew Lindesay font_family family; 4039883929bSAndrew Lindesay font_style style; 4049883929bSAndrew Lindesay sFont->SetSize(ceilf(sFont->Size() * 1.8f)); 4059883929bSAndrew Lindesay sFont->GetFamilyAndStyle(&family, &style); 4069883929bSAndrew Lindesay sFont->SetFamilyAndStyle(family, "Bold"); 4079883929bSAndrew Lindesay } 4089883929bSAndrew Lindesay 4099883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 4109883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 4119883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 4129883929bSAndrew Lindesay SetFont(sFont); 4139883929bSAndrew Lindesay BPoint pt(HEIGHT_PACKAGE, y + (HEIGHT_PACKAGE * Y_PROPORTION_TITLE)); 4149883929bSAndrew Lindesay DrawString(pkg->Title(), pt); 4159883929bSAndrew Lindesay 4169883929bSAndrew Lindesay if (pkg->State() == ACTIVATED) { 4179883929bSAndrew Lindesay const BBitmap* bitmap = sInstalledIcon->Bitmap( 4189883929bSAndrew Lindesay SharedBitmap::SIZE_16); 4199883929bSAndrew Lindesay float stringWidth = StringWidth(pkg->Title()); 4209883929bSAndrew Lindesay float offsetX = pt.x + stringWidth + PADDING; 4219883929bSAndrew Lindesay BRect targetRect(offsetX, pt.y - 16, offsetX + 16, pt.y); 4229883929bSAndrew Lindesay SetDrawingMode(B_OP_ALPHA); 4239883929bSAndrew Lindesay DrawBitmap(bitmap, bitmap->Bounds(), targetRect, 4249883929bSAndrew Lindesay B_FILTER_BITMAP_BILINEAR); 4259883929bSAndrew Lindesay } 4269883929bSAndrew Lindesay } 4279883929bSAndrew Lindesay 4289883929bSAndrew Lindesay 4299883929bSAndrew Lindesay void _DrawPackagePublisher(BRect updateRect, PackageInfoRef pkg, float y, 4309883929bSAndrew Lindesay bool selected) 4319883929bSAndrew Lindesay { 4329883929bSAndrew Lindesay static BFont* sFont = NULL; 4339883929bSAndrew Lindesay 4349883929bSAndrew Lindesay if (sFont == NULL) { 4359883929bSAndrew Lindesay sFont = new BFont(be_plain_font); 4369883929bSAndrew Lindesay font_family family; 4379883929bSAndrew Lindesay font_style style; 4389883929bSAndrew Lindesay sFont->SetSize(std::max(9.0f, floorf(sFont->Size() * 0.92f))); 4399883929bSAndrew Lindesay sFont->GetFamilyAndStyle(&family, &style); 4409883929bSAndrew Lindesay sFont->SetFamilyAndStyle(family, "Italic"); 4419883929bSAndrew Lindesay } 4429883929bSAndrew Lindesay 4439883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 4449883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 4459883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 4469883929bSAndrew Lindesay SetFont(sFont); 4479883929bSAndrew Lindesay 4489883929bSAndrew Lindesay float maxTextWidth = (X_POSITION_RATING - HEIGHT_PACKAGE) - PADDING; 4499883929bSAndrew Lindesay BString publisherName(pkg->Publisher().Name()); 4509883929bSAndrew Lindesay TruncateString(&publisherName, B_TRUNCATE_END, maxTextWidth); 4519883929bSAndrew Lindesay 4529883929bSAndrew Lindesay DrawString(publisherName, BPoint(HEIGHT_PACKAGE, 4539883929bSAndrew Lindesay y + (HEIGHT_PACKAGE * Y_PROPORTION_PUBLISHER))); 4549883929bSAndrew Lindesay } 4559883929bSAndrew Lindesay 4569883929bSAndrew Lindesay 4579883929bSAndrew Lindesay // TODO; show the sample size 4589883929bSAndrew Lindesay void _DrawPackageRating(BRect updateRect, PackageInfoRef pkg, float y, 4599883929bSAndrew Lindesay bool selected) 4609883929bSAndrew Lindesay { 4619883929bSAndrew Lindesay BPoint at(X_POSITION_RATING, 4629883929bSAndrew Lindesay y + (HEIGHT_PACKAGE - SIZE_RATING_STAR) / 2.0f); 4639883929bSAndrew Lindesay RatingUtils::Draw(this, at, 4649883929bSAndrew Lindesay pkg->CalculateRatingSummary().averageRating); 4659883929bSAndrew Lindesay } 4669883929bSAndrew Lindesay 4679883929bSAndrew Lindesay 4689883929bSAndrew Lindesay // TODO; handle multi-line rendering of the text 4699883929bSAndrew Lindesay void _DrawPackageSummary(BRect updateRect, PackageInfoRef pkg, float y, 4709883929bSAndrew Lindesay bool selected) 4719883929bSAndrew Lindesay { 4729883929bSAndrew Lindesay BRect bounds = Bounds(); 4739883929bSAndrew Lindesay 4749883929bSAndrew Lindesay SetDrawingMode(B_OP_COPY); 4759883929bSAndrew Lindesay SetHighUIColor(selected ? B_LIST_SELECTED_ITEM_TEXT_COLOR 4769883929bSAndrew Lindesay : B_LIST_ITEM_TEXT_COLOR); 4779883929bSAndrew Lindesay SetFont(be_plain_font); 4789883929bSAndrew Lindesay 4799883929bSAndrew Lindesay float maxTextWidth = bounds.Width() - X_POSITION_SUMMARY - PADDING; 4809883929bSAndrew Lindesay BString summary(pkg->ShortDescription()); 4819883929bSAndrew Lindesay TruncateString(&summary, B_TRUNCATE_END, maxTextWidth); 4829883929bSAndrew Lindesay 4839883929bSAndrew Lindesay DrawString(summary, BPoint(X_POSITION_SUMMARY, 4849883929bSAndrew Lindesay y + (HEIGHT_PACKAGE * 0.5))); 4859883929bSAndrew Lindesay } 4869883929bSAndrew Lindesay 4879883929bSAndrew Lindesay 4889883929bSAndrew Lindesay // #pragma mark - geometry and scrolling 4899883929bSAndrew Lindesay 4909883929bSAndrew Lindesay 4919883929bSAndrew Lindesay /*! This method will make sure that the package at the given index is 4929883929bSAndrew Lindesay visible. If the whole of the package can be seen already then it will 4939883929bSAndrew Lindesay do nothing. If the package is located above the visible region then it 4949883929bSAndrew Lindesay will scroll up to it. If the package is located below the visible 4959883929bSAndrew Lindesay region then it will scroll down to it. 4969883929bSAndrew Lindesay */ 4979883929bSAndrew Lindesay 4989883929bSAndrew Lindesay void _EnsureIndexVisible(int32 index) 4999883929bSAndrew Lindesay { 5009883929bSAndrew Lindesay if (!_IsIndexEntirelyVisible(index)) { 5019883929bSAndrew Lindesay BRect bounds = Bounds(); 5029883929bSAndrew Lindesay int32 indexOfCentreVisible = _IndexOfY( 5039883929bSAndrew Lindesay bounds.top + bounds.Height() / 2); 5049883929bSAndrew Lindesay if (index < indexOfCentreVisible) 5059883929bSAndrew Lindesay ScrollTo(0, _YOfIndex(index)); 5069883929bSAndrew Lindesay else { 5079883929bSAndrew Lindesay float scrollPointY = (_YOfIndex(index) + HEIGHT_PACKAGE) 5089883929bSAndrew Lindesay - bounds.Height(); 5099883929bSAndrew Lindesay ScrollTo(0, scrollPointY); 5109883929bSAndrew Lindesay } 5119883929bSAndrew Lindesay } 5129883929bSAndrew Lindesay } 5139883929bSAndrew Lindesay 5149883929bSAndrew Lindesay 5159883929bSAndrew Lindesay /*! This method will return true if the package at the supplied index is 5169883929bSAndrew Lindesay entirely visible. 5179883929bSAndrew Lindesay */ 5189883929bSAndrew Lindesay 5199883929bSAndrew Lindesay bool _IsIndexEntirelyVisible(int32 index) 5209883929bSAndrew Lindesay { 5219883929bSAndrew Lindesay BRect bounds = Bounds(); 5229883929bSAndrew Lindesay return bounds == (bounds | _RectOfIndex(index)); 5239883929bSAndrew Lindesay } 5249883929bSAndrew Lindesay 5259883929bSAndrew Lindesay 5269883929bSAndrew Lindesay BRect _RectOfIndex(int32 index) const 5279883929bSAndrew Lindesay { 5289883929bSAndrew Lindesay if (index < 0) 5299883929bSAndrew Lindesay return BRect(0, 0, 0, 0); 5309883929bSAndrew Lindesay return _RectOfY(_YOfIndex(index)); 5319883929bSAndrew Lindesay } 5329883929bSAndrew Lindesay 5339883929bSAndrew Lindesay 5349883929bSAndrew Lindesay /*! Provides the top coordinate (offset from the top of view) of the package 5359883929bSAndrew Lindesay supplied. If the package does not exist in the view then the coordinate 5369883929bSAndrew Lindesay returned will be B_SIZE_UNSET. 5379883929bSAndrew Lindesay */ 5389883929bSAndrew Lindesay 5399883929bSAndrew Lindesay float TopOfPackage(const PackageInfoRef& package) 5409883929bSAndrew Lindesay { 5419883929bSAndrew Lindesay if (package.Get() != NULL) { 5429883929bSAndrew Lindesay int index = _IndexOfPackage(package); 5439883929bSAndrew Lindesay if (-1 != index) 5449883929bSAndrew Lindesay return _YOfIndex(index); 5459883929bSAndrew Lindesay } 5469883929bSAndrew Lindesay return B_SIZE_UNSET; 5479883929bSAndrew Lindesay } 5489883929bSAndrew Lindesay 5499883929bSAndrew Lindesay 5509883929bSAndrew Lindesay BRect _RectOfY(float y) const 5519883929bSAndrew Lindesay { 5529883929bSAndrew Lindesay return BRect(0, y, Bounds().Width(), y + HEIGHT_PACKAGE); 5539883929bSAndrew Lindesay } 5549883929bSAndrew Lindesay 5559883929bSAndrew Lindesay 5569883929bSAndrew Lindesay float _YOfIndex(int32 i) const 5579883929bSAndrew Lindesay { 5589883929bSAndrew Lindesay return i * HEIGHT_PACKAGE; 5599883929bSAndrew Lindesay } 5609883929bSAndrew Lindesay 5619883929bSAndrew Lindesay 5629883929bSAndrew Lindesay /*! Finds the offset into the list of packages for the y-coord in the view's 5639883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then 5649883929bSAndrew Lindesay this will return -1 to signal this. 5659883929bSAndrew Lindesay */ 5669883929bSAndrew Lindesay 5679883929bSAndrew Lindesay int32 _IndexOfY(float y) const 5689883929bSAndrew Lindesay { 5699883929bSAndrew Lindesay if (fPackages.empty()) 5709883929bSAndrew Lindesay return -1; 5719883929bSAndrew Lindesay int32 i = y / HEIGHT_PACKAGE; 5729883929bSAndrew Lindesay if (i < 0 || i >= fPackages.size()) 5739883929bSAndrew Lindesay return -1; 5749883929bSAndrew Lindesay return i; 5759883929bSAndrew Lindesay } 5769883929bSAndrew Lindesay 5779883929bSAndrew Lindesay 5789883929bSAndrew Lindesay /*! Find the offset into the list of packages for the y-coord in the view's 5799883929bSAndrew Lindesay coordinate space. If the y is above or below the list of packages then 5809883929bSAndrew Lindesay this will return the first or last package index respectively. If there 5819883929bSAndrew Lindesay are no packages then this will return -1; 5829883929bSAndrew Lindesay */ 5839883929bSAndrew Lindesay 5849883929bSAndrew Lindesay int32 _IndexRoundedOfY(float y) const 5859883929bSAndrew Lindesay { 5869883929bSAndrew Lindesay if (fPackages.empty()) 5879883929bSAndrew Lindesay return -1; 5889883929bSAndrew Lindesay int32 i = y / HEIGHT_PACKAGE; 5899883929bSAndrew Lindesay if (i < 0) 5909883929bSAndrew Lindesay return 0; 5919883929bSAndrew Lindesay return std::min(i, (int32) (fPackages.size() - 1)); 5929883929bSAndrew Lindesay } 5939883929bSAndrew Lindesay 5949883929bSAndrew Lindesay 5959883929bSAndrew Lindesay virtual BSize PreferredSize() 5969883929bSAndrew Lindesay { 5979883929bSAndrew Lindesay return BSize(B_SIZE_UNLIMITED, HEIGHT_PACKAGE * fPackages.size()); 5989883929bSAndrew Lindesay } 5999883929bSAndrew Lindesay 600d45104b1SJulian Harnath 601f545fe6aSStephan Aßmus private: 6029883929bSAndrew Lindesay std::vector<PackageInfoRef> 6039883929bSAndrew Lindesay fPackages; 6049883929bSAndrew Lindesay int32 fSelectedIndex; 605f545fe6aSStephan Aßmus 6069883929bSAndrew Lindesay OnePackageMessagePackageListener* 6079883929bSAndrew Lindesay fPackageListener; 608f545fe6aSStephan Aßmus }; 609f545fe6aSStephan Aßmus 610f545fe6aSStephan Aßmus 611f545fe6aSStephan Aßmus // #pragma mark - FeaturedPackagesView 612f545fe6aSStephan Aßmus 613f545fe6aSStephan Aßmus 614f545fe6aSStephan Aßmus FeaturedPackagesView::FeaturedPackagesView() 615f545fe6aSStephan Aßmus : 616e00a489bSAugustin Cavalier BView(B_TRANSLATE("Featured packages"), 0) 617f545fe6aSStephan Aßmus { 6189883929bSAndrew Lindesay fPackagesView = new StackedFeaturedPackagesView(); 619f545fe6aSStephan Aßmus 6209883929bSAndrew Lindesay fScrollView = new BScrollView("featured packages scroll view", 621494bd147SAugustin Cavalier fPackagesView, 0, false, true, B_FANCY_BORDER); 6228507d262SStephan Aßmus 623f545fe6aSStephan Aßmus BLayoutBuilder::Group<>(this) 6249883929bSAndrew Lindesay .Add(fScrollView, 1.0f); 625f545fe6aSStephan Aßmus } 626f545fe6aSStephan Aßmus 627f545fe6aSStephan Aßmus 628f545fe6aSStephan Aßmus FeaturedPackagesView::~FeaturedPackagesView() 629f545fe6aSStephan Aßmus { 630f545fe6aSStephan Aßmus } 631f545fe6aSStephan Aßmus 632f545fe6aSStephan Aßmus 633ccf707d0SAndrew Lindesay /*! This method will add the package into the list to be displayed. The 634ccf707d0SAndrew Lindesay insertion will occur in alphabetical order. 635ccf707d0SAndrew Lindesay */ 636ccf707d0SAndrew Lindesay 637f545fe6aSStephan Aßmus void 638f545fe6aSStephan Aßmus FeaturedPackagesView::AddPackage(const PackageInfoRef& package) 639f545fe6aSStephan Aßmus { 6409883929bSAndrew Lindesay fPackagesView->AddPackage(package); 6419883929bSAndrew Lindesay _AdjustViews(); 642ccf707d0SAndrew Lindesay } 643f545fe6aSStephan Aßmus 644f545fe6aSStephan Aßmus 645f545fe6aSStephan Aßmus void 64618b941b4SStephan Aßmus FeaturedPackagesView::RemovePackage(const PackageInfoRef& package) 64718b941b4SStephan Aßmus { 6489883929bSAndrew Lindesay fPackagesView->RemovePackage(package); 6499883929bSAndrew Lindesay _AdjustViews(); 65018b941b4SStephan Aßmus } 65118b941b4SStephan Aßmus 65218b941b4SStephan Aßmus 65318b941b4SStephan Aßmus void 654f545fe6aSStephan Aßmus FeaturedPackagesView::Clear() 655f545fe6aSStephan Aßmus { 656*fa5c8097SAndrew Lindesay HDINFO("did clear the featured packages view"); 6579883929bSAndrew Lindesay fPackagesView->Clear(); 6589883929bSAndrew Lindesay _AdjustViews(); 659f545fe6aSStephan Aßmus } 660f545fe6aSStephan Aßmus 661664372abSStephan Aßmus 662664372abSStephan Aßmus void 66372fff6d3SJulian Harnath FeaturedPackagesView::SelectPackage(const PackageInfoRef& package, 66472fff6d3SJulian Harnath bool scrollToEntry) 665664372abSStephan Aßmus { 6669883929bSAndrew Lindesay fPackagesView->SelectPackage(package); 667ede65a8fSStephan Aßmus 6689883929bSAndrew Lindesay if (scrollToEntry) { 6699883929bSAndrew Lindesay float offset = fPackagesView->TopOfPackage(package); 6709883929bSAndrew Lindesay if (offset != B_SIZE_UNSET) 6719883929bSAndrew Lindesay fPackagesView->ScrollTo(0, offset); 67272fff6d3SJulian Harnath } 673664372abSStephan Aßmus } 6749883929bSAndrew Lindesay 6759883929bSAndrew Lindesay 6769883929bSAndrew Lindesay void 6779883929bSAndrew Lindesay FeaturedPackagesView::DoLayout() 6789883929bSAndrew Lindesay { 6799883929bSAndrew Lindesay BView::DoLayout(); 6809883929bSAndrew Lindesay _AdjustViews(); 6819883929bSAndrew Lindesay } 6829883929bSAndrew Lindesay 6839883929bSAndrew Lindesay 6849883929bSAndrew Lindesay void 6859883929bSAndrew Lindesay FeaturedPackagesView::_AdjustViews() 6869883929bSAndrew Lindesay { 687494bd147SAugustin Cavalier fScrollView->FrameResized(fScrollView->Frame().Width(), 688494bd147SAugustin Cavalier fScrollView->Frame().Height()); 689664372abSStephan Aßmus } 690664372abSStephan Aßmus 691056d423cSStephan Aßmus 692056d423cSStephan Aßmus void 693056d423cSStephan Aßmus FeaturedPackagesView::CleanupIcons() 694056d423cSStephan Aßmus { 695056d423cSStephan Aßmus sInstalledIcon.Unset(); 696056d423cSStephan Aßmus } 697