From 80a771828658d4ea4895c8b173016bd0f8adcb9e Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 3 Jan 2018 16:34:11 +0000 Subject: [PATCH] Added some event lambda callbacks for TextEditor and simplified a bit more code using them --- .../Demo/Source/Demos/CryptographyDemo.cpp | 32 ++++++++--------- examples/Demo/Source/Demos/FlexBoxDemo.cpp | 35 ++++++++----------- examples/Demo/Source/Demos/MDIDemo.cpp | 21 ++--------- examples/Demo/Source/Demos/NetworkingDemo.cpp | 8 +---- examples/Demo/Source/Demos/WebBrowserDemo.cpp | 20 +++-------- .../gui/juce_AudioDeviceSelectorComponent.cpp | 30 +++++++--------- .../filebrowser/juce_FileBrowserComponent.cpp | 22 ++++++------ .../filebrowser/juce_FileBrowserComponent.h | 4 +-- .../juce_gui_basics/widgets/juce_ComboBox.h | 5 ++- .../widgets/juce_TextEditor.cpp | 18 +++++++++- .../juce_gui_basics/widgets/juce_TextEditor.h | 13 +++++++ 11 files changed, 93 insertions(+), 115 deletions(-) diff --git a/examples/Demo/Source/Demos/CryptographyDemo.cpp b/examples/Demo/Source/Demos/CryptographyDemo.cpp index 254435020b..5cdb4db13b 100644 --- a/examples/Demo/Source/Demos/CryptographyDemo.cpp +++ b/examples/Demo/Source/Demos/CryptographyDemo.cpp @@ -57,7 +57,7 @@ public: area.removeFromTop (10); area.reduce (5, 5); - Rectangle topArea (area.removeFromTop (34)); + auto topArea = area.removeFromTop (34); topArea.removeFromLeft (110); bitSize.setBounds (topArea.removeFromLeft (topArea.getWidth() / 2).reduced (5)); generateRSAButton.setBounds (topArea.reduced (5)); @@ -123,8 +123,7 @@ private: }; //============================================================================== -class HashesComponent : public Component, - private TextEditor::Listener +class HashesComponent : public Component { public: HashesComponent() @@ -137,7 +136,17 @@ public: hashEntryBox.setReturnKeyStartsNewLine (true); hashEntryBox.setText ("Type some text in this box and the resulting MD5, SHA and Whirlpool hashes will update below"); - hashEntryBox.addListener (this); + + auto updateHashes = [this]() + { + auto text = hashEntryBox.getText(); + updateMD5Result (text.toUTF8()); + updateSHA256Result (text.toUTF8()); + updateWhirlpoolResult (text.toUTF8()); + }; + + hashEntryBox.onTextChange = updateHashes; + hashEntryBox.onReturnKey = updateHashes; hashLabel1.setText ("Text to Hash:", dontSendNotification); hashLabel2.setText ("MD5 Result:", dontSendNotification); @@ -156,14 +165,6 @@ public: updateHashes(); } - void updateHashes() - { - String text = hashEntryBox.getText(); - updateMD5Result (text.toUTF8()); - updateSHA256Result (text.toUTF8()); - updateWhirlpoolResult (text.toUTF8()); - } - void updateMD5Result (CharPointer_UTF8 text) { md5Result.setText (MD5 (text).toHexString(), dontSendNotification); @@ -181,7 +182,7 @@ public: void resized() override { - Rectangle area (getLocalBounds()); + auto area = getLocalBounds(); hashGroup.setBounds (area); area.removeFromLeft (120); area.removeFromTop (10); @@ -198,11 +199,6 @@ private: Label md5Result, shaResult, whirlpoolResult; Label hashLabel1, hashLabel2, hashLabel3, hashLabel4; - void textEditorTextChanged (TextEditor&) override { updateHashes(); } - void textEditorReturnKeyPressed (TextEditor&) override { updateHashes(); } - void textEditorEscapeKeyPressed (TextEditor&) override { updateHashes(); } - void textEditorFocusLost (TextEditor&) override { updateHashes(); } - void lookAndFeelChanged() override { hashGroup.setColour (GroupComponent::outlineColourId, diff --git a/examples/Demo/Source/Demos/FlexBoxDemo.cpp b/examples/Demo/Source/Demos/FlexBoxDemo.cpp index 4ac6a76741..65f1709266 100644 --- a/examples/Demo/Source/Demos/FlexBoxDemo.cpp +++ b/examples/Demo/Source/Demos/FlexBoxDemo.cpp @@ -29,27 +29,26 @@ // these classes are C++11-only #if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS -struct DemoFlexPanel : public juce::Component, - private juce::TextEditor::Listener +struct DemoFlexPanel : public juce::Component { DemoFlexPanel (juce::Colour col, FlexItem& item) : flexItem (item), colour (col) { int x = 70; int y = 3; - setupTextEditor (flexOrderEditor, { x, y, 20, 18 }, "0"); + setupTextEditor (flexOrderEditor, { x, y, 20, 18 }, "0", [this]() { flexItem.order = (int) flexOrderEditor.getText().getFloatValue(); }); addLabel ("order", flexOrderEditor); y += 20; - setupTextEditor (flexGrowEditor, { x, y, 20, 18 }, "0"); + setupTextEditor (flexGrowEditor, { x, y, 20, 18 }, "0", [this]() { flexItem.flexGrow = flexGrowEditor.getText().getFloatValue(); }); addLabel ("flex-grow", flexGrowEditor); y += 20; - setupTextEditor (flexShrinkEditor, { x, y, 20, 18 }, "1"); + setupTextEditor (flexShrinkEditor, { x, y, 20, 18 }, "1", [this]() { flexItem.flexShrink = flexShrinkEditor.getText().getFloatValue(); }); addLabel ("flex-shrink", flexShrinkEditor); y += 20; - setupTextEditor (flexBasisEditor, { x, y, 33, 18 }, "100"); + setupTextEditor (flexBasisEditor, { x, y, 33, 18 }, "100", [this]() { flexItem.flexBasis = flexBasisEditor.getText().getFloatValue(); }); addLabel ("flex-basis", flexBasisEditor); y += 20; @@ -67,11 +66,18 @@ struct DemoFlexPanel : public juce::Component, addLabel ("align-self", alignSelfCombo); } - void setupTextEditor (TextEditor& te, Rectangle b, StringRef initialText) + void setupTextEditor (TextEditor& te, Rectangle b, StringRef initialText, std::function updateFn) { te.setBounds (b); te.setText (initialText); - te.addListener (this); + + te.onTextChange = [this, updateFn]() + { + updateFn(); + + if (auto parent = getParentComponent()) + parent->resized(); + }; addAndMakeVisible (te); } @@ -99,19 +105,6 @@ struct DemoFlexPanel : public juce::Component, parent->resized(); } - void textEditorTextChanged (TextEditor& textEditor) override - { - auto textIntValue = textEditor.getText().getFloatValue(); - - if (&textEditor == &flexOrderEditor) flexItem.order = (int) textIntValue; - if (&textEditor == &flexGrowEditor) flexItem.flexGrow = textIntValue; - if (&textEditor == &flexBasisEditor) flexItem.flexBasis = textIntValue; - if (&textEditor == &flexShrinkEditor) flexItem.flexShrink = textIntValue; - - if (auto parent = getParentComponent()) - parent->resized(); - } - void paint (Graphics& g) override { auto r = getLocalBounds(); diff --git a/examples/Demo/Source/Demos/MDIDemo.cpp b/examples/Demo/Source/Demos/MDIDemo.cpp index 56f171452c..53739694d0 100644 --- a/examples/Demo/Source/Demos/MDIDemo.cpp +++ b/examples/Demo/Source/Demos/MDIDemo.cpp @@ -32,8 +32,7 @@ flag is used to promt the user to save the note when it is closed. */ class Note : public Component, - public FileBasedDocument, - private TextEditor::Listener + public FileBasedDocument { public: Note (const String& name, const String& contents) @@ -50,12 +49,7 @@ public: editor.setReturnKeyStartsNewLine (true); editor.getTextValue().referTo (textValueObject); addAndMakeVisible (editor); - editor.addListener (this); - } - - ~Note() - { - editor.removeListener (this); + editor.onTextChange = [this]() { changed(); }; } void resized() override @@ -107,22 +101,11 @@ private: Value textValueObject; TextEditor editor; - void textEditorTextChanged (TextEditor& ed) override - { - // let our FileBasedDocument know we've changed - if (&ed == &editor) - changed(); - } - void lookAndFeelChanged() override { editor.applyFontToAllText (editor.getFont()); } - void textEditorReturnKeyPressed (TextEditor&) override {} - void textEditorEscapeKeyPressed (TextEditor&) override {} - void textEditorFocusLost (TextEditor&) override {} - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Note) }; diff --git a/examples/Demo/Source/Demos/NetworkingDemo.cpp b/examples/Demo/Source/Demos/NetworkingDemo.cpp index 3b276f6a4d..2242fb0da1 100644 --- a/examples/Demo/Source/Demos/NetworkingDemo.cpp +++ b/examples/Demo/Source/Demos/NetworkingDemo.cpp @@ -29,7 +29,6 @@ //============================================================================== class NetworkingDemo : public Component, - private TextEditor::Listener, private Thread { public: @@ -41,7 +40,7 @@ public: addAndMakeVisible (urlBox); urlBox.setText ("https://www.google.com"); - urlBox.addListener (this); + urlBox.onReturnKey = [this]() { fetchButton.triggerClick(); }; addAndMakeVisible (fetchButton); fetchButton.setButtonText ("Download URL Contents"); @@ -106,11 +105,6 @@ private: CodeDocument resultsDocument; CodeEditorComponent resultsBox; - void textEditorReturnKeyPressed (TextEditor&) override - { - fetchButton.triggerClick(); - } - void lookAndFeelChanged() override { urlBox.applyFontToAllText (urlBox.getFont()); diff --git a/examples/Demo/Source/Demos/WebBrowserDemo.cpp b/examples/Demo/Source/Demos/WebBrowserDemo.cpp index 9df67fa887..59a1a999c7 100644 --- a/examples/Demo/Source/Demos/WebBrowserDemo.cpp +++ b/examples/Demo/Source/Demos/WebBrowserDemo.cpp @@ -68,21 +68,17 @@ private: //============================================================================== -class WebBrowserDemo : public Component, - private TextEditor::Listener +class WebBrowserDemo : public Component { public: WebBrowserDemo() - : goButton ("Go", "Go to URL"), - backButton ("<<", "Back"), - forwardButton (">>", "Forward") { setOpaque (true); // Create an address box.. addAndMakeVisible (addressTextBox); addressTextBox.setTextToShowWhenEmpty ("Enter a web address, e.g. https://www.juce.com", Colours::grey); - addressTextBox.addListener (this); + addressTextBox.onReturnKey = [this]() { webView->goToURL (addressTextBox.getText()); }; // create the actual browser component addAndMakeVisible (webView = new DemoBrowserComponent (addressTextBox)); @@ -118,16 +114,10 @@ private: ScopedPointer webView; TextEditor addressTextBox; - TextButton goButton, backButton, forwardButton; - void textEditorTextChanged (TextEditor&) override {} - void textEditorEscapeKeyPressed (TextEditor&) override {} - void textEditorFocusLost (TextEditor&) override {} - - void textEditorReturnKeyPressed (TextEditor&) override - { - webView->goToURL (addressTextBox.getText()); - } + TextButton goButton { "Go", "Go to URL" }, + backButton { "<<", "Back" }, + forwardButton { ">>", "Forward" }; void lookAndFeelChanged() override { diff --git a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index 56bebcf21d..223ff14e3a 100644 --- a/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -198,8 +198,7 @@ static String getNoDeviceString() { return "<< " + TRANS("none") + " >>"; } //============================================================================== class AudioDeviceSettingsPanel : public Component, - private ChangeListener, - private ComboBox::Listener + private ChangeListener { public: AudioDeviceSettingsPanel (AudioIODeviceType& t, AudioDeviceSetupDetails& setupDetails, @@ -328,17 +327,13 @@ public: } } - void comboBoxChanged (ComboBox* comboBoxThatHasChanged) override + void updateConfig (bool updateOutputDevice, bool updateInputDevice, bool updateSampleRate, bool updateBufferSize) { - if (comboBoxThatHasChanged == nullptr) - return; - AudioDeviceManager::AudioDeviceSetup config; setup.manager->getAudioDeviceSetup (config); String error; - if (comboBoxThatHasChanged == outputDeviceDropDown - || comboBoxThatHasChanged == inputDeviceDropDown) + if (updateOutputDevice || updateInputDevice) { if (outputDeviceDropDown != nullptr) config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String() @@ -351,7 +346,7 @@ public: if (! type.hasSeparateInputsAndOutputs()) config.inputDeviceName = config.outputDeviceName; - if (comboBoxThatHasChanged == inputDeviceDropDown) + if (updateInputDevice) config.useDefaultInputChannels = true; else config.useDefaultOutputChannels = true; @@ -364,7 +359,7 @@ public: updateControlPanelButton(); resized(); } - else if (comboBoxThatHasChanged == sampleRateDropDown) + else if (updateSampleRate) { if (sampleRateDropDown->getSelectedId() > 0) { @@ -372,7 +367,7 @@ public: error = setup.manager->setAudioDeviceSetup (config, true); } } - else if (comboBoxThatHasChanged == bufferSizeDropDown) + else if (updateBufferSize) { if (bufferSizeDropDown->getSelectedId() > 0) { @@ -606,7 +601,8 @@ private: if (outputDeviceDropDown == nullptr) { outputDeviceDropDown = new ComboBox(); - outputDeviceDropDown->addListener (this); + outputDeviceDropDown->onChange = [this]() { updateConfig (true, false, false, false); }; + addAndMakeVisible (outputDeviceDropDown); outputDeviceLabel = new Label ({}, type.hasSeparateInputsAndOutputs() ? TRANS("Output:") @@ -634,7 +630,7 @@ private: if (inputDeviceDropDown == nullptr) { inputDeviceDropDown = new ComboBox(); - inputDeviceDropDown->addListener (this); + inputDeviceDropDown->onChange = [this]() { updateConfig (false, true, false, false); }; addAndMakeVisible (inputDeviceDropDown); inputDeviceLabel = new Label ({}, TRANS("Input:")); @@ -662,7 +658,7 @@ private: else { sampleRateDropDown->clear(); - sampleRateDropDown->removeListener (this); + sampleRateDropDown->onChange = {}; } for (auto rate : currentDevice->getAvailableSampleRates()) @@ -672,7 +668,7 @@ private: } sampleRateDropDown->setSelectedId (roundToInt (currentDevice->getCurrentSampleRate()), dontSendNotification); - sampleRateDropDown->addListener (this); + sampleRateDropDown->onChange = [this]() { updateConfig (false, false, true, false); }; } void updateBufferSizeComboBox (AudioIODevice* currentDevice) @@ -687,7 +683,7 @@ private: else { bufferSizeDropDown->clear(); - bufferSizeDropDown->removeListener (this); + bufferSizeDropDown->onChange = {}; } auto currentRate = currentDevice->getCurrentSampleRate(); @@ -699,7 +695,7 @@ private: bufferSizeDropDown->addItem (String (bs) + " samples (" + String (bs * 1000.0 / currentRate, 1) + " ms)", bs); bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), dontSendNotification); - bufferSizeDropDown->addListener (this); + bufferSizeDropDown->onChange = [this]() { updateConfig (false, false, false, true); }; } public: diff --git a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index 59db0a1ef2..0e01ddc71d 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -31,7 +31,7 @@ FileBrowserComponent::FileBrowserComponent (int flags_, const File& initialFileOrDirectory, const FileFilter* fileFilter_, FilePreviewComponent* previewComp_) - : FileFilter (String()), + : FileFilter ({}), fileFilter (fileFilter_), flags (flags_), previewComp (previewComp_), @@ -93,7 +93,7 @@ FileBrowserComponent::FileBrowserComponent (int flags_, addAndMakeVisible (currentPathBox); currentPathBox.setEditableText (true); resetRecentPaths(); - currentPathBox.addListener (this); + currentPathBox.onChange = [this]() { updateSelectedPath(); }; addAndMakeVisible (filenameBox); filenameBox.setMultiLine (false); @@ -410,7 +410,7 @@ void FileBrowserComponent::fileDoubleClicked (const File& f) setRoot (f); if ((flags & canSelectDirectories) != 0 && (flags & doNotClearFileNameOnRootChange) == 0) - filenameBox.setText (String()); + filenameBox.setText ({}); } else { @@ -455,7 +455,7 @@ void FileBrowserComponent::textEditorReturnKeyPressed (TextEditor&) chosenFiles.clear(); if ((flags & doNotClearFileNameOnRootChange) == 0) - filenameBox.setText (String()); + filenameBox.setText ({}); } else { @@ -482,7 +482,7 @@ void FileBrowserComponent::textEditorFocusLost (TextEditor&) } //============================================================================== -void FileBrowserComponent::comboBoxChanged (ComboBox*) +void FileBrowserComponent::updateSelectedPath() { auto newText = currentPathBox.getText().trim().unquoted(); @@ -493,9 +493,9 @@ void FileBrowserComponent::comboBoxChanged (ComboBox*) StringArray rootNames, rootPaths; getRoots (rootNames, rootPaths); - if (rootPaths [index].isNotEmpty()) + if (rootPaths[index].isNotEmpty()) { - setRoot (File (rootPaths [index])); + setRoot (File (rootPaths[index])); } else { @@ -549,8 +549,8 @@ void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootNames.add (name); } - rootPaths.add (String()); - rootNames.add (String()); + rootPaths.add ({}); + rootNames.add ({}); rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); rootNames.add (TRANS("Documents")); @@ -573,8 +573,8 @@ void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); rootNames.add (TRANS("Desktop")); - rootPaths.add (String()); - rootNames.add (String()); + rootPaths.add ({}); + rootNames.add ({}); Array volumes; File vol ("/Volumes"); diff --git a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h index f0c58f4c93..83f64c574b 100644 --- a/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h +++ b/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h @@ -40,7 +40,6 @@ namespace juce class JUCE_API FileBrowserComponent : public Component, private FileBrowserListener, private TextEditor::Listener, - private ComboBox::Listener, private FileFilter, private Timer { @@ -236,8 +235,6 @@ public: /** @internal */ void lookAndFeelChanged() override; /** @internal */ - void comboBoxChanged (ComboBox*) override; - /** @internal */ void textEditorTextChanged (TextEditor&) override; /** @internal */ void textEditorReturnKeyPressed (TextEditor&) override; @@ -297,6 +294,7 @@ private: void timerCallback() override; void sendListenerChangeMessage(); bool isFileOrDirSuitable (const File&) const; + void updateSelectedPath(); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent) }; diff --git a/modules/juce_gui_basics/widgets/juce_ComboBox.h b/modules/juce_gui_basics/widgets/juce_ComboBox.h index fefe67e9ff..4b2643c82a 100644 --- a/modules/juce_gui_basics/widgets/juce_ComboBox.h +++ b/modules/juce_gui_basics/widgets/juce_ComboBox.h @@ -38,9 +38,8 @@ namespace juce either be read-only text, or editable. To find out when the user selects a different item or edits the text, you - can register a ComboBox::Listener to receive callbacks. - - @see ComboBox::Listener + can assign a lambda to the onChange member, or register a ComboBox::Listener + to receive callbacks. */ class JUCE_API ComboBox : public Component, public SettableTooltipClient, diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp index e715d6633e..142aac3d6b 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.cpp +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.cpp @@ -1220,7 +1220,7 @@ void TextEditor::textChanged() { updateTextHolderSize(); - if (listeners.size() > 0) + if (listeners.size() != 0 || onTextChange != nullptr) postCommandMessage (TextEditorDefs::textChangeMessageId); if (textValue.getValueSource().getReferenceCount() > 1) @@ -2099,19 +2099,35 @@ void TextEditor::handleCommandMessage (const int commandId) { case TextEditorDefs::textChangeMessageId: listeners.callChecked (checker, [this] (Listener& l) { l.textEditorTextChanged (*this); }); + + if (! checker.shouldBailOut() && onTextChange != nullptr) + onTextChange(); + break; case TextEditorDefs::returnKeyMessageId: listeners.callChecked (checker, [this] (Listener& l) { l.textEditorReturnKeyPressed (*this); }); + + if (! checker.shouldBailOut() && onReturnKey != nullptr) + onReturnKey(); + break; case TextEditorDefs::escapeKeyMessageId: listeners.callChecked (checker, [this] (Listener& l) { l.textEditorEscapeKeyPressed (*this); }); + + if (! checker.shouldBailOut() && onEscapeKey != nullptr) + onEscapeKey(); + break; case TextEditorDefs::focusLossMessageId: updateValueFromText(); listeners.callChecked (checker, [this] (Listener& l) { l.textEditorFocusLost (*this); }); + + if (! checker.shouldBailOut() && onFocusLost != nullptr) + onFocusLost(); + break; default: diff --git a/modules/juce_gui_basics/widgets/juce_TextEditor.h b/modules/juce_gui_basics/widgets/juce_TextEditor.h index ebf3231ee2..7616d6a487 100644 --- a/modules/juce_gui_basics/widgets/juce_TextEditor.h +++ b/modules/juce_gui_basics/widgets/juce_TextEditor.h @@ -318,6 +318,19 @@ public: */ void removeListener (Listener* listenerToRemove); + //============================================================================== + /** You can assign a lambda to this callback object to have it called when the text is changed. */ + std::function onTextChange; + + /** You can assign a lambda to this callback object to have it called when the return key is pressed. */ + std::function onReturnKey; + + /** You can assign a lambda to this callback object to have it called when the escape key is pressed. */ + std::function onEscapeKey; + + /** You can assign a lambda to this callback object to have it called when the editor loses key focus. */ + std::function onFocusLost; + //============================================================================== /** Returns the entire contents of the editor. */ String getText() const;