@@ -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); | |||