Skip to content

Commit 0381a0d

Browse files
committed
Merge branch 'develop' into feature/jack4all_juce
2 parents 15ba6fd + d0a04c0 commit 0381a0d

File tree

13 files changed

+3191
-208
lines changed

13 files changed

+3191
-208
lines changed

Source/Canvas.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,13 @@ Canvas::Canvas(PluginEditor* parent, pd::Patch::Ptr p, Component* parentGraph)
316316
commandLocked.referTo(pd->commandLocked);
317317
commandLocked.addListener(this);
318318

319+
// pd->commandLocked doesn't get updated when a canvas isn't active
320+
// So we set it to false here when a canvas is remade
321+
// Otherwise the last canvas could have set it true, and it would still be
322+
// in that state without command actually being locked
323+
if (!isGraph)
324+
commandLocked.setValue(false);
325+
319326
// init border for testing
320327
settingsChanged("border", SettingsFile::getInstance()->getPropertyAsValue("border"));
321328

Source/Components/DraggableNumber.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class DraggableNumber : public Label
5454
addListener(this);
5555
setFont(Fonts::getTabularNumbersFont().withHeight(14.0f));
5656
lookAndFeelChanged();
57+
setInterceptsMouseClicks(true, true);
5758
}
5859

5960
void colourChanged() override
@@ -104,10 +105,10 @@ class DraggableNumber : public Label
104105
onInteraction(false);
105106
}
106107

107-
void setEditableOnClick(bool editable)
108+
void setEditableOnClick(bool editable, bool handleFocusLossManually = false)
108109
{
109-
setEditable(editable, editable);
110-
setInterceptsMouseClicks(true, true);
110+
setEditable(editable, editable, handleFocusLossManually);
111+
setWantsKeyboardFocus(true);
111112
}
112113

113114
void setMaximum(double maximum)
@@ -589,14 +590,14 @@ class DraggableNumber : public Label
589590

590591
void textEditorFocusLost (TextEditor& editor) override
591592
{
592-
textEditorReturnKeyPressed(editor);
593+
//hideEditor(false);
593594
}
594595

595596
void textEditorReturnKeyPressed(TextEditor& editor) override
596597
{
597598
auto text = editor.getText();
598599
double newValue = parseExpression(text);
599-
setValue(newValue);
600+
setValue(newValue, dontSendNotification);
600601
onReturnKey(newValue);
601602
}
602603
};
@@ -610,7 +611,7 @@ struct DraggableListNumber : public DraggableNumber {
610611
explicit DraggableListNumber()
611612
: DraggableNumber(true)
612613
{
613-
setEditableOnClick(true);
614+
setEditableOnClick(true, true);
614615
}
615616

616617
void mouseDown(MouseEvent const& e) override
@@ -693,7 +694,6 @@ struct DraggableListNumber : public DraggableNumber {
693694
void paint(Graphics& g) override
694695
{
695696
if (hoveredDecimal >= 0) {
696-
// TODO: make this colour Id configurable?
697697
g.setColour(outlineColour.withAlpha(isMouseButtonDown() ? 0.5f : 0.3f));
698698
g.fillRoundedRectangle(hoveredDecimalPosition, 2.5f);
699699
}
@@ -724,7 +724,6 @@ struct DraggableListNumber : public DraggableNumber {
724724
}
725725

726726
if (hoveredDecimal >= 0) {
727-
// TODO: make this colour Id configurable
728727
auto const highlightColour = outlineColour.withAlpha(isMouseButtonDown() ? 0.5f : 0.3f);
729728
nvgFillColor(nvg, NVGComponent::convertColour(highlightColour));
730729
nvgFillRoundedRect(nvg, hoveredDecimalPosition.getX(), hoveredDecimalPosition.getY() - 1, hoveredDecimalPosition.getWidth(), hoveredDecimalPosition.getHeight(), 2.5f);
@@ -759,6 +758,12 @@ struct DraggableListNumber : public DraggableNumber {
759758
repaint();
760759
}
761760
}
761+
762+
void textEditorReturnKeyPressed(TextEditor& editor) override
763+
{
764+
onReturnKey(0);
765+
hideEditor(false);
766+
}
762767

763768
std::tuple<int, int, double> getListItemAtPosition(int x, Rectangle<float>* position = nullptr)
764769
{

Source/Components/WelcomePanel.h

Lines changed: 102 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "Utility/CachedTextRender.h"
1010
#include "Utility/NanoVGGraphicsContext.h"
1111
#include "Components/BouncingViewport.h"
12+
#include "Utility/PatchInfo.h"
1213

1314
class WelcomePanel : public Component
1415
, public NVGComponent
@@ -121,13 +122,13 @@ class WelcomePanel : public Component
121122

122123
auto lB = bounds.toFloat().expanded(0.5f);
123124
{
124-
auto bgCol = !isHovered ? convertColour(findColour(PlugDataColour::canvasBackgroundColourId)) : convertColour(findColour(PlugDataColour::toolbarBackgroundColourId));
125+
auto bgCol = !isHovered ? convertColour(findColour(PlugDataColour::panelForegroundColourId)) : convertColour(findColour(PlugDataColour::toolbarBackgroundColourId));
125126

126127
// Draw border around
127128
nvgDrawRoundedRect(nvg, lB.getX(), lB.getY(), lB.getWidth(), lB.getHeight(), bgCol, convertColour(findColour(PlugDataColour::toolbarOutlineColourId)), Corners::largeCornerRadius);
128129
}
129130

130-
auto const bgColour = findColour(PlugDataColour::canvasBackgroundColourId);
131+
auto const bgColour = findColour(PlugDataColour::panelForegroundColourId);
131132
auto const bgCol = convertColour(bgColour);
132133
auto const newOpenIconCol = convertColour(bgColour.contrasting().withAlpha(0.32f));
133134
auto const iconSize = 48;
@@ -255,22 +256,22 @@ class WelcomePanel : public Component
255256
enum TileType
256257
{
257258
Patch,
258-
Library
259+
LibraryPatch
259260
};
260261

261262
TileType tileType = Patch;
262263

263264
public:
264-
WelcomePanelTile(WelcomePanel& welcomePanel, File& patchFile, float scale, bool favourited, Image const& thumbImage = Image())
265+
WelcomePanelTile(WelcomePanel& welcomePanel, File& patchFile, String patchAuthor, float scale, bool favourited, Image const& thumbImage = Image())
265266
: isFavourited(favourited)
266267
, parent(welcomePanel)
267268
, snapshotScale(scale)
268269
, thumbnailImageData(thumbImage)
270+
, patchFile(patchFile)
269271
{
270272
tileName = patchFile.getFileNameWithoutExtension();
271-
272-
tileType = Library;
273-
273+
tileSubtitle = patchAuthor;
274+
tileType = LibraryPatch;
274275
resized();
275276
}
276277

@@ -282,7 +283,7 @@ class WelcomePanel : public Component
282283
{
283284
patchFile = File(subTree.getProperty("Path").toString());
284285
tileName = patchFile.getFileNameWithoutExtension();
285-
286+
286287
auto is24Hour = OSUtils::is24HourTimeFormat();
287288

288289
auto formatTimeDescription = [is24Hour](const Time& openTime, bool showDayAndDate = false) {
@@ -309,9 +310,9 @@ class WelcomePanel : public Component
309310

310311
};
311312

312-
auto const accessedInPlugdasta = Time(static_cast<int64>(subTree.getProperty("Time")));
313+
auto const accessedInPlugdata = Time(static_cast<int64>(subTree.getProperty("Time")));
313314

314-
tileSubtitle = formatTimeDescription(accessedInPlugdasta);
315+
tileSubtitle = formatTimeDescription(accessedInPlugdata);
315316

316317
auto const fileSize = patchFile.getSize();
317318

@@ -329,7 +330,7 @@ class WelcomePanel : public Component
329330
// We need to show the time accessed from plugdata, which is saved in the settings XML
330331
// We want to show this again as well as in the subtile, but format it differently (with both Today/Yesterday and date)
331332
// because the popup menu may occlude the tile + subtitle
332-
accessedTimeDescription = formatTimeDescription(accessedInPlugdasta, true);
333+
accessedTimeDescription = formatTimeDescription(accessedInPlugdata, true);
333334

334335
updateGeneratedThumbnailIfNeeded(thumbImage, svgImage);
335336
}
@@ -369,27 +370,70 @@ class WelcomePanel : public Component
369370

370371
PopupMenu tileMenu;
371372

372-
tileMenu.addItem(PlatformStrings::getBrowserTip(), [this]() {
373-
if (patchFile.existsAsFile())
374-
patchFile.revealToUser();
375-
});
376-
tileMenu.addSeparator();
377-
tileMenu.addItem(isFavourited ? "Remove from favourites" : "Add to favourites", [this]() {
378-
isFavourited = !isFavourited;
379-
onFavourite(isFavourited);
380-
});
381-
tileMenu.addSeparator();
382-
PopupMenu patchInfoSubMenu;
383-
patchInfoSubMenu.addItem(String("Size: " + fileSizeDescription), false, false, nullptr);
384-
patchInfoSubMenu.addSeparator();
385-
patchInfoSubMenu.addItem(String("Created: " + creationTimeDescription), false, false, nullptr);
386-
patchInfoSubMenu.addItem(String("Modified: " + modifiedTimeDescription), false, false, nullptr);
387-
patchInfoSubMenu.addItem(String("Accessed: " + accessedTimeDescription), false, false, nullptr);
388-
tileMenu.addSubMenu(String(tileName + ".pd file info"), patchInfoSubMenu, true);
389-
tileMenu.addSeparator();
390-
// TODO: we may want to be clearer about this - that it doesn't delete the file on disk
391-
// Put this at he bottom, so it's not accidentally clicked on
392-
tileMenu.addItem("Remove from recently opened", onRemove);
373+
if (tileType == LibraryPatch) {
374+
tileMenu.addItem(PlatformStrings::getBrowserTip(), [this]() {
375+
if (patchFile.existsAsFile())
376+
patchFile.revealToUser();
377+
});
378+
379+
tileMenu.addSeparator();
380+
381+
auto metaFile = patchFile.getParentDirectory().getChildFile("meta.json");
382+
if(metaFile.existsAsFile()) {
383+
384+
auto json = JSON::fromString(metaFile.loadFileAsString());
385+
auto patchInfo = PatchInfo(json);
386+
387+
PopupMenu patchInfoSubMenu;
388+
patchInfoSubMenu.addItem("Title: " + patchInfo.title, false, false, nullptr);
389+
patchInfoSubMenu.addItem("Author: " + patchInfo.author, false, false, nullptr);
390+
patchInfoSubMenu.addItem("Released: " + patchInfo.releaseDate, false, false, nullptr);
391+
patchInfoSubMenu.addItem("About: " + patchInfo.description, false, false, nullptr);
392+
393+
tileMenu.addSubMenu(String(tileName + " info"), patchInfoSubMenu, true);
394+
} else {
395+
tileMenu.addItem("Patch info not provided", false, false, nullptr);
396+
}
397+
398+
tileMenu.addSeparator();
399+
400+
// Put this at the bottom, so it's not accidentally clicked on
401+
tileMenu.addItem("Delete from library...", [this]() {
402+
Dialogs::showMultiChoiceDialog(&parent.confirmationDialog, parent.getParentComponent(), "Are you sure you want to delete: " + patchFile.getFileNameWithoutExtension(), [this](int choice) {
403+
if (choice == 0) {
404+
patchFile.getParentDirectory().deleteRecursively(true);
405+
parent.triggerAsyncUpdate();
406+
}
407+
}, { "Yes", "No" }, Icons::Warning);
408+
});
409+
} else {
410+
if (tileType == Patch) {
411+
tileMenu.addItem(PlatformStrings::getBrowserTip(), [this]() {
412+
if (patchFile.existsAsFile())
413+
patchFile.revealToUser();
414+
});
415+
416+
tileMenu.addSeparator();
417+
tileMenu.addItem(isFavourited ? "Remove from favourites" : "Add to favourites", [this]() {
418+
isFavourited = !isFavourited;
419+
onFavourite(isFavourited);
420+
});
421+
422+
tileMenu.addSeparator();
423+
PopupMenu patchInfoSubMenu;
424+
patchInfoSubMenu.addItem(String("Size: " + fileSizeDescription), false, false, nullptr);
425+
patchInfoSubMenu.addSeparator();
426+
patchInfoSubMenu.addItem(String("Created: " + creationTimeDescription), false, false, nullptr);
427+
patchInfoSubMenu.addItem(String("Modified: " + modifiedTimeDescription), false, false, nullptr);
428+
patchInfoSubMenu.addItem(String("Accessed: " + accessedTimeDescription), false, false, nullptr);
429+
tileMenu.addSubMenu(String(tileName + ".pd file info"), patchInfoSubMenu, true);
430+
}
431+
tileMenu.addSeparator();
432+
433+
// TODO: we may want to be clearer about this - that it doesn't delete the file on disk
434+
// Put this at he bottom, so it's not accidentally clicked on
435+
tileMenu.addItem("Remove from recently opened", onRemove);
436+
}
393437

394438
PopupMenu::Options options;
395439
options.withTargetComponent(this);
@@ -447,7 +491,7 @@ class WelcomePanel : public Component
447491
});
448492
}
449493
} else {
450-
if (tileType == Patch && snapshot && !snapshotImage.isValid()) {
494+
if (tileType != LibraryPatch && snapshot && !snapshotImage.isValid()) {
451495
snapshotImage = NVGImage(nvg, bounds.getWidth() * 2, (bounds.getHeight() - 32) * 2, [this](Graphics& g) {
452496
g.addTransform(AffineTransform::scale(2.0f));
453497
snapshot->drawAt(g, 0, 0, 1.0f);
@@ -461,7 +505,7 @@ class WelcomePanel : public Component
461505

462506
auto lB = bounds.toFloat().expanded(0.5f);
463507
// Draw background even for images incase there is a transparent PNG
464-
nvgDrawRoundedRect(nvg, lB.getX(), lB.getY(), lB.getWidth(), lB.getHeight(), convertColour(findColour(PlugDataColour::canvasBackgroundColourId)), convertColour(findColour(PlugDataColour::toolbarOutlineColourId)), Corners::largeCornerRadius);
508+
nvgDrawRoundedRect(nvg, lB.getX(), lB.getY(), lB.getWidth(), lB.getHeight(), convertColour(findColour(PlugDataColour::panelForegroundColourId)), convertColour(findColour(PlugDataColour::toolbarOutlineColourId)), Corners::largeCornerRadius);
465509
if (thumbnailImageData.isValid()) {
466510
// Render the thumbnail image file that is in the root dir of the pd patch
467511
auto sB = bounds.toFloat().reduced(0.2f);
@@ -477,7 +521,7 @@ class WelcomePanel : public Component
477521
nvgFontFace(nvg, "icon_font-Regular");
478522
nvgFontSize(nvg, 68.0f);
479523
nvgTextAlign(nvg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
480-
nvgText(nvg, bounds.getCentreX(), (bounds.getHeight() - 30) * 0.5f, Icons::PlugdataIconStandard.toRawUTF8(), nullptr);
524+
nvgText(nvg, bounds.getCentreX(), (bounds.getHeight() - 30) * 0.5f, tileType == LibraryPatch ? Icons::PlugdataIconStandard.toRawUTF8() : Icons::Error.toRawUTF8(), nullptr);
481525
}
482526

483527
nvgRestore(nvg);
@@ -677,6 +721,9 @@ class WelcomePanel : public Component
677721
auto const buttonY = getHeight() * 0.5f - 30;
678722
newPatchTile->setBounds(rowBounds.withX(startX).withWidth(buttonWidth).withY(buttonY));
679723
openPatchTile->setBounds(rowBounds.withX(startX + buttonWidth + tileSpacing).withWidth(buttonWidth).withY(buttonY));
724+
725+
auto firstTileBounds = rowBounds.removeFromLeft(actualTileWidth * 1.5f);
726+
storeTile->setBounds(firstTileBounds);
680727
} else {
681728
auto firstTileBounds = rowBounds.removeFromLeft(actualTileWidth * 1.5f);
682729
newPatchTile->setBounds(firstTileBounds);
@@ -782,6 +829,16 @@ class WelcomePanel : public Component
782829

783830
auto subTree = recentlyOpenedTree.getChild(i);
784831
auto patchFile = File(subTree.getProperty("Path").toString());
832+
833+
if(!File(patchFile).existsAsFile())
834+
{
835+
if(!subTree.hasProperty("Removable"))
836+
{
837+
recentlyOpenedTree.removeChild(subTree, nullptr);
838+
}
839+
continue;
840+
}
841+
785842
auto patchThumbnailBase = File(patchFile.getParentDirectory().getFullPathName() + "\\" + patchFile.getFileNameWithoutExtension() + "_thumb");
786843

787844
auto favourited = subTree.hasProperty("Pinned") && static_cast<bool>(subTree.getProperty("Pinned"));
@@ -874,7 +931,14 @@ class WelcomePanel : public Component
874931
break;
875932
}
876933
}
877-
auto* tile = libraryTiles.add(new WelcomePanelTile(*this, patchFile, scale, false, thumbImage));
934+
auto metaFile = patchFile.getParentDirectory().getChildFile("meta.json");
935+
String author;
936+
if(metaFile.existsAsFile())
937+
{
938+
auto json = JSON::fromString(metaFile.loadFileAsString());
939+
author = json["Author"].toString();
940+
}
941+
auto* tile = libraryTiles.add(new WelcomePanelTile(*this, patchFile, author, scale, false, thumbImage));
878942
tile->onClick = [this, patchFile]() mutable {
879943
if (patchFile.existsAsFile()) {
880944
editor->pd->autosave->checkForMoreRecentAutosave(patchFile, editor, [this, patchFile]() {
@@ -958,6 +1022,8 @@ class WelcomePanel : public Component
9581022
String searchQuery;
9591023
Tab currentTab = Home;
9601024
UnorderedMap<String, String> patchSvgCache;
1025+
1026+
std::unique_ptr<Dialog> confirmationDialog;
9611027

9621028
// To make the library panel update automatically
9631029
class LibraryFSListener : public FileSystemWatcher::Listener

0 commit comments

Comments
 (0)