| @@ -296,6 +296,7 @@ public: | |||
| directoryList.setDirectory (File::getSpecialLocation (File::userHomeDirectory), true, true); | |||
| fileTreeComp.setTitle ("Files"); | |||
| fileTreeComp.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f)); | |||
| fileTreeComp.addListener (this); | |||
| @@ -44,6 +44,9 @@ public: | |||
| dontSendNotification); | |||
| linkButton.setColour (HyperlinkButton::textColourId, Colours::lightblue); | |||
| setTitle ("Home"); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| } | |||
| void paint (Graphics& g) override | |||
| @@ -72,6 +75,7 @@ private: | |||
| { | |||
| LogoDrawComponent() | |||
| { | |||
| setTitle ("JUCE Logo"); | |||
| startTimerHz (30); // repaint at 30 fps | |||
| } | |||
| @@ -110,6 +114,11 @@ private: | |||
| elapsed += 0.02f; | |||
| } | |||
| std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | |||
| { | |||
| return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::image); | |||
| } | |||
| Path logoPath { getJUCELogoPath() }; | |||
| float elapsed = 0.0f; | |||
| }; | |||
| @@ -165,39 +165,35 @@ public: | |||
| } | |||
| g.setColour (textColour); | |||
| g.drawFittedText (getNameForRow (rowNumber), bounds, Justification::centred, 1); | |||
| } | |||
| int getNumRows() override | |||
| { | |||
| return (int) (selectedCategory.isEmpty() ? JUCEDemos::getCategories().size() | |||
| : JUCEDemos::getCategory (selectedCategory).demos.size()); | |||
| } | |||
| String getNameForRow (int rowNumber) override | |||
| { | |||
| if (selectedCategory.isEmpty()) | |||
| { | |||
| if (isPositiveAndBelow (rowNumber, JUCEDemos::getCategories().size())) | |||
| g.drawFittedText (JUCEDemos::getCategories()[(size_t) rowNumber].name, | |||
| bounds, Justification::centred, 1); | |||
| return JUCEDemos::getCategories()[(size_t) rowNumber].name; | |||
| } | |||
| else | |||
| { | |||
| auto& category = JUCEDemos::getCategory (selectedCategory); | |||
| if (isPositiveAndBelow (rowNumber, category.demos.size())) | |||
| g.drawFittedText (category.demos[(size_t) rowNumber].demoFile.getFileName(), | |||
| bounds, Justification::centred, 1); | |||
| return category.demos[(size_t) rowNumber].demoFile.getFileName(); | |||
| } | |||
| } | |||
| int getNumRows() override | |||
| { | |||
| return (int) (selectedCategory.isEmpty() ? JUCEDemos::getCategories().size() | |||
| : JUCEDemos::getCategory (selectedCategory).demos.size()); | |||
| return {}; | |||
| } | |||
| void selectedRowsChanged (int row) override | |||
| { | |||
| if (row < 0) | |||
| return; | |||
| if (selectedCategory.isEmpty()) | |||
| showCategory (JUCEDemos::getCategories()[(size_t) row].name); | |||
| else | |||
| demoHolder.setDemo (selectedCategory, row); | |||
| } | |||
| void returnKeyPressed (int row) override { selectRow (row); } | |||
| void listBoxItemClicked (int row, const MouseEvent&) override { selectRow (row); } | |||
| //============================================================================== | |||
| void showCategory (const String& categoryName) noexcept | |||
| @@ -206,37 +202,75 @@ public: | |||
| demos.deselectAllRows(); | |||
| demos.setHeaderComponent (categoryName.isEmpty() ? nullptr | |||
| : std::make_unique<Header> (*this)); | |||
| : std::make_unique<CategoryListHeaderComponent> (*this)); | |||
| demos.updateContent(); | |||
| } | |||
| private: | |||
| String selectedCategory; | |||
| DemoContentComponent& demoHolder; | |||
| ListBox demos; | |||
| struct Header : public Component | |||
| //============================================================================== | |||
| class CategoryListHeaderComponent : public Button | |||
| { | |||
| Header (DemoList& o) | |||
| : owner (o) | |||
| public: | |||
| explicit CategoryListHeaderComponent (DemoList& o) | |||
| : Button ({}), | |||
| owner (o) | |||
| { | |||
| setTitle ("Previous"); | |||
| setSize (0, 30); | |||
| } | |||
| void paint (Graphics& g) override | |||
| void paintButton (Graphics& g, bool, bool) override | |||
| { | |||
| g.setColour (findColour (Label::textColourId)); | |||
| g.drawFittedText ("<", getLocalBounds().reduced (20, 0), Justification::centredLeft, 1); | |||
| } | |||
| void mouseDown (const MouseEvent&) override | |||
| void clicked() override | |||
| { | |||
| owner.showCategory ({}); | |||
| } | |||
| using Button::clicked; | |||
| private: | |||
| DemoList& owner; | |||
| }; | |||
| //============================================================================== | |||
| void selectRow (int row) | |||
| { | |||
| if (row < 0) | |||
| return; | |||
| if (selectedCategory.isEmpty()) | |||
| showCategory (JUCEDemos::getCategories()[(size_t) row].name); | |||
| else | |||
| demoHolder.setDemo (selectedCategory, row); | |||
| selectFirstRow(); | |||
| } | |||
| void selectFirstRow() | |||
| { | |||
| if (auto* handler = demos.getAccessibilityHandler()) | |||
| { | |||
| for (auto* child : handler->getChildren()) | |||
| { | |||
| if (child->getRole() == AccessibilityRole::listItem) | |||
| { | |||
| child->grabFocus(); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| String selectedCategory; | |||
| DemoContentComponent& demoHolder; | |||
| ListBox demos; | |||
| }; | |||
| //============================================================================== | |||
| @@ -266,6 +300,9 @@ MainComponent::MainComponent() | |||
| addAndMakeVisible (showDemosButton); | |||
| addAndMakeVisible (demosPanel); | |||
| demosPanel.setTitle ("Demos"); | |||
| demosPanel.setFocusContainerType (FocusContainerType::focusContainer); | |||
| showDemosButton.onClick = [this] { demosPanel.showOrHide (true); }; | |||
| demosPanel.onPanelMove = [this] | |||
| @@ -284,6 +321,9 @@ MainComponent::MainComponent() | |||
| if (isShowingHeavyweightDemo) | |||
| resized(); | |||
| if (auto* handler = demosPanel.getAccessibilityHandler()) | |||
| handler->grabFocus(); | |||
| } | |||
| else | |||
| { | |||
| @@ -37,6 +37,9 @@ public: | |||
| settingsViewport.setViewedComponent (&innerContent, false); | |||
| addAndMakeVisible (settingsViewport); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| setTitle ("DemoRunner Settings"); | |||
| setOpaque (true); | |||
| } | |||
| @@ -47,39 +50,43 @@ public: | |||
| void resized() override | |||
| { | |||
| constexpr int minimumWidth = 350; | |||
| constexpr int minimumHeight = 550; | |||
| auto r = getLocalBounds(); | |||
| auto scrollBarWidth = getLookAndFeel().getDefaultScrollbarWidth(); | |||
| const auto scrollBarWidth = getLookAndFeel().getDefaultScrollbarWidth(); | |||
| innerContent.setSize (jmax (r.getWidth() - scrollBarWidth, innerContent.getMinimumWidth()), | |||
| jmax (r.getHeight(), innerContent.getMinimumHeight())); | |||
| innerContent.setSize (jmax (r.getWidth() - scrollBarWidth, minimumWidth), | |||
| jmax (r.getHeight(), minimumHeight)); | |||
| settingsViewport.setBounds (r); | |||
| } | |||
| private: | |||
| Viewport settingsViewport; | |||
| static constexpr float titleLabelFontHeight = 18.0f; | |||
| static constexpr int itemHeight = 30; | |||
| static constexpr int itemSpacing = 7; | |||
| //============================================================================== | |||
| class InnerContent : public Component, | |||
| public ComponentMovementWatcher | |||
| class GraphicsSettingsGroup : public Component, | |||
| private ComponentMovementWatcher | |||
| { | |||
| public: | |||
| InnerContent (MainComponent& topLevelComponent) | |||
| : ComponentMovementWatcher (this), mainComponent (topLevelComponent) | |||
| GraphicsSettingsGroup (MainComponent& comp) | |||
| : ComponentMovementWatcher (&comp), | |||
| mainComponent (comp) | |||
| { | |||
| addAndMakeVisible (graphicsTitleLabel); | |||
| graphicsTitleLabel.setFont (18.0f); | |||
| addAndMakeVisible (audioTitleLabel); | |||
| audioTitleLabel.setFont (18.0f); | |||
| addAndMakeVisible (titleLabel); | |||
| titleLabel.setFont (titleLabelFontHeight); | |||
| addLookAndFeels(); | |||
| addAndMakeVisible (lookAndFeelSelector); | |||
| for (int i = 0; i < lookAndFeelNames.size(); ++i) | |||
| lookAndFeelSelector.addItem (lookAndFeelNames.getReference (i), i + 1); | |||
| lookAndFeelSelector.setSelectedItemIndex (lookAndFeelNames.indexOf ("LookAndFeel_V4 (Dark)")); | |||
| lookAndFeelSelector.onChange = [this] | |||
| { | |||
| auto* lf = lookAndFeels.getUnchecked (lookAndFeelSelector.getSelectedItemIndex()); | |||
| @@ -97,87 +104,37 @@ private: | |||
| rendererLabel.setJustificationType (Justification::centredRight); | |||
| rendererLabel.attachToComponent (&rendererSelector, true); | |||
| audioSettings.reset (new AudioDeviceSelectorComponent (getSharedAudioDeviceManager(), | |||
| 0, 256, 0, 256, true, true, true, false)); | |||
| addAndMakeVisible (audioSettings.get()); | |||
| audioSettings->setItemHeight (itemHeight); | |||
| setOpaque (true); | |||
| } | |||
| void paint (Graphics& g) override | |||
| { | |||
| g.fillAll (findColour (ResizableWindow::backgroundColourId).contrasting (0.2f)); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| setTitle ("Graphics Settings"); | |||
| } | |||
| void resized() override | |||
| { | |||
| auto bounds = getLocalBounds(); | |||
| auto space = itemHeight / 4; | |||
| graphicsTitleLabel.setBounds (bounds.removeFromTop (30)); | |||
| bounds.removeFromTop (space); | |||
| auto xPos = (float) bounds.getX() + ((float) bounds.getWidth() * 0.35f); | |||
| auto width = (float) bounds.getWidth() * 0.6f; | |||
| titleLabel.setBounds (bounds.removeFromTop (itemHeight)); | |||
| bounds.removeFromTop (itemSpacing); | |||
| lookAndFeelSelector.setBounds (bounds.removeFromTop (itemHeight).withWidth ((int) width).withX ((int) xPos)); | |||
| const auto xPos = roundToInt ((float) bounds.getX() + ((float) bounds.getWidth() * 0.35f)); | |||
| const auto width = roundToInt ((float) bounds.getWidth() * 0.6f); | |||
| bounds.removeFromTop (space); | |||
| lookAndFeelSelector.setBounds (bounds.removeFromTop (itemHeight).withWidth (width).withX (xPos)); | |||
| bounds.removeFromTop (itemSpacing); | |||
| rendererSelector.setBounds (bounds.removeFromTop (itemHeight).withWidth ((int) width).withX ((int) xPos)); | |||
| bounds.removeFromTop (space); | |||
| audioTitleLabel.setBounds (bounds.removeFromTop (30)); | |||
| bounds.removeFromTop (space); | |||
| audioSettings->setBounds (bounds); | |||
| rendererSelector.setBounds (bounds.removeFromTop (itemHeight).withWidth (width).withX (xPos)); | |||
| } | |||
| //============================================================================== | |||
| int getMinimumHeight() const noexcept { return 550; } | |||
| int getMinimumWidth() const noexcept { return 350; } | |||
| private: | |||
| MainComponent& mainComponent; | |||
| ComponentPeer* peer = nullptr; | |||
| const int itemHeight = 30; | |||
| Label graphicsTitleLabel { {}, "Graphics" }, | |||
| audioTitleLabel { {}, "Audio" }, | |||
| lookAndFeelLabel { {}, "LookAndFeel:" }, | |||
| rendererLabel { {}, "Renderer:" }; | |||
| ComboBox lookAndFeelSelector, rendererSelector; | |||
| StringArray lookAndFeelNames; | |||
| OwnedArray<LookAndFeel> lookAndFeels; | |||
| std::unique_ptr<AudioDeviceSelectorComponent> audioSettings; | |||
| //============================================================================== | |||
| void refreshRenderingEngineSelector() | |||
| { | |||
| StringArray renderingEngines (mainComponent.getRenderingEngines()); | |||
| rendererSelector.clear (NotificationType::dontSendNotification); | |||
| for (int i = 0; i < renderingEngines.size(); ++i) | |||
| rendererSelector.addItem (renderingEngines.getReference (i), i + 1); | |||
| rendererSelector.setSelectedItemIndex (mainComponent.getCurrentRenderingEngine()); | |||
| } | |||
| //============================================================================== | |||
| void componentMovedOrResized (bool, bool) override {} | |||
| using ComponentListener::componentMovedOrResized; | |||
| void componentVisibilityChanged() override {} | |||
| using ComponentListener::componentVisibilityChanged; | |||
| void componentPeerChanged() override | |||
| { | |||
| auto* newPeer = getPeer(); | |||
| auto* newPeer = mainComponent.getPeer(); | |||
| if (peer != newPeer) | |||
| { | |||
| peer = newPeer; | |||
| @@ -187,7 +144,14 @@ private: | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void refreshRenderingEngineSelector() | |||
| { | |||
| rendererSelector.clear (NotificationType::dontSendNotification); | |||
| rendererSelector.addItemList (mainComponent.getRenderingEngines(), 1); | |||
| rendererSelector.setSelectedItemIndex (mainComponent.getCurrentRenderingEngine()); | |||
| } | |||
| void addLookAndFeels() | |||
| { | |||
| lookAndFeelNames.addArray ({ "LookAndFeel_V1", "LookAndFeel_V2", "LookAndFeel_V3", | |||
| @@ -202,7 +166,81 @@ private: | |||
| lookAndFeels.add (new LookAndFeel_V4 (LookAndFeel_V4::getGreyColourScheme())); | |||
| lookAndFeels.add (new LookAndFeel_V4 (LookAndFeel_V4::getLightColourScheme())); | |||
| } | |||
| MainComponent& mainComponent; | |||
| ComponentPeer* peer = nullptr; | |||
| Label titleLabel { {}, "Graphics" }, | |||
| lookAndFeelLabel { {}, "LookAndFeel:" }, | |||
| rendererLabel { {}, "Renderer:" }; | |||
| ComboBox lookAndFeelSelector, rendererSelector; | |||
| StringArray lookAndFeelNames; | |||
| OwnedArray<LookAndFeel> lookAndFeels; | |||
| }; | |||
| class AudioSettingsGroup : public Component | |||
| { | |||
| public: | |||
| AudioSettingsGroup() | |||
| : deviceSelectorComp (getSharedAudioDeviceManager(), 0, 256, 0, 256, true, true, true, false) | |||
| { | |||
| addAndMakeVisible (titleLabel); | |||
| titleLabel.setFont (titleLabelFontHeight); | |||
| addAndMakeVisible (deviceSelectorComp); | |||
| deviceSelectorComp.setItemHeight (itemHeight); | |||
| setFocusContainerType (FocusContainerType::focusContainer); | |||
| setTitle ("Audio Settings"); | |||
| } | |||
| void resized() override | |||
| { | |||
| auto bounds = getLocalBounds(); | |||
| titleLabel.setBounds (bounds.removeFromTop (itemHeight)); | |||
| bounds.removeFromTop (itemSpacing); | |||
| deviceSelectorComp.setBounds (bounds); | |||
| } | |||
| private: | |||
| Label titleLabel { {}, "Audio" }; | |||
| AudioDeviceSelectorComponent deviceSelectorComp; | |||
| }; | |||
| //============================================================================== | |||
| class InnerContent : public Component | |||
| { | |||
| public: | |||
| InnerContent (MainComponent& mainComponent) | |||
| : graphicsSettings (mainComponent) | |||
| { | |||
| addAndMakeVisible (graphicsSettings); | |||
| addAndMakeVisible (audioSettings); | |||
| setOpaque (true); | |||
| } | |||
| void paint (Graphics& g) override | |||
| { | |||
| g.fillAll (findColour (ResizableWindow::backgroundColourId).contrasting (0.2f)); | |||
| } | |||
| void resized() override | |||
| { | |||
| auto bounds = getLocalBounds(); | |||
| graphicsSettings.setBounds (bounds.removeFromTop (150)); | |||
| audioSettings.setBounds (bounds); | |||
| } | |||
| private: | |||
| GraphicsSettingsGroup graphicsSettings; | |||
| AudioSettingsGroup audioSettings; | |||
| }; | |||
| Viewport settingsViewport; | |||
| InnerContent innerContent; | |||
| }; | |||
| @@ -94,6 +94,7 @@ public: | |||
| Font::findFonts (fonts); // Generate the list of fonts | |||
| listBox.setTitle ("Fonts"); | |||
| listBox.setRowHeight (20); | |||
| listBox.setModel (this); // Tell the listbox where to get its data model | |||
| listBox.setColour (ListBox::textColourId, Colours::black); | |||
| @@ -218,12 +219,17 @@ public: | |||
| AttributedString s; | |||
| s.setWordWrap (AttributedString::none); | |||
| s.setJustification (Justification::centredLeft); | |||
| s.append (font.getTypefaceName(), font.withHeight ((float) height * 0.7f), Colours::black); | |||
| s.append (getNameForRow (rowNumber), font.withHeight ((float) height * 0.7f), Colours::black); | |||
| s.append (" " + font.getTypefaceName(), Font ((float) height * 0.5f, Font::italic), Colours::grey); | |||
| s.draw (g, Rectangle<int> (width, height).expanded (-4, 50).toFloat()); | |||
| } | |||
| String getNameForRow (int rowNumber) override | |||
| { | |||
| return fonts[rowNumber].getTypefaceName(); | |||
| } | |||
| void selectedRowsChanged (int /*lastRowselected*/) override | |||
| { | |||
| refreshPreviewBoxFont(); | |||
| @@ -644,35 +644,44 @@ public: | |||
| demos.add (new LinesDemo (controls)); | |||
| addAndMakeVisible (listBox); | |||
| listBox.setTitle ("Test List"); | |||
| listBox.setModel (this); | |||
| listBox.selectRow (0); | |||
| } | |||
| void resized() | |||
| void resized() override | |||
| { | |||
| listBox.setBounds (getLocalBounds()); | |||
| } | |||
| int getNumRows() | |||
| int getNumRows() override | |||
| { | |||
| return demos.size(); | |||
| } | |||
| void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) | |||
| void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override | |||
| { | |||
| if (demos[rowNumber] == nullptr) | |||
| return; | |||
| if (rowIsSelected) | |||
| g.fillAll (Colour::contrasting (findColour (ListBox::textColourId), | |||
| findColour (ListBox::backgroundColourId))); | |||
| g.setColour (findColour (ListBox::textColourId)); | |||
| g.setFont (14.0f); | |||
| g.drawFittedText (getNameForRow (rowNumber), 8, 0, width - 10, height, Justification::centredLeft, 2); | |||
| } | |||
| String getNameForRow (int rowNumber) override | |||
| { | |||
| if (auto* demo = demos[rowNumber]) | |||
| { | |||
| if (rowIsSelected) | |||
| g.fillAll (Colour::contrasting (findColour (ListBox::textColourId), | |||
| findColour (ListBox::backgroundColourId))); | |||
| return demo->getName(); | |||
| g.setColour (findColour (ListBox::textColourId)); | |||
| g.setFont (14.0f); | |||
| g.drawFittedText (demo->getName(), 8, 0, width - 10, height, Justification::centredLeft, 2); | |||
| } | |||
| return {}; | |||
| } | |||
| void selectedRowsChanged (int lastRowSelected) | |||
| void selectedRowsChanged (int lastRowSelected) override | |||
| { | |||
| demoHolder.setDemo (demos [lastRowSelected]); | |||
| } | |||
| @@ -59,6 +59,7 @@ public: | |||
| imageList.setDirectory (File::getSpecialLocation (File::userPicturesDirectory), true, true); | |||
| directoryThread.startThread (1); | |||
| fileTree.setTitle ("Files"); | |||
| fileTree.addListener (this); | |||
| fileTree.setColour (TreeView::backgroundColourId, Colours::grey); | |||
| addAndMakeVisible (fileTree); | |||
| @@ -156,6 +156,7 @@ public: | |||
| movieList.setDirectory (File::getSpecialLocation (File::userMoviesDirectory), true, true); | |||
| directoryThread.startThread (1); | |||
| fileTree.setTitle ("Files"); | |||
| fileTree.addListener (this); | |||
| fileTree.setColour (FileTreeComponent::backgroundColourId, Colours::lightgrey.withAlpha (0.6f)); | |||
| addAndMakeVisible (fileTree); | |||
| @@ -188,6 +188,7 @@ public: | |||
| { | |||
| addAndMakeVisible (tree); | |||
| tree.setTitle ("ValueTree"); | |||
| tree.setDefaultOpenness (true); | |||
| tree.setMultiSelectEnabled (true); | |||
| rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager)); | |||
| @@ -262,6 +262,7 @@ public: | |||
| addAndMakeVisible (codeDocumentComponent); | |||
| codeDocument.addListener (this); | |||
| resultsTree.setTitle ("Results"); | |||
| addAndMakeVisible (resultsTree); | |||
| resultsTree.setColour (TreeView::backgroundColourId, Colours::white); | |||
| resultsTree.setDefaultOpenness (true); | |||