Vital
Loading...
Searching...
No Matches
popup_browser.cpp
Go to the documentation of this file.
1#include "popup_browser.h"
2
3#include "skin.h"
4#include "fonts.h"
5#include "load_save.h"
6#include "paths.h"
7#include "open_gl_component.h"
8#include "synth_section.h"
9
10namespace {
11 // Sorts selection arrays using a comparator.
12 template<class Comparator>
13 void sortSelectionArray(Array<File>& selection_array) {
14 Comparator comparator;
15 selection_array.sort(comparator, true);
16 }
17
18 const std::string kAddFolderName = "Add Folder";
19 const std::string kStoreUrl = "";
20 constexpr int kMaxRootFiles = 8000;
21
22 // Checks if a given root folder contains too many files.
23 bool isAcceptableRoot(const File& file) {
24 std::list<File> folders;
25 folders.push_back(file);
26 int num_files = 0;
27
28 while (!folders.empty()) {
29 File current_file = folders.back();
30 folders.pop_back();
31
32 num_files += current_file.getNumberOfChildFiles(File::findFiles);
33 if (num_files > kMaxRootFiles)
34 return false;
35
36 Array<File> sub_folders = current_file.findChildFiles(File::findDirectories, false);
37
38 for (const File& folder : sub_folders)
39 folders.push_back(folder);
40
41 }
42 return true;
43 }
44
45 // Shows a warning if a folder has too many files.
46 void showAddRootWarning() {
47 String error = String("Folder has too many files to add to browser. Max: ") + String(kMaxRootFiles);
48 NativeMessageBox::showMessageBoxAsync(AlertWindow::WarningIcon, "Error Adding Folder", error);
49 }
50}
51
52PopupDisplay::PopupDisplay() : SynthSection("Popup Display"), text_("Popup Text", ""),
53 body_(Shaders::kRoundedRectangleFragment),
54 border_(Shaders::kRoundedRectangleBorderFragment) {
55 addOpenGlComponent(&body_);
56 addOpenGlComponent(&border_);
57 addOpenGlComponent(&text_);
58
59 text_.setJustification(Justification::centred);
61
63}
64
66 Rectangle<int> bounds = getLocalBounds();
67 int rounding = findValue(Skin::kBodyRounding);
68
69 body_.setBounds(bounds);
70 body_.setRounding(rounding);
71 body_.setColor(findColour(Skin::kBody, true));
72
73 border_.setBounds(bounds);
74 border_.setRounding(rounding);
75 border_.setThickness(1.0f, true);
76 border_.setColor(findColour(Skin::kBorder, true));
77
78 text_.setBounds(bounds);
79 text_.setColor(findColour(Skin::kBodyText, true));
80}
81
82void PopupDisplay::setContent(const std::string& text, Rectangle<int> bounds,
83 BubbleComponent::BubblePlacement placement) {
84
85 // Convert std::string to JUCE String
86 String textToDisplay(text.c_str());
87
88 // 1) Split lines:
89 StringArray lines;
90 lines.addLines(textToDisplay);
91
92 // 2) Choose your base Font:
93 static constexpr int kBaseHeight = 24;
94 int height = kBaseHeight * size_ratio_;
95 int mult = getPixelMultiple();
96 Font font = Fonts::instance()->proportional_light().withPointHeight(height * 0.5f * mult);
97
98 // 3) Measure widest line and total height:
99 int maxWidth = 0;
100 for (auto& line : lines) {
101 maxWidth = jmax(maxWidth, font.getStringWidth(line));
102 }
103
104 // Add some margins/padding:
105 int padding = height / 4;
106 int buffer = padding * 2 + 2;
107 int totalWidth = (maxWidth / getPixelMultiple()) + buffer;
108
109 // Each line is about `height` tall, or you can do more precise measurement:
110 int numLines = lines.size();
111 int lineHeight = height; // or e.g. 1.2 * height if you want more spacing
112 int totalHeight = lineHeight * numLines;
113
114 // 4) Decide final bounds:
115 // For instance, if bubble is above:
116 int middle_x = bounds.getX() + bounds.getWidth() / 2;
117 int middle_y = bounds.getY() + bounds.getHeight() / 2;
118
119 if (placement == BubbleComponent::above) {
120 setBounds(middle_x - totalWidth / 2,
121 bounds.getY() - totalHeight,
122 totalWidth, totalHeight);
123 } else if (placement == BubbleComponent::below) {
124 setBounds(middle_x - totalWidth / 2,
125 bounds.getBottom(),
126 totalWidth, totalHeight);
127 } else if (placement == BubbleComponent::left) {
128 setBounds(bounds.getX() - totalWidth,
129 middle_y - totalHeight / 2,
130 totalWidth, totalHeight);
131 } else if (placement == BubbleComponent::right) {
132 setBounds(bounds.getRight(),
133 middle_y - totalHeight / 2,
134 totalWidth, totalHeight);
135 }
136
137 // 5) Actually set the text and font size in your PlainTextComponent
138 text_.setText(text); // keep the newlines
139 text_.setJustification(Justification::centred);
140 text_.setTextSize(height * 0.5f);
141}
142
144 selected_(-1), hovered_(-1), show_selected_(false), view_position_(0.0f),
145 highlight_(Shaders::kColorFragment), hover_(Shaders::kColorFragment) {
146 highlight_.setTargetComponent(this);
147 highlight_.setAdditive(true);
148
149 hover_.setTargetComponent(this);
150 hover_.setAdditive(true);
151
152 scroll_bar_ = std::make_unique<OpenGlScrollBar>();
153 addAndMakeVisible(scroll_bar_.get());
154 addOpenGlComponent(scroll_bar_->getGlComponent());
155 scroll_bar_->addListener(this);
156}
157
159 Colour lighten = findColour(Skin::kLightenScreen, true);
160 scroll_bar_->setColor(lighten);
161
162 if (getScrollableRange() > getHeight()) {
163 int scroll_bar_width = kScrollBarWidth * getSizeRatio();
164 int scroll_bar_height = getHeight();
165 scroll_bar_->setVisible(true);
166 scroll_bar_->setBounds(getWidth() - scroll_bar_width, 0, scroll_bar_width, scroll_bar_height);
168 }
169 else
170 scroll_bar_->setVisible(false);
171
172 redoImage();
173}
174
176 selections_ = std::move(selections);
177 selected_ = std::min(selected_, selections_.size() - 1);
178 hovered_ = std::min(hovered_, selections_.size() - 1);
179 for (int i = 0; i < selections_.size(); ++i) {
180 if (selections_.items[i].selected)
181 selected_ = i;
182 }
183 resized();
184}
185
186int PopupList::getRowFromPosition(float mouse_position) {
187 int index = floorf((mouse_position + getViewPosition()) / getRowHeight());
188 if (index < selections_.size() && index >= 0 && selections_.items[index].id < 0)
189 return -1;
190 return index;
191}
192
194 static constexpr int kMinWidth = 150;
195
196 Font font = getFont();
197 int max_width = kMinWidth * size_ratio_;
198 int buffer = getTextPadding() * 2 + 2;
199 for (int i = 0; i < selections_.size(); ++i)
200 max_width = std::max(max_width, font.getStringWidth(selections_.items[i].name) / getPixelMultiple() + buffer);
201
202 return max_width;
203}
204
205void PopupList::mouseMove(const MouseEvent& e) {
206 int row = getRowFromPosition(e.position.y);
207 if (row >= selections_.size() || row < 0)
208 row = -1;
209 hovered_ = row;
210}
211
212void PopupList::mouseDrag(const MouseEvent& e) {
213 int row = getRowFromPosition(e.position.y);
214 if (e.position.x < 0 || e.position.x > getWidth() || row >= selections_.size() || row < 0)
215 row = -1;
216 hovered_ = row;
217}
218
219void PopupList::mouseExit(const MouseEvent& e) {
220 hovered_ = -1;
221}
222
223int PopupList::getSelection(const MouseEvent& e) {
224 float click_y_position = e.position.y;
225 int row = getRowFromPosition(click_y_position);
226 if (row < selections_.size() && row >= 0)
227 return row;
228
229 return -1;
230}
231
232void PopupList::mouseUp(const MouseEvent& e) {
233 if (e.position.x < 0 || e.position.x > getWidth())
234 return;
235
237}
238
239void PopupList::mouseDoubleClick(const MouseEvent& e) {
240 int selection = getSelection(e);
241 if (selection != selected_ || selection < 0)
242 return;
243
244 for (Listener* listener : listeners_)
245 listener->doubleClickedSelected(this, selections_.items[selection].id, selection);
246}
247
248void PopupList::select(int selection) {
249 if (selection < 0 || selection >= selections_.size())
250 return;
251
252 selected_ = selection;
253 for (int i = 0; i < selections_.size(); ++i)
254 selections_.items[i].selected = false;
255 selections_.items[selected_].selected = true;
256
257 for (Listener* listener : listeners_)
258 listener->newSelection(this, selections_.items[selection].id, selection);
259}
260
262 rows_.init(open_gl);
263 rows_.setColor(Colours::white);
264
265 highlight_.init(open_gl);
266 hover_.init(open_gl);
268}
269
270void PopupList::redoImage() {
271 if (getWidth() <= 0 || getHeight() <= 0)
272 return;
273
274 int mult = getPixelMultiple();
275 int row_height = getRowHeight() * mult;
276 int image_width = getWidth() * mult;
277
278 Colour text_color = findColour(Skin::kTextComponentText, true);
279 Colour lighten = findColour(Skin::kLightenScreen, true);
280 int image_height = std::max(row_height * selections_.size(), getHeight());
281 Image rows_image(Image::ARGB, image_width, image_height, true);
282 Graphics g(rows_image);
283 g.setColour(text_color);
284 g.setFont(getFont());
285
286 int padding = getTextPadding();
287 int width = (getWidth() - 2 * padding) * mult;
288 for (int i = 0; i < selections_.size(); ++i) {
289 if (selections_.items[i].id < 0) {
290 g.setColour(lighten);
291 int y = row_height * (i + 0.5f);
292 g.drawRect(padding, y, width, 1);
293 }
294 else {
295 g.setColour(text_color);
296 String name = selections_.items[i].name;
297 g.drawText(name, padding, row_height * i, width, row_height, Justification::centredLeft, true);
298 }
299 }
300 rows_.setOwnImage(rows_image);
301}
302
303void PopupList::moveQuadToRow(OpenGlQuad& quad, int row) {
304 int row_height = getRowHeight();
305 float view_height = getHeight();
306 float open_gl_row_height = 2.0f * row_height / view_height;
307 float offset = row * open_gl_row_height - 2.0f * getViewPosition() / view_height;
308
309 float y = 1.0f - offset;
310 quad.setQuad(0, -1.0f, y - open_gl_row_height, 2.0f, open_gl_row_height);
311}
312
314 Rectangle<int> view_bounds(getLocalBounds());
315 OpenGlComponent::setViewPort(this, view_bounds, open_gl);
316
317 float image_width = vital::utils::nextPowerOfTwo(getWidth());
318 float image_height = vital::utils::nextPowerOfTwo(rows_.getImageHeight());
319 float width_ratio = image_width / getWidth();
320 float height_ratio = image_height / (getPixelMultiple() * getHeight());
321 float y_offset = 2.0f * getViewPosition() / getHeight();
322
323 rows_.setTopLeft(-1.0f, 1.0f + y_offset);
324 rows_.setTopRight(2.0f * width_ratio - 1.0f, 1.0f + y_offset);
325 rows_.setBottomLeft(-1.0f, 1.0f + y_offset - 2.0f * height_ratio);
326 rows_.setBottomRight(2.0f * width_ratio - 1.0f, 1.0f + y_offset - 2.0f * height_ratio);
327 rows_.drawImage(open_gl);
328
329 if (hovered_ >= 0) {
330 moveQuadToRow(hover_, hovered_);
331 if (show_selected_)
332 hover_.setColor(findColour(Skin::kLightenScreen, true));
333 else
334 hover_.setColor(findColour(Skin::kWidgetPrimary1, true).darker(0.8f));
335 hover_.render(open_gl, animate);
336 }
337 if (selected_ >= 0 && show_selected_) {
338 moveQuadToRow(highlight_, selected_);
339 highlight_.setColor(findColour(Skin::kWidgetPrimary1, true).darker(0.8f));
340 highlight_.render(open_gl, animate);
341 }
342
344}
345
347 rows_.destroy(open_gl);
348
349 highlight_.destroy(open_gl);
350 hover_.destroy(open_gl);
352}
353
355 view_position_ = 0;
357}
358
359void PopupList::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) {
360 view_position_ -= wheel.deltaY * kScrollSensitivity;
361 view_position_ = std::max(0.0f, view_position_);
362 float scaled_height = getHeight();
363 int scrollable_range = getScrollableRange();
364 view_position_ = std::min(view_position_, 1.0f * scrollable_range - scaled_height);
366}
367
368void PopupList::scrollBarMoved(ScrollBar* scroll_bar, double range_start) {
369 view_position_ = range_start;
370}
371
373 static constexpr float kScrollStepRatio = 0.05f;
374
375 float scaled_height = getHeight();
376 scroll_bar_->setRangeLimits(0.0f, getScrollableRange());
377 scroll_bar_->setCurrentRange(getViewPosition(), scaled_height, dontSendNotification);
378 scroll_bar_->setSingleStepSize(scroll_bar_->getHeight() * kScrollStepRatio);
379 scroll_bar_->cancelPendingUpdate();
380}
381
383 int row_height = getRowHeight();
384 int selections_height = row_height * static_cast<int>(selections_.size());
385 return std::max(selections_height, getHeight());
386}
387
388SelectionList::SelectionList() : SynthSection("Selection List"), favorites_option_(false),
389 num_view_selections_(0), hovered_(-1), x_area_(false), cache_position_(0), is_additional_(),
390 highlight_(Shaders::kColorFragment), hover_(Shaders::kColorFragment),
391 remove_additional_x_("remove_additional"), view_position_(0.0f) {
392 addAndMakeVisible(browse_area_);
393 addChildComponent(remove_additional_x_);
394
395 remove_additional_x_.setShape(Paths::thickX());
396 browse_area_.setInterceptsMouseClicks(false, false);
397 highlight_.setTargetComponent(&browse_area_);
398 hover_.setTargetComponent(&browse_area_);
399
400 scroll_bar_ = std::make_unique<OpenGlScrollBar>();
401 addAndMakeVisible(scroll_bar_.get());
402 addOpenGlComponent(scroll_bar_->getGlComponent());
403 scroll_bar_->addListener(this);
404
405 highlight_.setAdditive(true);
406 hover_.setAdditive(true);
407
408 favorites_ = LoadSave::getFavorites();
409}
410
412 int scroll_bar_width = kScrollBarWidth * getSizeRatio();
413 int scroll_bar_height = getHeight();
414 scroll_bar_->setBounds(getWidth() - scroll_bar_width, 0, scroll_bar_width, scroll_bar_height);
416
417 browse_area_.setBounds(2, 0, scroll_bar_->getX() - 3, getHeight());
418 int row_height = getRowHeight();
419 remove_additional_x_.setBounds(0, 0, row_height, row_height);
420 remove_additional_x_.redrawImage(false);
421
422 loadBrowserCache(cache_position_, cache_position_ + kNumCachedRows);
423
424 Colour lighten = findColour(Skin::kLightenScreen, true);
425 scroll_bar_->setColor(lighten);
426}
427
428void SelectionList::sort() {
429 sortFileArray<FileNameAscendingComparator>(selections_);
430 filter(filter_string_);
432}
433
434void SelectionList::setSelections(Array<File> selections) {
435 selections_ = std::move(selections);
436 sort();
437 redoCache();
438}
439
441 view_position_ = 0;
442 viewPositionChanged();
444}
445
446void SelectionList::mouseWheelMove(const MouseEvent& e, const MouseWheelDetails& wheel) {
447 view_position_ -= wheel.deltaY * kScrollSensitivity;
448 view_position_ = std::max(0.0f, view_position_);
449 float scaled_height = getHeight();
450 int scrollable_range = getScrollableRange();
451 view_position_ = std::min(view_position_, 1.0f * scrollable_range - scaled_height);
452 viewPositionChanged();
454}
455
456int SelectionList::getRowFromPosition(float mouse_position) {
457 return floorf((mouse_position + getViewPosition()) / getRowHeight());
458}
459
460void SelectionList::mouseMove(const MouseEvent& e) {
461 hovered_ = getRowFromPosition(e.position.y);
462 if (hovered_ >= filtered_selections_.size())
463 hovered_ = -1;
464
465 int row_height = getRowHeight();
466 int x = e.position.x - getWidth() + kScrollBarWidth * size_ratio_ + row_height;
467 x_area_ = x >= 0 && x < row_height;
468}
469
470void SelectionList::mouseExit(const MouseEvent& e) {
471 hovered_ = -1;
472}
473
475 if (result < 0 || result >= filtered_selections_.size())
476 return;
477
478 filtered_selections_[result].revealToUser();
479}
480
481void SelectionList::menuClick(const MouseEvent& e) {
482 float click_y_position = e.position.y;
483 int row = getRowFromPosition(click_y_position);
484
485 if (row >= 0 && hovered_ >= 0) {
486 PopupItems options;
487 options.addItem(hovered_, "Open File Location");
488 showPopupSelector(this, e.getPosition(), options, [=](int selection) { respondToMenuCallback(selection); });
489 }
490}
491
492File SelectionList::getSelection(const MouseEvent& e) {
493 float click_y_position = e.position.y;
494 int row = getRowFromPosition(click_y_position);
495 if (row < filtered_selections_.size() && row >= 0)
496 return filtered_selections_[row];
497
498 return File();
499}
500
501void SelectionList::leftClick(const MouseEvent& e) {
502 float click_x_position = e.position.x;
503 int star_right = getRowHeight() + getIconPadding();
504 File selection = getSelection(e);
505 if (!selection.exists() && selection != getFavoritesFile() && selection != getAllFile()) {
506 if (selection.getFileName() == String(kAddFolderName))
508 return;
509 }
510
511 if (click_x_position < star_right)
512 selectIcon(selection);
513 else if (x_area_)
514 removeAdditionalFolder(selection);
515 else
516 select(selection);
517}
518
519void SelectionList::mouseDown(const MouseEvent& e) {
520 if (e.mods.isPopupMenu())
521 menuClick(e);
522 else
523 leftClick(e);
524}
525
526void SelectionList::mouseDoubleClick(const MouseEvent& e) {
527 float click_x_position = e.position.x;
528 int star_right = getRowHeight() + getIconPadding();
529 File selection = getSelection(e);
530 if (!selection.exists())
531 return;
532
533 if (click_x_position < star_right || selection != selected_)
534 return;
535
536 for (Listener* listener : listeners_)
537 listener->doubleClickedSelected(selection);
538}
539
541 FileChooser open_box("Add Folder", File());
542 if (open_box.browseForDirectory()) {
543 File result = open_box.getResult();
544 if (result.exists()) {
545 if (isAcceptableRoot(result)) {
546 std::vector<std::string> roots = LoadSave::getAdditionalFolders(additional_roots_name_);
547 for (const std::string& root : roots) {
548 if (result == File(root)) {
549 NativeMessageBox::showMessageBoxAsync(AlertWindow::WarningIcon, "Error Adding Folder",
550 String("Folder already added"));
551 return;
552 }
553 }
554 if (selections_.contains(File(result))) {
555 NativeMessageBox::showMessageBoxAsync(AlertWindow::WarningIcon, "Error Adding Folder",
556 String("Folder already added"));
557 return;
558 }
559 additional_roots_.add(result);
560 roots.push_back(result.getFullPathName().toStdString());
561 LoadSave::saveAdditionalFolders(additional_roots_name_, roots);
562 sort();
563 redoCache();
564 }
565 else
566 showAddRootWarning();
567 }
568 }
569}
570
572 additional_roots_.removeFirstMatchingValue(folder);
573 std::vector<std::string> roots = LoadSave::getAdditionalFolders(additional_roots_name_);
574 std::string path = folder.getFullPathName().toStdString();
575 const auto& pos = std::find(roots.begin(), roots.end(), path);
576 if (pos != roots.end())
577 roots.erase(pos);
578 LoadSave::saveAdditionalFolders(additional_roots_name_, roots);
579
580 sort();
581 redoCache();
582}
583
584void SelectionList::select(const File& selection) {
585 if (selection.exists() && selection.isDirectory() && selection == selected_) {
586 toggleOpenFolder(selection);
587 return;
588 }
589 selected_ = selection;
590
591 if (selected_ == getFavoritesFile()) {
592 for (Listener* listener : listeners_)
593 listener->favoritesSelected();
594 }
595 else if (selected_ == getAllFile()) {
596 for (Listener* listener : listeners_)
597 listener->allSelected();
598 }
599 else if (selected_.exists()) {
600 for (Listener* listener : listeners_)
601 listener->newSelection(selection);
602 }
603}
604
605void SelectionList::selectIcon(const File& selection) {
606 if (selection.isDirectory())
607 select(selection);
608 else
609 toggleFavorite(selection);
610}
611
612void SelectionList::toggleFavorite(const File& selection) {
613 if (selection == getFavoritesFile() || selection == getAllFile()) {
614 select(selection);
615 return;
616 }
617
618 std::string path = selection.getFullPathName().toStdString();
619 if (favorites_.count(path)) {
620 favorites_.erase(path);
621 LoadSave::removeFavorite(selection);
622 }
623 else {
624 favorites_.insert(path);
625 LoadSave::addFavorite(selection);
626 }
627 redoCache();
628}
629
630void SelectionList::toggleOpenFolder(const File& selection) {
631 std::string path = selection.getFullPathName().toStdString();
632 Array<File> children;
633 selection.findChildFiles(children, File::findDirectories, false);
634 if (open_folders_.count(path))
635 open_folders_.erase(path);
636 else if (!children.isEmpty())
637 open_folders_[path] = getFolderDepth(selection);
638 sort();
639 redoCache();
640}
641
642void SelectionList::scrollBarMoved(ScrollBar* scroll_bar, double range_start) {
643 view_position_ = range_start;
644 viewPositionChanged();
645}
646
648 static constexpr float kScrollStepRatio = 0.05f;
649
650 float scaled_height = getHeight();
651 scroll_bar_->setRangeLimits(0.0f, getScrollableRange());
652 scroll_bar_->setCurrentRange(getViewPosition(), scaled_height, dontSendNotification);
653 scroll_bar_->setSingleStepSize(scroll_bar_->getHeight() * kScrollStepRatio);
654 scroll_bar_->cancelPendingUpdate();
655}
656
658 if (getWidth() <= 0 || getHeight() <= 0)
659 return;
660
661 int max = static_cast<int>(filtered_selections_.size()) - kNumCachedRows;
662 int position = std::max(0, std::min<int>(cache_position_, max));
663 loadBrowserCache(position, position + kNumCachedRows);
664}
665
666int SelectionList::getFolderDepth(const File& file) {
667 std::string parent_string = file.getParentDirectory().getFullPathName().toStdString();
668 if (open_folders_.count(parent_string))
669 return open_folders_[parent_string] + 1;
670 return 0;
671}
672
673void SelectionList::addSubfolderSelections(const File& selection, std::vector<File>& selections) {
674 Array<File> children;
675 selection.findChildFiles(children, File::findDirectories, false);
676 for (const File& child : children) {
677 selections.push_back(child);
678 if (open_folders_.count(child.getFullPathName().toStdString()))
679 addSubfolderSelections(child, selections);
680 }
681}
682
683void SelectionList::setAdditionalRootsName(const std::string& name) {
684 additional_roots_name_ = name;
685 additional_roots_.clear();
686 if (!name.empty()) {
687 std::vector<std::string> roots = LoadSave::getAdditionalFolders(additional_roots_name_);
688 for (const std::string& root : roots) {
689 File file(root);
690 if (file.exists())
691 additional_roots_.add(file);
692 }
693 }
694}
695
696void SelectionList::filter(const String& filter_string) {
697 filter_string_ = filter_string.toLowerCase();
698 StringArray tokens;
699 tokens.addTokens(filter_string_, " ", "");
700 filtered_selections_.clear();
701 if (favorites_option_) {
702 filtered_selections_.push_back(getAllFile());
703 filtered_selections_.push_back(getFavoritesFile());
704 }
705
706 Array<File> all_selections = selections_;
707 all_selections.addArray(additional_roots_);
708
709 for (const File& selection : all_selections) {
710 bool match = true;
711 if (tokens.size()) {
712 String name = selection.getFileNameWithoutExtension().toLowerCase();
713
714 for (const String& token : tokens) {
715 if (!name.contains(token))
716 match = false;
717 }
718 }
719 if (match) {
720 filtered_selections_.push_back(selection);
721 if (open_folders_.count(selection.getFullPathName().toStdString()))
722 addSubfolderSelections(selection, filtered_selections_);
723 }
724 }
725
726 if (!additional_roots_name_.empty())
727 filtered_selections_.push_back(File::getCurrentWorkingDirectory().getChildFile("_").getChildFile(kAddFolderName));
728 num_view_selections_ = static_cast<int>(filtered_selections_.size());
729
730 auto found = std::find(filtered_selections_.begin(), filtered_selections_.end(), selected_);
731 if (found == filtered_selections_.end())
732 selected_ = File();
733}
734
736 for (int i = 0; i < filtered_selections_.size(); ++i) {
737 if (selected_ == filtered_selections_[i])
738 return i;
739 }
740 return -1;
741}
742
744 int row_height = getRowHeight();
745 int presets_height = row_height * static_cast<int>(filtered_selections_.size());
746 return std::max(presets_height, getHeight());
747}
748
750 if (filtered_selections_.empty())
751 return;
752
753 int index = getSelectedIndex();
754 index = (index + 1) % filtered_selections_.size();
755 select(filtered_selections_[index]);
756}
757
759 if (filtered_selections_.empty())
760 return;
761
762 int index = std::max(0, getSelectedIndex());
763 index = (index - 1 + filtered_selections_.size()) % filtered_selections_.size();
764 select(filtered_selections_[index]);
765}
766
768 for (OpenGlImage& row : rows_) {
769 row.setScissor(true);
770 row.init(open_gl);
771 row.setColor(Colours::white);
772 }
773
774 highlight_.init(open_gl);
775 hover_.init(open_gl);
776 remove_additional_x_.init(open_gl);
778}
779
780void SelectionList::viewPositionChanged() {
781 int row_height = getRowHeight();
782
783 int last_cache_position = cache_position_;
784 cache_position_ = getViewPosition() / row_height;
785 int max = static_cast<int>(filtered_selections_.size() - kNumCachedRows);
786 cache_position_ = std::max(0, std::min<int>(cache_position_, max));
787
788 if (std::abs(cache_position_ - last_cache_position) >= kNumCachedRows)
789 redoCache();
790 else if (last_cache_position < cache_position_)
791 loadBrowserCache(last_cache_position + kNumCachedRows, cache_position_ + kNumCachedRows);
792 else if (last_cache_position > cache_position_)
793 loadBrowserCache(cache_position_, last_cache_position);
794}
795
796void SelectionList::loadBrowserCache(int start_index, int end_index) {
797 int mult = getPixelMultiple();
798 int row_height = getRowHeight() * mult;
799 int image_width = getWidth() * mult;
800
801 int padding = getIconPadding();
802 int icon_x = padding;
803 int icon_width = row_height;
804 int name_x = icon_x + icon_width + padding;
805 int name_width = image_width - name_x;
806
807 end_index = std::min(static_cast<int>(filtered_selections_.size()), end_index);
808 Font font = Fonts::instance()->proportional_light().withPointHeight(row_height * 0.55f);
809
810 Path star = Paths::star();
811 Path folder = Paths::folder();
812 float star_draw_width = row_height * 0.8f;
813 float star_y = (row_height - star_draw_width) / 2.0f;
814 Rectangle<float> star_bounds(icon_x + (icon_width - star_draw_width) / 2.0f, star_y,
815 star_draw_width, star_draw_width);
816 star.applyTransform(star.getTransformToScaleToFit(star_bounds, true));
817
818 float folder_draw_width = row_height * 0.6f;
819 float folder_y = (row_height - folder_draw_width) / 2.0f;
820 Rectangle<float> folder_bounds(icon_x + (icon_width - folder_draw_width) / 2.0f, folder_y,
821 folder_draw_width, folder_draw_width);
822 folder.applyTransform(folder.getTransformToScaleToFit(folder_bounds, true));
823 PathStrokeType icon_stroke(1.0f, PathStrokeType::curved);
824
825 Colour text_color = findColour(Skin::kTextComponentText, true);
826 Colour icon_unselected = text_color.withMultipliedAlpha(0.5f);
827 Colour icon_selected = findColour(Skin::kWidgetPrimary1, true);
828
829 for (int i = start_index; i < end_index; ++i) {
830 Image row_image(Image::ARGB, image_width, row_height, true);
831 Graphics g(row_image);
832
833 File selection = filtered_selections_[i];
834 String name = selection.getFileNameWithoutExtension();
835 if (selection.isDirectory()) {
836 int parents = getFolderDepth(selection);
837 g.addTransform(AffineTransform::translation(Point<int>(parents * folder_draw_width, 0)));
838
839 if (name == String(passthrough_name_))
840 name = selection.getParentDirectory().getFileNameWithoutExtension();
841
842 g.setColour(icon_unselected);
843 if (open_folders_.count(selection.getFullPathName().toStdString()))
844 g.fillPath(folder);
845
846 g.strokePath(folder, icon_stroke);
847 }
848 else if (selection.getFileName() == String(kAddFolderName)) {
849 g.setColour(icon_unselected);
850 Path add_folder_path;
851 float dashes[2] = { 4.0f * size_ratio_, 2.0f * size_ratio_ };
852 icon_stroke.createDashedStroke(add_folder_path, folder, dashes, 2);
853 g.fillPath(add_folder_path);
854 }
855 else if (selection.exists() || selection.getFileName() == "Favorites") {
856 if (favorites_.count(selection.getFullPathName().toStdString())) {
857 g.setColour(icon_selected);
858 g.fillPath(star);
859 }
860 else
861 g.setColour(icon_unselected);
862
863 g.strokePath(star, icon_stroke);
864 }
865
866 g.setColour(text_color);
867 g.setFont(font);
868 g.drawText(name, name_x, 0, name_width - 2 * padding, row_height, Justification::centredLeft, true);
869 rows_[i % kNumCachedRows].setOwnImage(row_image);
870 is_additional_[i % kNumCachedRows] = additional_roots_.contains(selection);
871 }
872}
873
874void SelectionList::moveQuadToRow(OpenGlQuad& quad, int row, float y_offset) {
875 int row_height = getRowHeight();
876 float view_height = getHeight();
877 float open_gl_row_height = 2.0f * row_height / view_height;
878 float offset = row * open_gl_row_height;
879
880 float y = 1.0f + y_offset - offset;
881 quad.setQuad(0, -1.0f, y - open_gl_row_height, 2.0f, open_gl_row_height);
882}
883
885 float view_height = getHeight();
886 int row_height = getRowHeight();
887 int num_presets = num_view_selections_;
888
889 int view_position = getViewPosition();
890 float y_offset = 2.0f * view_position / view_height;
891
892 Rectangle<int> view_bounds(getLocalBounds());
893 OpenGlComponent::setViewPort(this, view_bounds, open_gl);
894
895 float image_width = vital::utils::nextPowerOfTwo(getWidth());
896 float image_height = vital::utils::nextPowerOfTwo(row_height);
897 float width_ratio = image_width / getWidth();
898 float height_ratio = image_height / row_height;
899
900 float open_gl_row_height = height_ratio * 2.0f * row_height / view_height;
901 int cache_position = std::max(0, std::min(cache_position_, num_presets - kNumCachedRows));
902 for (int i = 0; i < kNumCachedRows && i < num_presets; ++i) {
903 int row = cache_position + i;
904 int cache_index = row % kNumCachedRows;
905 float offset = (2.0f * row_height * row) / view_height;
906 float y = 1.0f + y_offset - offset;
907
908 Rectangle<int> row_bounds(0, row_height * row - view_position, getWidth(), row_height);
909 OpenGlComponent::setScissorBounds(this, row_bounds, open_gl);
910
911 rows_[cache_index].setTopLeft(-1.0f, y);
912 rows_[cache_index].setTopRight(-1.0f + 2.0f * width_ratio, y);
913 rows_[cache_index].setBottomLeft(-1.0f, y - open_gl_row_height);
914 rows_[cache_index].setBottomRight(-1.0f + 2.0f * width_ratio, y - open_gl_row_height);
915 rows_[cache_index].drawImage(open_gl);
916 }
917
918 int selected_index = getSelectedIndex();
919 if (selected_index >= 0) {
920 moveQuadToRow(highlight_, selected_index, y_offset);
921 highlight_.setColor(findColour(Skin::kWidgetPrimary1, true).darker(0.8f));
922 highlight_.render(open_gl, animate);
923 }
924
925 if (hovered_ >= 0) {
926 moveQuadToRow(hover_, hovered_, y_offset);
927 hover_.setColor(findColour(Skin::kLightenScreen, true));
928 hover_.render(open_gl, animate);
929
930 int cache_index = hovered_ % kNumCachedRows;
931
932 int scroll_bar_width = kScrollBarWidth * size_ratio_;
933 Rectangle<int> bounds(getWidth() - row_height - scroll_bar_width, row_height * hovered_ - view_position_,
934 row_height, row_height);
935 if (OpenGlComponent::setViewPort(&browse_area_, bounds, open_gl) && is_additional_[cache_index]) {
936 if (x_area_)
937 remove_additional_x_.setColor(findColour(Skin::kIconButtonOffHover, true));
938 else
939 remove_additional_x_.setColor(findColour(Skin::kIconButtonOff, true));
940 remove_additional_x_.image().drawImage(open_gl);
941 }
942 }
943
945}
946
948 for (OpenGlImage& row : rows_)
949 row.destroy(open_gl);
950
951 highlight_.destroy(open_gl);
952 hover_.destroy(open_gl);
953 remove_additional_x_.destroy(open_gl);
955}
956
958 body_(Shaders::kRoundedRectangleFragment),
959 border_(Shaders::kRoundedRectangleBorderFragment) {
960 callback_ = nullptr;
961 cancel_ = nullptr;
962 addOpenGlComponent(&body_);
963 addOpenGlComponent(&border_);
964
965 popup_list_ = std::make_unique<PopupList>();
966 popup_list_->addListener(this);
967 addSubSection(popup_list_.get());
968 popup_list_->setAlwaysOnTop(true);
969 popup_list_->setWantsKeyboardFocus(false);
970
972}
973
976
977 Rectangle<int> bounds = getLocalBounds();
978 int rounding = findValue(Skin::kBodyRounding);
979 popup_list_->setBounds(1, rounding, getWidth() - 2, getHeight() - 2 * rounding);
980
981 body_.setBounds(bounds);
983 body_.setColor(findColour(Skin::kBody, true));
984
985 border_.setBounds(bounds);
987 border_.setThickness(1.0f, true);
988 border_.setColor(findColour(Skin::kBorder, true));
989}
990
991void SinglePopupSelector::setPosition(Point<int> position, Rectangle<int> bounds) {
992 int rounding = findValue(Skin::kBodyRounding);
993 int width = popup_list_->getBrowseWidth();
994 int height = popup_list_->getBrowseHeight() + 2 * rounding;
995 int x = position.x;
996 int y = position.y;
997 if (x + width > bounds.getRight())
998 x -= width;
999 if (y + height > bounds.getBottom())
1000 y = bounds.getBottom() - height;
1001 setBounds(x, y, width, height);
1002}
1003
1005 body_(Shaders::kRoundedRectangleFragment),
1006 border_(Shaders::kRoundedRectangleBorderFragment),
1007 divider_(Shaders::kColorFragment) {
1008 callback_ = nullptr;
1009
1010 addOpenGlComponent(&body_);
1011 addOpenGlComponent(&border_);
1012 addOpenGlComponent(&divider_);
1013
1014 left_list_ = std::make_unique<PopupList>();
1015 left_list_->addListener(this);
1016 addSubSection(left_list_.get());
1017 left_list_->setAlwaysOnTop(true);
1018 left_list_->setWantsKeyboardFocus(false);
1019 left_list_->showSelected(true);
1020
1021 right_list_ = std::make_unique<PopupList>();
1022 right_list_->addListener(this);
1023 addSubSection(right_list_.get());
1024 right_list_->setAlwaysOnTop(true);
1025 right_list_->setWantsKeyboardFocus(false);
1026 right_list_->showSelected(true);
1027
1029}
1030
1033
1034 Rectangle<int> bounds = getLocalBounds();
1035 int rounding = findValue(Skin::kBodyRounding);
1036 int height = getHeight() - 2 * rounding;
1037 left_list_->setBounds(1, rounding, getWidth() / 2 - 2, height);
1038 int right_x = left_list_->getRight() + 1;
1039 right_list_->setBounds(right_x, rounding, getWidth() - right_x - 1, height);
1040
1041 body_.setBounds(bounds);
1043 body_.setColor(findColour(Skin::kBody, true));
1044
1045 border_.setBounds(bounds);
1047 border_.setThickness(1.0f, true);
1048
1049 divider_.setBounds(getWidth() / 2 - 1, 1, 1, getHeight() - 2);
1050
1051 Colour border = findColour(Skin::kBorder, true);
1052 border_.setColor(border);
1053 divider_.setColor(border);
1054}
1055
1056void DualPopupSelector::setPosition(Point<int> position, int width, Rectangle<int> bounds) {
1057 int rounding = findValue(Skin::kBodyRounding);
1058 int height = left_list_->getBrowseHeight() + 2 * rounding;
1059 int x = position.x;
1060 int y = position.y;
1061 if (x + width > bounds.getRight())
1062 x -= width;
1063 if (y + height > bounds.getBottom())
1064 y = bounds.getBottom() - height;
1065 setBounds(x, y, width, height);
1066}
1067
1068void DualPopupSelector::newSelection(PopupList* list, int id, int index) {
1069 if (list == left_list_.get()) {
1070 PopupItems right_items = left_list_->getSelectionItems(index);
1071 if (right_items.size() == 0) {
1072 callback_(id);
1073 right_list_->setSelections(right_items);
1074 return;
1075 }
1076
1077 int right_selection = right_list_->getSelected();
1078 if (right_selection < 0 || right_selection >= right_items.size() ||
1079 right_list_->getSelectionItems(right_selection).name != right_items.items[right_selection].name) {
1080 right_selection = 0;
1081 }
1082
1083 right_list_->setSelections(right_items);
1084 right_list_->select(right_selection);
1085 }
1086 else
1087 callback_(id);
1088}
1089
1091 body_(Shaders::kRoundedRectangleFragment),
1092 border_(Shaders::kRoundedRectangleBorderFragment),
1093 horizontal_divider_(Shaders::kColorFragment),
1094 vertical_divider_(Shaders::kColorFragment),
1095 owner_(nullptr) {
1096 addKeyListener(this);
1097 setInterceptsMouseClicks(false, true);
1098
1099 addOpenGlComponent(&body_);
1100 addOpenGlComponent(&border_);
1101 addOpenGlComponent(&horizontal_divider_);
1102 addOpenGlComponent(&vertical_divider_);
1103
1104 folder_list_ = std::make_unique<SelectionList>();
1105 folder_list_->addFavoritesOption();
1106 folder_list_->addListener(this);
1107 addSubSection(folder_list_.get());
1108 folder_list_->setAlwaysOnTop(true);
1109
1110 selection_list_ = std::make_unique<SelectionList>();
1111 selection_list_->addListener(this);
1112 addSubSection(selection_list_.get());
1113 selection_list_->setAlwaysOnTop(true);
1114
1115 for (int i = 0; i < 4; ++i) {
1116 addAndMakeVisible(closing_areas_[i]);
1117 closing_areas_[i].addListener(this);
1118 }
1119
1120 exit_button_ = std::make_unique<OpenGlShapeButton>("Exit");
1121 addAndMakeVisible(exit_button_.get());
1122 addOpenGlComponent(exit_button_->getGlComponent());
1123 exit_button_->addListener(this);
1124 exit_button_->setShape(Paths::exitX());
1125
1126 store_button_ = std::make_unique<OpenGlToggleButton>("Store");
1127 store_button_->setUiButton(true);
1128 addButton(store_button_.get());
1129 store_button_->setVisible(false);
1130
1131 download_button_ = std::make_unique<OpenGlToggleButton>("Login");
1132 download_button_->setUiButton(true);
1133 download_button_->setText("Download content");
1134 addButton(download_button_.get());
1135 download_button_->setVisible(false);
1136
1137#if !defined(NO_TEXT_ENTRY)
1138 search_box_ = std::make_unique<OpenGlTextEditor>("Search");
1139 search_box_->addListener(this);
1140 search_box_->setSelectAllWhenFocused(true);
1141 search_box_->setMultiLine(false, false);
1142 search_box_->setJustification(Justification::centredLeft);
1143 addAndMakeVisible(search_box_.get());
1144 addOpenGlComponent(search_box_->getImageComponent());
1145#endif
1146
1147 setWantsKeyboardFocus(true);
1148 setMouseClickGrabsKeyboardFocus(true);
1150}
1151
1152PopupBrowser::~PopupBrowser() = default;
1153
1155 static constexpr float kBrowseWidthRatio = 0.5f;
1156 static constexpr float kTopHeight = 38.0f;
1157
1159
1160 closing_areas_[0].setBounds(0, 0, passthrough_bounds_.getX(), getHeight());
1161 closing_areas_[1].setBounds(passthrough_bounds_.getRight(), 0,
1162 getWidth() - passthrough_bounds_.getRight(), getHeight());
1163 closing_areas_[2].setBounds(0, 0, getWidth(), passthrough_bounds_.getY());
1164 closing_areas_[3].setBounds(0, passthrough_bounds_.getBottom(),
1165 getWidth(), getHeight() - passthrough_bounds_.getBottom());
1166
1167 body_.setBounds(browser_bounds_);
1169 body_.setColor(findColour(Skin::kBody, true));
1170
1171 border_.setBounds(browser_bounds_);
1173 border_.setThickness(1.0f, true);
1174
1175 Colour border = findColour(Skin::kBorder, true);
1176 border_.setColor(border);
1177 horizontal_divider_.setColor(border);
1178 vertical_divider_.setColor(border);
1179
1180 Colour empty_color = findColour(Skin::kBodyText, true);
1181 empty_color = empty_color.withAlpha(0.5f * empty_color.getFloatAlpha());
1182
1183 if (search_box_) {
1184 search_box_->setTextToShowWhenEmpty(TRANS("Search"), empty_color);
1185 search_box_->setColour(CaretComponent::caretColourId, findColour(Skin::kTextEditorCaret, true));
1186 search_box_->setColour(TextEditor::textColourId, findColour(Skin::kBodyText, true));
1187 search_box_->setColour(TextEditor::highlightedTextColourId, findColour(Skin::kBodyText, true));
1188 search_box_->setColour(TextEditor::highlightColourId, findColour(Skin::kTextEditorSelection, true));
1189 }
1190
1191 int selection_list_width = browser_bounds_.getWidth() * kBrowseWidthRatio;
1192 int top_height = kTopHeight * size_ratio_;
1193 int folder_list_width = browser_bounds_.getWidth() - selection_list_width;
1194 int list_height = browser_bounds_.getHeight() - top_height - 2;
1195 int x = browser_bounds_.getX();
1196 int y = browser_bounds_.getY();
1197
1198 folder_list_->setBounds(x, y + top_height + 1, folder_list_width - 1, list_height);
1199 selection_list_->setBounds(x + folder_list_width, y + top_height + 1, selection_list_width - 3, list_height);
1200 horizontal_divider_.setBounds(x + 1, y + top_height - 1, browser_bounds_.getWidth() - 2, 1);
1201 vertical_divider_.setBounds(x + folder_list_width, y + top_height, 1, list_height);
1202
1203 int padding = getPadding();
1204 int text_height = top_height - 2 * padding;
1205 download_button_->setBounds(x + padding, y + padding, selection_list_width - 2 * padding, text_height);
1206 if (search_box_) {
1207 search_box_->setBounds(download_button_->getBounds());
1208 search_box_->resized();
1209 }
1210
1211 int store_x = x + padding + selection_list_width;
1212 store_button_->setBounds(store_x, y + padding, browser_bounds_.getRight() - store_x - top_height, text_height);
1213 exit_button_->setBounds(x + browser_bounds_.getWidth() - top_height, y, top_height, top_height);
1214
1215 Image image(Image::ARGB, 1, 1, false);
1216 Graphics g(image);
1218}
1219
1223 SynthSection::visibilityChanged();
1224 if (search_box_)
1225 search_box_->setText("");
1226
1227 File selected = folder_list_->selected();
1228 if (selected.exists())
1229 newSelection(selected);
1230}
1231
1232void PopupBrowser::newSelection(File selection) {
1233 if (selection.exists() && selection.isDirectory()) {
1234 Array<File> files;
1235 selection.findChildFiles(files, File::findFiles, true, extensions_);
1236 selection_list_->setSelections(files);
1237 selection_list_->resetScrollPosition();
1238 }
1239 else {
1240 if (owner_) {
1241 owner_->loadFile(selection);
1243 }
1244 }
1245}
1246
1248 Array<File> files;
1249 Array<File> folders = folder_list_->getSelections();
1250 folders.addArray(folder_list_->getAdditionalFolders());
1251 for (const File& folder : folders) {
1252 if (folder.exists() && folder.isDirectory())
1253 folder.findChildFiles(files, File::findFiles, true, extensions_);
1254 }
1255
1256 selection_list_->setSelections(files);
1257 selection_list_->resetScrollPosition();
1258}
1259
1261 Array<File> files;
1262 Array<File> folders = folder_list_->getSelections();
1263 folders.addArray(folder_list_->getAdditionalFolders());
1264 for (const File& folder : folders) {
1265 if (folder.exists() && folder.isDirectory())
1266 folder.findChildFiles(files, File::findFiles, true, extensions_);
1267 }
1268 Array<File> favorites;
1269 std::set<std::string> favorite_lookup = LoadSave::getFavorites();
1270 for (const File& file : files) {
1271 if (favorite_lookup.count(file.getFullPathName().toStdString()))
1272 favorites.add(file);
1273 }
1274 selection_list_->setSelections(favorites);
1275 selection_list_->resetScrollPosition();
1276}
1277
1279 if (selection.exists() && !selection.isDirectory())
1280 setVisible(false);
1281}
1282
1283bool PopupBrowser::keyPressed(const KeyPress &key, Component *origin) {
1284 if (!isVisible())
1285 return search_box_->hasKeyboardFocus(true);
1286
1287 if (key.getKeyCode() == KeyPress::escapeKey) {
1288 setVisible(false);
1289 return true;
1290 }
1291 if (key.getKeyCode() == KeyPress::upKey || key.getKeyCode() == KeyPress::leftKey) {
1292 selection_list_->selectPrev();
1293 return true;
1294 }
1295 if (key.getKeyCode() == KeyPress::downKey || key.getKeyCode() == KeyPress::rightKey) {
1296 selection_list_->selectNext();
1297 return true;
1298 }
1299 return search_box_->hasKeyboardFocus(true);
1300}
1301
1302bool PopupBrowser::keyStateChanged(bool is_key_down, Component *origin) {
1303 if (is_key_down)
1304 return search_box_->hasKeyboardFocus(true);
1305 return false;
1306}
1307
1308void PopupBrowser::closingAreaClicked(PopupClosingArea* closing_area, const MouseEvent& e) {
1309 if (!browser_bounds_.contains(e.getPosition() + closing_area->getPosition()))
1310 setVisible(false);
1311}
1312
1314 bool has_content = LoadSave::hasDataDirectory();
1315 if (search_box_)
1316 search_box_->setVisible(has_content);
1317 download_button_->setVisible(!has_content);
1318}
1319
1321 if (owner_) {
1322 std::string author = owner_->getFileAuthor();
1323 String type = folder_list_->getPassthroughFolderName();
1324 store_button_->setText("Get more " + type.toLowerCase().toStdString() + " by " + author);
1325 String cleaned = String(author).removeCharacters(" _.").toLowerCase();
1326 store_button_->setVisible(more_author_presets_.count(cleaned.toStdString()));
1327 }
1328}
1329
1330void PopupBrowser::loadPresets(std::vector<File> directories, const String& extensions,
1331 const std::string& passthrough_name, const std::string& additional_folders_name) {
1332 extensions_ = extensions;
1333 if (search_box_)
1334 search_box_->setText("");
1335
1336 Array<File> folders;
1337 for (const File& directory : directories)
1338 folders.add(directory);
1339
1340 folder_list_->setPassthroughFolderName(passthrough_name);
1341 folder_list_->setAdditionalRootsName(additional_folders_name);
1342 folder_list_->setSelections(folders);
1343
1344 if (!additional_folders_name.empty()) {
1345 std::vector<std::string> additional = LoadSave::getAdditionalFolders(additional_folders_name);
1346 for (const std::string& path : additional)
1347 directories.emplace_back(File(path));
1348 }
1349
1350 Array<File> presets;
1351 selection_list_->setSelected(File());
1352 folder_list_->filter("");
1353 if (!folder_list_->selected().exists()) {
1354 LoadSave::getAllFilesOfTypeInDirectories(presets, extensions_, directories);
1355 selection_list_->setSelections(presets);
1356 }
1357 selection_list_->filter("");
1358 if (owner_)
1359 selection_list_->setSelected(owner_->getCurrentFile());
1360
1361 more_author_presets_.clear();
1362 try {
1363 json available = LoadSave::getAvailablePacks();
1364 json available_packs = available["packs"];
1365 for (auto& pack : available_packs) {
1366 if (pack.count(folder_list_->getPassthroughFolderName()) == 0)
1367 continue;
1368
1369 bool contains_files = pack[folder_list_->getPassthroughFolderName()];
1370 if (!contains_files)
1371 continue;
1372
1373 bool purchased = false;
1374 if (pack.count("Purchased"))
1375 purchased = pack["Purchased"];
1376 if (purchased)
1377 continue;
1378
1379 std::string author_data = pack["Author"];
1380 StringArray authors;
1381 authors.addTokens(author_data, ",", "");
1382 for (const String& author : authors)
1383 more_author_presets_.insert(author.removeCharacters(" ._").toLowerCase().toStdString());
1384 }
1385 }
1386 catch (const json::exception& e) {
1387 }
1390}
1391
1393 selection_list_->filter(search_box_->getText());
1394 selection_list_->redoCache();
1395}
1396
1397void PopupBrowser::textEditorTextChanged(TextEditor& editor) {
1398 filterPresets();
1399}
1400
1402 editor.setText("");
1403}
1404
1405void PopupBrowser::buttonClicked(Button* clicked_button) {
1406 if (clicked_button == exit_button_.get())
1407 setVisible(false);
1408 else if (clicked_button == download_button_.get()) {
1409 FullInterface* parent = findParentComponentOfClass<FullInterface>();
1410 if (parent) {
1411 setVisible(false);
1412 parent->startDownload();
1413 }
1414 }
1415 else if (clicked_button == store_button_.get() && owner_) {
1416 String encoded_author = URL::addEscapeChars(owner_->getFileAuthor(), true);
1417 encoded_author = encoded_author.replace("+", "%2B");
1418
1419 URL url(String(kStoreUrl) + encoded_author);
1420 url.launchInDefaultBrowser();
1421 }
1422}
DualPopupSelector()
Constructs a DualPopupSelector.
Definition popup_browser.cpp:1004
void newSelection(PopupList *list, int id, int index) override
Called when a new selection is made.
Definition popup_browser.cpp:1068
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition popup_browser.cpp:1031
void setPosition(Point< int > position, int width, Rectangle< int > bounds)
Positions the DualPopupSelector.
Definition popup_browser.cpp:1056
Font & proportional_light()
Returns a reference to the proportional light font.
Definition fonts.h:28
static Fonts * instance()
Gets the singleton instance of the Fonts class.
Definition fonts.h:52
The main GUI container for the entire synthesizer interface.
Definition full_interface.h:61
void startDownload()
Starts a download of additional content if needed.
Definition full_interface.cpp:573
static void saveAdditionalFolders(const std::string &name, std::vector< std::string > folders)
Saves additional folder paths for presets, wavetables, or samples.
Definition load_save.cpp:1695
static void getAllFilesOfTypeInDirectories(Array< File > &files, const String &extensions, const std::vector< File > &directories)
Scans a set of directories for files matching certain extensions.
Definition load_save.cpp:1826
static std::vector< std::string > getAdditionalFolders(const std::string &name)
Retrieves a list of additional folder paths for a given category.
Definition load_save.cpp:1705
static void addFavorite(const File &new_favorite)
Adds a new favorite preset or item to the favorites file.
Definition load_save.cpp:1219
static void removeFavorite(const File &old_favorite)
Removes a favorite item from the favorites.
Definition load_save.cpp:1225
static json getAvailablePacks()
Parses and returns JSON data about available packs.
Definition load_save.cpp:1441
static bool hasDataDirectory()
Checks if a data directory is properly configured (exists and has packs.json).
Definition load_save.cpp:1417
static std::set< std::string > getFavorites()
Retrieves all favorites as a set of string paths.
Definition load_save.cpp:1235
static bool setViewPort(Component *component, Rectangle< int > bounds, OpenGlWrapper &open_gl)
Sets the OpenGL viewport to match a specified rectangle within a component.
Definition open_gl_component.cpp:42
static void setScissorBounds(Component *component, Rectangle< int > bounds, OpenGlWrapper &open_gl)
Sets the OpenGL scissor region to a specified rectangle within a component.
Definition open_gl_component.cpp:82
virtual void init(OpenGlWrapper &open_gl) override
Initializes any OpenGL resources for rendering this component.
Definition open_gl_image_component.cpp:57
virtual void destroy(OpenGlWrapper &open_gl) override
Destroys OpenGL-related resources used by this component.
Definition open_gl_image_component.cpp:69
virtual void redrawImage(bool force)
Redraws the image if necessary, creating or updating the internal Image.
Definition open_gl_image_component.cpp:16
void setColor(Colour color)
Sets a color tint for the image.
Definition open_gl_image_component.h:101
OpenGlImage & image()
Provides access to the underlying OpenGlImage.
Definition open_gl_image_component.h:107
A utility class for rendering a single image using OpenGL.
Definition open_gl_image.h:16
void setOwnImage(Image &image)
Sets the image from an owned copy.
Definition open_gl_image.h:69
void init(OpenGlWrapper &open_gl)
Initializes the OpenGL buffers and shader attributes needed for rendering the image.
Definition open_gl_image.cpp:33
void setBottomLeft(float x, float y)
Sets the bottom-left corner position of the image quad.
Definition open_gl_image.h:116
void drawImage(OpenGlWrapper &open_gl)
Draws the image to the current OpenGL context.
Definition open_gl_image.cpp:59
int getImageHeight()
Gets the height of the currently set image.
Definition open_gl_image.h:138
void setBottomRight(float x, float y)
Sets the bottom-right corner position of the image quad.
Definition open_gl_image.h:121
void setTopLeft(float x, float y)
Sets the top-left corner position of the image quad.
Definition open_gl_image.h:111
void destroy(OpenGlWrapper &open_gl)
Releases any OpenGL resources allocated by this object.
Definition open_gl_image.cpp:128
void setTopRight(float x, float y)
Sets the top-right corner position of the image quad.
Definition open_gl_image.h:126
void setColor(Colour color)
Sets the color tint applied to the image.
Definition open_gl_image.h:92
void setThickness(float thickness, bool reset=false)
Sets the thickness used by some shaders and can reset to this thickness.
Definition open_gl_multi_quad.h:338
virtual void init(OpenGlWrapper &open_gl) override
Initializes OpenGL buffers and shader attributes.
Definition open_gl_multi_quad.cpp:37
void setQuad(int i, float x, float y, float w, float h)
Sets the position and size of a quad in normalized device space.
Definition open_gl_multi_quad.h:313
void setTargetComponent(Component *target_component)
Sets a target component to help position the quads.
Definition open_gl_multi_quad.h:358
virtual void render(OpenGlWrapper &open_gl, bool animate) override
Renders the quads using OpenGL.
Definition open_gl_multi_quad.cpp:92
virtual void destroy(OpenGlWrapper &open_gl) override
Releases OpenGL resources when the component is destroyed.
Definition open_gl_multi_quad.cpp:69
force_inline void setColor(Colour color)
Sets the base color for the quads.
Definition open_gl_multi_quad.h:102
void setRounding(float rounding)
Sets the rounding radius of the quads.
Definition open_gl_multi_quad.h:347
void setAdditive(bool additive)
Enables or disables additive blending for rendering.
Definition open_gl_multi_quad.h:377
A convenience class for a single quad rendered via OpenGL.
Definition open_gl_multi_quad.h:447
static Path star()
Definition paths.h:446
static Path folder()
Creates a folder icon path.
Definition paths.h:133
static Path exitX()
Definition paths.h:416
static Path thickX()
Definition paths.h:431
void setShape(Path shape)
Sets the shape to be drawn.
Definition open_gl_image_component.h:441
@ kLight
Definition open_gl_image_component.h:309
void setText(String text)
Sets the displayed text and redraws the image.
Definition open_gl_image_component.h:336
void setTextSize(float size)
Sets the size of the text in points.
Definition open_gl_image_component.h:372
void setJustification(Justification justification)
Sets the text justification (e.g., centered, left, right).
Definition open_gl_image_component.h:389
void setFontType(FontType font_type)
Sets the font type (Title, Light, Regular, Mono).
Definition open_gl_image_component.h:381
void doubleClickedSelected(File selection) override
Called when a file is double-clicked.
Definition popup_browser.cpp:1278
PopupBrowser()
Constructs a PopupBrowser.
Definition popup_browser.cpp:1090
void loadPresets(std::vector< File > directories, const String &extensions, const std::string &passthrough_name, const std::string &additional_folders_name)
Loads presets from a set of directories.
Definition popup_browser.cpp:1330
bool keyStateChanged(bool is_key_down, Component *origin) override
Definition popup_browser.cpp:1302
void visibilityChanged() override
Definition popup_browser.cpp:1220
void filterPresets()
Filters the displayed presets based on the search text.
Definition popup_browser.cpp:1392
~PopupBrowser()
Destructor.
void closingAreaClicked(PopupClosingArea *closing_area, const MouseEvent &e) override
Called when the closing area is clicked.
Definition popup_browser.cpp:1308
void newSelection(File selection) override
Called when a new File is selected.
Definition popup_browser.cpp:1232
void favoritesSelected() override
Called when "Favorites" special selection is made.
Definition popup_browser.cpp:1260
void textEditorEscapeKeyPressed(TextEditor &editor) override
Definition popup_browser.cpp:1401
void allSelected() override
Called when "All" special selection is made.
Definition popup_browser.cpp:1247
bool keyPressed(const KeyPress &key, Component *origin) override
Definition popup_browser.cpp:1283
void textEditorTextChanged(TextEditor &editor) override
Definition popup_browser.cpp:1397
void checkStoreButton()
Checks and updates if the store button should be displayed.
Definition popup_browser.cpp:1320
void buttonClicked(Button *clicked_button) override
Called when a button is clicked. Updates the synth parameter accordingly.
Definition popup_browser.cpp:1405
void checkNoContent()
Checks and updates the UI if there is no content available.
Definition popup_browser.cpp:1313
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition popup_browser.cpp:1154
A transparent area that triggers a closing event when clicked.
Definition popup_browser.h:643
void addListener(Listener *listener)
Adds a Listener to receive closing events.
Definition popup_browser.h:671
void setContent(const std::string &text, Rectangle< int > bounds, BubbleComponent::BubblePlacement placement)
Sets the content of the popup display.
Definition popup_browser.cpp:82
void resized() override
Called when the component is resized.
Definition popup_browser.cpp:65
PopupDisplay()
Constructs a PopupDisplay.
Definition popup_browser.cpp:52
Interface for receiving selection events from PopupList.
Definition popup_browser.h:58
A scrollable, selectable popup list of items.
Definition popup_browser.h:52
void mouseUp(const MouseEvent &e) override
Definition popup_browser.cpp:232
void setSelections(PopupItems selections)
Sets the list of items to be displayed.
Definition popup_browser.cpp:175
static constexpr float kScrollSensitivity
Scroll sensitivity factor.
Definition popup_browser.h:80
int getScrollableRange()
Gets the total scrollable range in pixels.
Definition popup_browser.cpp:382
void mouseExit(const MouseEvent &e) override
Definition popup_browser.cpp:219
PopupList()
Constructs a PopupList.
Definition popup_browser.cpp:143
void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate) override
Renders all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:313
int getRowFromPosition(float mouse_position)
Gets the row index corresponding to a given vertical position.
Definition popup_browser.cpp:186
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition popup_browser.cpp:158
void scrollBarMoved(ScrollBar *scroll_bar, double range_start) override
Definition popup_browser.cpp:368
int getSelection(const MouseEvent &e)
Retrieves the selection index at a mouse event position.
Definition popup_browser.cpp:223
void mouseWheelMove(const MouseEvent &e, const MouseWheelDetails &wheel) override
Definition popup_browser.cpp:359
void mouseDoubleClick(const MouseEvent &e) override
Definition popup_browser.cpp:239
int getTextPadding()
Returns text padding around list items.
Definition popup_browser.h:120
static constexpr float kScrollBarWidth
Width of the scrollbar.
Definition popup_browser.h:81
void select(int select)
Programmatically selects an item by its index.
Definition popup_browser.cpp:248
void destroyOpenGlComponents(OpenGlWrapper &open_gl) override
Destroys all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:346
void setScrollBarRange()
Definition popup_browser.cpp:372
void mouseDrag(const MouseEvent &e) override
Definition popup_browser.cpp:212
Font getFont()
Gets the font used for displaying items.
Definition popup_browser.h:138
int getBrowseWidth()
Calculates the width needed to display all items.
Definition popup_browser.cpp:193
void mouseMove(const MouseEvent &e) override
Definition popup_browser.cpp:205
int getRowHeight()
Returns the row height in current scaling.
Definition popup_browser.h:114
void resetScrollPosition()
Definition popup_browser.cpp:354
void initOpenGlComponents(OpenGlWrapper &open_gl) override
Initializes all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:261
Interface for receiving selection events from SelectionList.
Definition popup_browser.h:238
int getFolderDepth(const File &file)
Gets the depth of a given folder relative to open folders.
Definition popup_browser.cpp:666
int getRowFromPosition(float mouse_position)
Definition popup_browser.cpp:456
void mouseExit(const MouseEvent &e) override
Definition popup_browser.cpp:470
void scrollBarMoved(ScrollBar *scroll_bar, double range_start) override
Definition popup_browser.cpp:642
static File getAllFile()
Returns a File object representing "All" selection.
Definition popup_browser.h:275
void selectPrev()
Selects the previous item in the list.
Definition popup_browser.cpp:758
void resetScrollPosition()
Definition popup_browser.cpp:440
static constexpr float kScrollBarWidth
Scrollbar width.
Definition popup_browser.h:269
void setAdditionalRootsName(const std::string &name)
Sets the name associated with additional roots.
Definition popup_browser.cpp:683
void setScrollBarRange()
Definition popup_browser.cpp:647
void initOpenGlComponents(OpenGlWrapper &open_gl) override
Initializes all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:767
void addAdditionalFolder()
Definition popup_browser.cpp:540
static constexpr float kScrollSensitivity
Scroll sensitivity.
Definition popup_browser.h:268
void selectIcon(const File &selection)
Handles selecting the icon area (favorite toggle).
Definition popup_browser.cpp:605
File getSelection(const MouseEvent &e)
Gets the File at the mouse event position.
Definition popup_browser.cpp:492
void addSubfolderSelections(const File &selection, std::vector< File > &selections)
Adds subfolder selections if a folder is open.
Definition popup_browser.cpp:673
void mouseMove(const MouseEvent &e) override
Definition popup_browser.cpp:460
void removeAdditionalFolder(const File &folder)
Definition popup_browser.cpp:571
void mouseWheelMove(const MouseEvent &e, const MouseWheelDetails &wheel) override
Definition popup_browser.cpp:446
SelectionList()
Constructs a SelectionList.
Definition popup_browser.cpp:388
void filter(const String &filter_string)
Filters the selection list by a given string.
Definition popup_browser.cpp:696
void leftClick(const MouseEvent &e)
Definition popup_browser.cpp:501
void setSelections(Array< File > presets)
Sets the files/folders displayed in this SelectionList.
Definition popup_browser.cpp:434
void mouseDoubleClick(const MouseEvent &e) override
Definition popup_browser.cpp:526
void destroyOpenGlComponents(OpenGlWrapper &open_gl) override
Destroys all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:947
int getScrollableRange()
Gets the total scrollable range.
Definition popup_browser.cpp:743
static File getFavoritesFile()
Returns a File object representing "Favorites" selection.
Definition popup_browser.h:281
int getSelectedIndex()
Gets the index of the currently selected file.
Definition popup_browser.cpp:735
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition popup_browser.cpp:411
void redoCache()
Refreshes the cached row images.
Definition popup_browser.cpp:657
void mouseDown(const MouseEvent &e) override
Definition popup_browser.cpp:519
int getIconPadding()
Gets the icon padding.
Definition popup_browser.h:330
void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate) override
Renders all OpenGL components in this section and sub-sections.
Definition popup_browser.cpp:884
void respondToMenuCallback(int result)
Definition popup_browser.cpp:474
void menuClick(const MouseEvent &e)
Definition popup_browser.cpp:481
void selectNext()
Selects the next item in the list.
Definition popup_browser.cpp:749
void select(const File &selection)
Selects a given file.
Definition popup_browser.cpp:584
static constexpr int kNumCachedRows
Number of rows cached for performance.
Definition popup_browser.h:265
int getRowHeight()
Returns the scaled row height.
Definition popup_browser.h:324
Manages and provides access to vertex and fragment shaders used by the OpenGL rendering pipeline.
Definition shaders.h:19
void setPosition(Point< int > position, Rectangle< int > bounds)
Sets the popup position relative to a given bounding area.
Definition popup_browser.cpp:991
void resized() override
Called when the component is resized. Arranges layout of child components.
Definition popup_browser.cpp:974
SinglePopupSelector()
Constructs a SinglePopupSelector.
Definition popup_browser.cpp:957
@ kBodyRounding
Definition skin.h:71
@ kIconButtonOff
Definition skin.h:185
@ kBorder
Definition skin.h:134
@ kWidgetPrimary1
Definition skin.h:165
@ kIconButtonOffHover
Definition skin.h:186
@ kBodyText
Definition skin.h:133
@ kLightenScreen
Definition skin.h:141
@ kTextEditorSelection
Definition skin.h:203
@ kTextComponentText
Definition skin.h:148
@ kTextEditorCaret
Definition skin.h:202
@ kBody
Definition skin.h:129
@ kPopupBrowser
Definition skin.h:58
Base class for all synthesizer sections, providing UI layout, painting, and interaction logic.
Definition synth_section.h:193
virtual void renderOpenGlComponents(OpenGlWrapper &open_gl, bool animate)
Renders all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:357
float getPadding()
Definition synth_section.cpp:660
virtual void animate(bool animate)
Triggers animation state change in sub-sections if needed.
Definition synth_section.cpp:822
void addSubSection(SynthSection *section, bool show=true)
Adds a subsection (another SynthSection) as a child.
Definition synth_section.cpp:457
void paintOpenGlChildrenBackgrounds(Graphics &g)
Paints the backgrounds for all OpenGL child components.
Definition synth_section.cpp:267
virtual File getCurrentFile()
Gets the currently loaded file. Overridden by subclasses.
Definition synth_section.h:376
virtual void resized() override
Called when the component is resized. Arranges layout of child components.
Definition synth_section.cpp:35
virtual int getPixelMultiple() const
Definition synth_section.cpp:729
virtual void destroyOpenGlComponents(OpenGlWrapper &open_gl)
Destroys all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:383
virtual std::string getFileAuthor()
Gets the author metadata of the currently loaded file. Overridden by subclasses.
Definition synth_section.h:388
void showPopupSelector(Component *source, Point< int > position, const PopupItems &options, std::function< void(int)> callback, std::function< void()> cancel={ })
Shows a popup selector with options.
Definition synth_section.cpp:119
float size_ratio_
Definition synth_section.h:821
void addButton(OpenGlToggleButton *button, bool show=true)
Definition synth_section.cpp:428
float findValue(Skin::ValueId value_id) const
Finds a value in the skin overrides or from the parent if not found locally.
Definition synth_section.cpp:18
virtual void loadFile(const File &file)
Loads a file (e.g., a sample or wavetable). Overridden by subclasses.
Definition synth_section.h:370
void addOpenGlComponent(OpenGlComponent *open_gl_component, bool to_beginning=false)
Definition synth_section.cpp:489
float getSizeRatio() const
Definition synth_section.h:765
void setSkinOverride(Skin::SectionOverride skin_override)
Definition synth_section.h:303
virtual void initOpenGlComponents(OpenGlWrapper &open_gl)
Initializes all OpenGL components in this section and sub-sections.
Definition synth_section.cpp:349
nlohmann::json json
Definition line_generator.h:7
force_inline int nextPowerOfTwo(mono_float value)
Finds the next power of two greater than or equal to a float value.
Definition utils.h:370
A helper struct containing references to OpenGL context, shaders, and display scale.
Definition shaders.h:174
A hierarchical structure of popup menu items for a selector component.
Definition synth_section.h:29
std::vector< PopupItems > items
Nested items for submenus or hierarchical choices.
Definition synth_section.h:33
void addItem(int sub_id, const std::string &sub_name, bool sub_selected=false)
Adds a new item as a submenu entry.
Definition synth_section.h:51
int size() const
Returns the number of nested items.
Definition synth_section.h:65