@@ -70,7 +70,7 @@ class WelcomePanel : public Component
7070
7171 void setSearchQuery (String const & searchQuery)
7272 {
73- setVisible (tileName.upToFirstOccurrenceOf ( " .pd " , false , false ). containsIgnoreCase (searchQuery));
73+ setVisible (tileName.containsIgnoreCase (searchQuery));
7474 }
7575
7676 void paint (Graphics& g) override
@@ -247,16 +247,16 @@ class WelcomePanel : public Component
247247 : NVGComponent(this )
248248 , editor(pluginEditor)
249249 {
250- recentlyOpenedViewport .setViewedComponent (&recentlyOpenedComponent , false );
251- recentlyOpenedViewport .setScrollBarsShown (true , false , false , false );
252- recentlyOpenedComponent .setVisible (true );
250+ viewport .setViewedComponent (&contentComponent , false );
251+ viewport .setScrollBarsShown (true , false , false , false );
252+ contentComponent .setVisible (true );
253253#if JUCE_IOS
254- recentlyOpenedViewport .setVisible (OSUtils::isIPad ());
254+ viewport .setVisible (OSUtils::isIPad ());
255255#else
256- recentlyOpenedViewport .setVisible (true );
256+ viewport .setVisible (true );
257257#endif
258258
259- addChildComponent (recentlyOpenedViewport );
259+ addChildComponent (viewport );
260260
261261 // A top rectangle component that hides anything behind (we use this instead of scissoring)
262262 topFillAllRect.setBGColour (findColour (PlugDataColour::panelBackgroundColourId));
@@ -286,6 +286,7 @@ class WelcomePanel : public Component
286286 if (newPatchTile) newPatchTile->setVisible (searchQuery.isEmpty ());
287287 if (openPatchTile) openPatchTile->setVisible (searchQuery.isEmpty ());
288288
289+ auto & tiles = currentTab == Home ? recentlyOpenedTiles : libraryTiles;
289290 for (auto * tile : tiles) {
290291 tile->setSearchQuery (searchQuery);
291292 }
@@ -306,31 +307,29 @@ class WelcomePanel : public Component
306307 // Adjust the tile width to fit within the available width
307308 int actualTileWidth = (totalWidth - (numColumns - 1 ) * tileSpacing) / numColumns;
308309
309- if (newPatchTile && newPatchTile-> isVisible () )
310+ if (newPatchTile && currentTab == Home )
310311 newPatchTile->setBounds (rowBounds.removeFromLeft (actualTileWidth));
311312 rowBounds.removeFromLeft (4 );
312- if (openPatchTile && openPatchTile-> isVisible () )
313+ if (openPatchTile && currentTab == Home )
313314 openPatchTile->setBounds (rowBounds.removeFromLeft (actualTileWidth));
314315
315- auto viewPos = recentlyOpenedViewport.getViewPosition ();
316- recentlyOpenedViewport.setBounds (getLocalBounds ().withTrimmedTop ((newPatchTile && newPatchTile->isVisible ()) ? 200 : 0 ));
317-
318- // Place a rectangle directly behind the newTile & openTile so to hide any content that draws behind it.
319- topFillAllRect.setBounds (0 , 0 , getWidth (), recentlyOpenedViewport.getY ());
316+ auto viewPos = viewport.getViewPosition ();
317+ viewport.setBounds (getLocalBounds ());
320318
319+ auto & tiles = currentTab == Home ? recentlyOpenedTiles : libraryTiles;
321320 int numRows = (tiles.size () + numColumns - 1 ) / numColumns;
322- int totalHeight = numRows * 160 ;
321+ int totalHeight = ( numRows * 160 ) + 200 ;
323322
324- auto scrollable = Rectangle<int >(24 , 6 , totalWidth + 24 , totalHeight + 24 );
325- recentlyOpenedComponent .setBounds (scrollable );
323+ auto tilesBounds = Rectangle<int >(24 , currentTab == Home ? 206 : 6 , totalWidth + 24 , totalHeight + 24 );
324+ contentComponent .setBounds (tilesBounds );
326325
327- // Start positioning the tiles
328- rowBounds = scrollable .removeFromTop (160 );
326+ // Start positioning the recentlyOpenedTiles
327+ rowBounds = tilesBounds .removeFromTop (160 );
329328 for (auto * tile : tiles) {
330329 if (!tile->isVisible ()) continue ;
331330 if (tile->isFavourited ) {
332331 if (rowBounds.getWidth () < actualTileWidth) {
333- rowBounds = scrollable .removeFromTop (160 );
332+ rowBounds = tilesBounds .removeFromTop (160 );
334333 }
335334 tile->setBounds (rowBounds.removeFromLeft (actualTileWidth));
336335 rowBounds.removeFromLeft (tileSpacing);
@@ -341,13 +340,13 @@ class WelcomePanel : public Component
341340 if (!tile->isVisible ()) continue ;
342341 if (!tile->isFavourited ) {
343342 if (rowBounds.getWidth () < actualTileWidth) {
344- rowBounds = scrollable .removeFromTop (160 );
343+ rowBounds = tilesBounds .removeFromTop (160 );
345344 }
346345 tile->setBounds (rowBounds.removeFromLeft (actualTileWidth));
347346 rowBounds.removeFromLeft (tileSpacing);
348347 }
349348 }
350- recentlyOpenedViewport .setViewPosition (viewPos);
349+ viewport .setViewPosition (viewPos);
351350 }
352351
353352 void setShownTab (WelcomePanel::Tab tab)
@@ -357,23 +356,34 @@ class WelcomePanel : public Component
357356 {
358357 newPatchTile->setVisible (true );
359358 openPatchTile->setVisible (true );
360- for (auto * tile : tiles )
359+ for (auto * tile : recentlyOpenedTiles )
361360 {
362361 tile->setVisible (true );
363362 }
363+ for (auto * tile : libraryTiles)
364+ {
365+ tile->setVisible (false );
366+ }
364367 }
365368 else {
366369 newPatchTile->setVisible (false );
367370 openPatchTile->setVisible (false );
368- for (auto * tile : tiles )
371+ for (auto * tile : recentlyOpenedTiles )
369372 {
370373 tile->setVisible (false );
371374 }
375+ for (auto * tile : libraryTiles)
376+ {
377+ tile->setVisible (true );
378+ }
372379 }
380+
381+ triggerAsyncUpdate ();
373382 }
374383
375384 void handleAsyncUpdate () override
376385 {
386+ // TODO: why are we updating this??
377387 newPatchTile = std::make_unique<WelcomePanelTile>(*this , " New Patch" , " Create a new empty patch" , newIcon, findColour (PlugDataColour::panelTextColourId), 0 .33f , false );
378388 openPatchTile = std::make_unique<WelcomePanelTile>(*this , " Open Patch" , " Browse for a patch to open" , openIcon, findColour (PlugDataColour::panelTextColourId), 0 .33f , false );
379389
@@ -383,10 +393,10 @@ class WelcomePanel : public Component
383393 topFillAllRect.setBGColour (findColour (PlugDataColour::panelBackgroundColourId));
384394 addAndMakeVisible (topFillAllRect);
385395
386- addAndMakeVisible (*newPatchTile);
387- addAndMakeVisible (*openPatchTile);
396+ contentComponent. addAndMakeVisible (*newPatchTile);
397+ contentComponent. addAndMakeVisible (*openPatchTile);
388398
389- tiles .clear ();
399+ recentlyOpenedTiles .clear ();
390400
391401 auto settingsTree = SettingsFile::getInstance ()->getValueTree ();
392402 auto recentlyOpenedTree = settingsTree.getChildWithName (" RecentlyOpened" );
@@ -435,7 +445,7 @@ class WelcomePanel : public Component
435445 String time = openTime.toString (false , true , false , true );
436446 String timeDescription = date + " , " + time;
437447
438- auto * tile = tiles .add (new WelcomePanelTile (*this , patchFile.getFileName (), timeDescription, silhoutteSvg, snapshotColour, 1 .0f , favourited, thumbImage));
448+ auto * tile = recentlyOpenedTiles .add (new WelcomePanelTile (*this , patchFile.getFileNameWithoutExtension (), timeDescription, silhoutteSvg, snapshotColour, 1 .0f , favourited, thumbImage));
439449 tile->onClick = [this , patchFile]() mutable {
440450 if (patchFile.existsAsFile ()) {
441451 editor->pd ->autosave ->checkForMoreRecentAutosave (patchFile, editor, [this , patchFile]() {
@@ -456,12 +466,77 @@ class WelcomePanel : public Component
456466 subTree.setProperty (" Pinned" , shouldBeFavourite, nullptr );
457467 resized ();
458468 };
459- recentlyOpenedComponent .addAndMakeVisible (tile);
469+ contentComponent .addAndMakeVisible (tile);
460470 }
461471 }
462472
473+ findLibraryPatches ();
463474 resized ();
464475 }
476+
477+ void findLibraryPatches ()
478+ {
479+ libraryTiles.clear ();
480+
481+ auto addTile = [this ](File& patchFile){
482+ auto patchThumbnailBase = File (patchFile.getParentDirectory ().getFullPathName () + " \\ " + patchFile.getFileNameWithoutExtension () + " _thumb" );
483+ StringArray possibleExtensions { " .png" , " .jpg" , " .jpeg" , " .gif" };
484+
485+ float scale = 1 .0f ;
486+ Image thumbImage;
487+ for (auto & ext : possibleExtensions) {
488+ auto patchThumbnail = patchThumbnailBase.withFileExtension (ext);
489+ if (patchThumbnail.existsAsFile ()) {
490+ FileInputStream fileStream (patchThumbnail);
491+ if (fileStream.openedOk ()) {
492+ thumbImage = ImageFileFormat::loadFrom (fileStream).convertedToFormat (Image::ARGB);
493+ break ;
494+ }
495+ }
496+ }
497+ String placeholderIcon;
498+ if (!thumbImage.isValid ())
499+ {
500+ scale = 0 .6f ;
501+ placeholderIcon = libraryPlaceholderIcon;
502+ }
503+ auto snapshotColour = LookAndFeel::getDefaultLookAndFeel ().findColour (PlugDataColour::objectSelectedOutlineColourId).withAlpha (0 .3f );
504+
505+ auto * tile = libraryTiles.add (new WelcomePanelTile (*this , patchFile.getFileNameWithoutExtension (), " " , placeholderIcon, snapshotColour, scale, false , thumbImage));
506+ tile->onClick = [this , patchFile]() mutable {
507+ if (patchFile.existsAsFile ()) {
508+ editor->pd ->autosave ->checkForMoreRecentAutosave (patchFile, editor, [this , patchFile]() {
509+ editor->getTabComponent ().openPatch (URL (patchFile));
510+ SettingsFile::getInstance ()->addToRecentlyOpened (patchFile);
511+ });
512+ } else {
513+ editor->pd ->logError (" Patch not found" );
514+ }
515+ };
516+ contentComponent.addAndMakeVisible (tile);
517+ };
518+ auto patchesFolder = ProjectInfo::appDataDir.getChildFile (" Patches" );
519+ for (auto & file : OSUtils::iterateDirectory (patchesFolder, false , false ))
520+ {
521+ if (OSUtils::isDirectoryFast (file.getFullPathName ()))
522+ {
523+ for (auto & subfile : OSUtils::iterateDirectory (file, false , false ))
524+ {
525+ if (subfile.hasFileExtension (" pd" ))
526+ {
527+ addTile (subfile);
528+ break ;
529+ }
530+ }
531+ }
532+ else {
533+ if (file.hasFileExtension (" pd" ))
534+ {
535+ addTile (file);
536+ }
537+ }
538+ }
539+ }
465540
466541 void show ()
467542 {
@@ -486,10 +561,10 @@ class WelcomePanel : public Component
486561 g.reduceClipRegion (editor->nvgSurface .getInvalidArea ());
487562 paintEntireComponent (g, false );
488563
489- auto gradient = nvgLinearGradient (nvg, 0 , recentlyOpenedViewport .getY (), 0 , recentlyOpenedViewport .getY () + 20 , convertColour (findColour (PlugDataColour::panelBackgroundColourId)), nvgRGBA (255 , 255 , 255 , 0 ));
564+ auto gradient = nvgLinearGradient (nvg, 0 , viewport .getY (), 0 , viewport .getY () + 20 , convertColour (findColour (PlugDataColour::panelBackgroundColourId)), nvgRGBA (255 , 255 , 255 , 0 ));
490565
491566 nvgFillPaint (nvg, gradient);
492- nvgFillRect (nvg, recentlyOpenedViewport .getX () + 8 , recentlyOpenedViewport .getY (), recentlyOpenedViewport .getWidth () - 16 , 20 );
567+ nvgFillRect (nvg, viewport .getX () + 8 , viewport .getY (), viewport .getWidth () - 16 , 20 );
493568 }
494569
495570 void lookAndFeelChanged () override
@@ -511,22 +586,39 @@ class WelcomePanel : public Component
511586 " d=\" M1180 555h506q72 0 126 47t64 118v13l2 14v768q0 76 -52 131t-128 60h-12h-1324q-76 0 -131 -51.5t-59 -127.5l-2 -12v-620l530 2l17 -2q51 -4 92 -33l4 -3t6 -5l4 -2zM700 342q59 0 109 32l14 11l181 149l-263 219l-8 4q-10 8 -24 11h-9h-530v-236q0 -76 52 -131\n "
512587 " t128 -59h12h338z\" />\n "
513588 " </svg>\n " ;
589+
590+ static inline String const libraryPlaceholderIcon = " <svg width=\" 864\" height=\" 864\" viewBox=\" 0 0 864 864\" fill=\" none\" xmlns=\" http://www.w3.org/2000/svg\" >\n "
591+ " <path d=\" M538.114 201.488C550.72 201.488 560.94 191.268 560.94 178.662C560.94 166.055 550.72 155.836 538.114 155.836C525.507 155.836 515.288 166.055 515.288 178.662C515.288 191.268 525.507 201.488 538.114 201.488Z\" fill=\" black\" />\n "
592+ " <path d=\" M178.662 560.94C191.268 560.94 201.488 550.72 201.488 538.114C201.488 525.507 191.268 515.288 178.662 515.288C166.055 515.288 155.836 525.507 155.836 538.114C155.836 550.72 166.055 560.94 178.662 560.94Z\" fill=\" black\" />\n "
593+ " <path d=\" M695.922 201.488C708.528 201.488 718.748 191.268 718.748 178.662C718.748 166.055 708.528 155.836 695.922 155.836C683.315 155.836 673.096 166.055 673.096 178.662C673.096 191.268 683.315 201.488 695.922 201.488Z\" fill=\" black\" />\n "
594+ " <path d=\" M336.47 560.94C349.076 560.94 359.296 550.72 359.296 538.114C359.296 525.507 349.076 515.288 336.47 515.288C323.863 515.288 313.644 525.507 313.644 538.114C313.644 550.72 323.863 560.94 336.47 560.94Z\" fill=\" black\" />\n "
595+ " <path d=\" M695.922 359.296C708.528 359.296 718.748 349.076 718.748 336.47C718.748 323.863 708.528 313.644 695.922 313.644C683.315 313.644 673.096 323.863 673.096 336.47C673.096 349.076 683.315 359.296 695.922 359.296Z\" fill=\" black\" />\n "
596+ " <path d=\" M336.47 718.748C349.076 718.748 359.296 708.528 359.296 695.922C359.296 683.315 349.076 673.096 336.47 673.096C323.863 673.096 313.644 683.315 313.644 695.922C313.644 708.528 323.863 718.748 336.47 718.748Z\" fill=\" black\" />\n "
597+ " <path d=\" M538.114 359.296C550.72 359.296 560.94 349.076 560.94 336.47C560.94 323.863 550.72 313.644 538.114 313.644C525.507 313.644 515.288 323.863 515.288 336.47C515.288 349.076 525.507 359.296 538.114 359.296Z\" fill=\" black\" />\n "
598+ " <path d=\" M178.662 718.748C191.268 718.748 201.488 708.528 201.488 695.922C201.488 683.315 191.268 673.096 178.662 673.096C166.055 673.096 155.836 683.315 155.836 695.922C155.836 708.528 166.055 718.748 178.662 718.748Z\" fill=\" black\" />\n "
599+ " <path fill-rule=\" evenodd\" clip-rule=\" evenodd\" d=\" M216.158 112L287.842 112C324.06 112 337.194 115.771 350.434 122.852C363.675 129.933 374.066 140.325 381.148 153.566C388.229 166.806 392 179.94 392 216.158V287.842C392 324.06 388.229 337.194 381.148 350.434C374.066 363.675 363.675 374.066 350.434 381.148C337.194 388.229 324.06 392 287.842 392H216.158C179.94 392 166.806 388.229 153.566 381.148C140.325 374.066 129.933 363.675 122.852 350.434C115.771 337.194 112 324.06 112 287.842V216.158C112 179.94 115.771 166.806 122.852 153.566C129.933 140.325 140.325 129.933 153.566 122.852C166.806 115.771 179.94 112 216.158 112Z\" fill=\" black\" />\n "
600+ " <path fill-rule=\" evenodd\" clip-rule=\" evenodd\" d=\" M576.158 472H647.842C684.06 472 697.194 475.771 710.434 482.852C723.675 489.933 734.066 500.325 741.148 513.566C748.229 526.806 752 539.94 752 576.158V647.842C752 684.06 748.229 697.194 741.148 710.434C734.066 723.675 723.675 734.066 710.434 741.148C697.194 748.229 684.06 752 647.842 752H576.158C539.94 752 526.806 748.229 513.566 741.148C500.325 734.066 489.933 723.675 482.852 710.434C475.771 697.194 472 684.06 472 647.842V576.158C472 539.94 475.771 526.806 482.852 513.566C489.933 500.325 500.325 489.933 513.566 482.852C526.806 475.771 539.94 472 576.158 472Z\" fill=\" black\" />\n "
601+ " <rect x=\" 30\" y=\" 30\" width=\" 804\" height=\" 804\" rx=\" 172\" stroke=\" black\" stroke-width=\" 8\" />\n "
602+ " </svg>\n " ;
514603
515604 std::unique_ptr<WelcomePanelTile> newPatchTile;
516605 std::unique_ptr<WelcomePanelTile> openPatchTile;
517606
518- Component recentlyOpenedComponent ;
519- BouncingViewport recentlyOpenedViewport ;
607+ Component contentComponent ;
608+ BouncingViewport viewport ;
520609
521610 TopFillAllRect topFillAllRect;
522611
523612 std::unique_ptr<NanoVGGraphicsContext> nvgContext = nullptr ;
524613
525614 NVGImage shadowImage;
526- OwnedArray<WelcomePanelTile> tiles;
615+ OwnedArray<WelcomePanelTile> recentlyOpenedTiles;
616+ OwnedArray<WelcomePanelTile> libraryTiles;
527617 PluginEditor* editor;
618+
619+ static inline Image logo = ImageFileFormat::loadFrom(BinaryData::plugdata_logo_png, BinaryData::plugdata_logo_pngSize).rescaled(128 , 128 );
528620
529621 String searchQuery;
530- Tab currentTab;
622+ Tab currentTab = Home ;
531623
532624};
0 commit comments