From f28acdb48ca144613df358ff4c893e078da79962 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 10 May 2021 09:39:36 +0100 Subject: [PATCH] Examples/DemoRunner: Accessibility updates --- examples/Audio/AudioPlaybackDemo.h | 1 + .../DemoRunner/Source/Demos/IntroScreen.h | 9 + .../DemoRunner/Source/UI/MainComponent.cpp | 100 ++++++--- .../DemoRunner/Source/UI/SettingsContent.h | 194 +++++++++++------- examples/GUI/FontsDemo.h | 8 +- examples/GUI/GraphicsDemo.h | 33 +-- examples/GUI/ImagesDemo.h | 1 + examples/GUI/VideoDemo.h | 1 + examples/Utilities/ValueTreesDemo.h | 1 + examples/Utilities/XMLandJSONDemo.h | 1 + 10 files changed, 228 insertions(+), 121 deletions(-) diff --git a/examples/Audio/AudioPlaybackDemo.h b/examples/Audio/AudioPlaybackDemo.h index 8837cea53e..495f2f953d 100644 --- a/examples/Audio/AudioPlaybackDemo.h +++ b/examples/Audio/AudioPlaybackDemo.h @@ -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); diff --git a/examples/DemoRunner/Source/Demos/IntroScreen.h b/examples/DemoRunner/Source/Demos/IntroScreen.h index 270e2c1585..ea752b9fd0 100644 --- a/examples/DemoRunner/Source/Demos/IntroScreen.h +++ b/examples/DemoRunner/Source/Demos/IntroScreen.h @@ -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 createAccessibilityHandler() override + { + return std::make_unique (*this, AccessibilityRole::image); + } + Path logoPath { getJUCELogoPath() }; float elapsed = 0.0f; }; diff --git a/examples/DemoRunner/Source/UI/MainComponent.cpp b/examples/DemoRunner/Source/UI/MainComponent.cpp index 173fc12b24..c0a545c29a 100644 --- a/examples/DemoRunner/Source/UI/MainComponent.cpp +++ b/examples/DemoRunner/Source/UI/MainComponent.cpp @@ -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
(*this)); + : std::make_unique (*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 { diff --git a/examples/DemoRunner/Source/UI/SettingsContent.h b/examples/DemoRunner/Source/UI/SettingsContent.h index c02cf5af4b..08b1a7d3bf 100644 --- a/examples/DemoRunner/Source/UI/SettingsContent.h +++ b/examples/DemoRunner/Source/UI/SettingsContent.h @@ -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 lookAndFeels; - - std::unique_ptr 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 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; }; diff --git a/examples/GUI/FontsDemo.h b/examples/GUI/FontsDemo.h index 60053be609..111530abda 100644 --- a/examples/GUI/FontsDemo.h +++ b/examples/GUI/FontsDemo.h @@ -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 (width, height).expanded (-4, 50).toFloat()); } + String getNameForRow (int rowNumber) override + { + return fonts[rowNumber].getTypefaceName(); + } + void selectedRowsChanged (int /*lastRowselected*/) override { refreshPreviewBoxFont(); diff --git a/examples/GUI/GraphicsDemo.h b/examples/GUI/GraphicsDemo.h index 4ad4468253..a404c785aa 100644 --- a/examples/GUI/GraphicsDemo.h +++ b/examples/GUI/GraphicsDemo.h @@ -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]); } diff --git a/examples/GUI/ImagesDemo.h b/examples/GUI/ImagesDemo.h index 574372ff35..903673ee9e 100644 --- a/examples/GUI/ImagesDemo.h +++ b/examples/GUI/ImagesDemo.h @@ -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); diff --git a/examples/GUI/VideoDemo.h b/examples/GUI/VideoDemo.h index d2e807f87d..3ae280f7d3 100644 --- a/examples/GUI/VideoDemo.h +++ b/examples/GUI/VideoDemo.h @@ -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); diff --git a/examples/Utilities/ValueTreesDemo.h b/examples/Utilities/ValueTreesDemo.h index 00ba020917..f2aae76eec 100644 --- a/examples/Utilities/ValueTreesDemo.h +++ b/examples/Utilities/ValueTreesDemo.h @@ -188,6 +188,7 @@ public: { addAndMakeVisible (tree); + tree.setTitle ("ValueTree"); tree.setDefaultOpenness (true); tree.setMultiSelectEnabled (true); rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager)); diff --git a/examples/Utilities/XMLandJSONDemo.h b/examples/Utilities/XMLandJSONDemo.h index 1d67f54e86..d23efbda1f 100644 --- a/examples/Utilities/XMLandJSONDemo.h +++ b/examples/Utilities/XMLandJSONDemo.h @@ -262,6 +262,7 @@ public: addAndMakeVisible (codeDocumentComponent); codeDocument.addListener (this); + resultsTree.setTitle ("Results"); addAndMakeVisible (resultsTree); resultsTree.setColour (TreeView::backgroundColourId, Colours::white); resultsTree.setDefaultOpenness (true);