/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2020 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { //============================================================================== class TextPropertyComponent::LabelComp : public Label, public FileDragAndDropTarget { public: LabelComp (TextPropertyComponent& tpc, int charLimit, bool multiline, bool editable) : Label ({}, {}), owner (tpc), maxChars (charLimit), isMultiline (multiline) { setEditable (editable, editable); updateColours(); } bool isInterestedInFileDrag (const StringArray&) override { return interestedInFileDrag; } void filesDropped (const StringArray& files, int, int) override { setText (getText() + files.joinIntoString (isMultiline ? "\n" : ", "), sendNotificationSync); showEditor(); } TextEditor* createEditorComponent() override { auto* ed = Label::createEditorComponent(); ed->setInputRestrictions (maxChars); if (isMultiline) { ed->setMultiLine (true, true); ed->setReturnKeyStartsNewLine (true); } return ed; } void textWasEdited() override { owner.textWasEdited(); } void updateColours() { setColour (backgroundColourId, owner.findColour (TextPropertyComponent::backgroundColourId)); setColour (outlineColourId, owner.findColour (TextPropertyComponent::outlineColourId)); setColour (textColourId, owner.findColour (TextPropertyComponent::textColourId)); repaint(); } void setInterestedInFileDrag (bool isInterested) { interestedInFileDrag = isInterested; } void setTextToDisplayWhenEmpty (const String& text, float alpha) { textToDisplayWhenEmpty = text; alphaToUseForEmptyText = alpha; } void paintOverChildren (Graphics& g) override { if (getText().isEmpty() && ! isBeingEdited()) { auto& lf = owner.getLookAndFeel(); auto textArea = lf.getLabelBorderSize (*this).subtractedFrom (getLocalBounds()); auto labelFont = lf.getLabelFont (*this); g.setColour (owner.findColour (TextPropertyComponent::textColourId).withAlpha (alphaToUseForEmptyText)); g.setFont (labelFont); g.drawFittedText (textToDisplayWhenEmpty, textArea, getJustificationType(), jmax (1, (int) (textArea.getHeight() / labelFont.getHeight())), getMinimumHorizontalScale()); } } private: TextPropertyComponent& owner; int maxChars; bool isMultiline; bool interestedInFileDrag = true; String textToDisplayWhenEmpty; float alphaToUseForEmptyText = 0.0f; }; //============================================================================== class TextPropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource { public: RemapperValueSourceWithDefault (ValueWithDefault* vwd) : valueWithDefault (vwd) { } var getValue() const override { if (valueWithDefault == nullptr || valueWithDefault->isUsingDefault()) return {}; return valueWithDefault->get(); } void setValue (const var& newValue) override { if (valueWithDefault == nullptr) return; if (newValue.toString().isEmpty()) valueWithDefault->resetToDefault(); else *valueWithDefault = newValue; } private: WeakReference valueWithDefault; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault) }; //============================================================================== TextPropertyComponent::TextPropertyComponent (const String& name, int maxNumChars, bool multiLine, bool isEditable) : PropertyComponent (name), isMultiLine (multiLine) { createEditor (maxNumChars, isEditable); } TextPropertyComponent::TextPropertyComponent (const Value& valueToControl, const String& name, int maxNumChars, bool multiLine, bool isEditable) : TextPropertyComponent (name, maxNumChars, multiLine, isEditable) { textEditor->getTextValue().referTo (valueToControl); } TextPropertyComponent::TextPropertyComponent (ValueWithDefault& valueToControl, const String& name, int maxNumChars, bool multiLine, bool isEditable) : TextPropertyComponent (name, maxNumChars, multiLine, isEditable) { valueWithDefault = &valueToControl; textEditor->getTextValue().referTo (Value (new RemapperValueSourceWithDefault (valueWithDefault))); textEditor->setTextToDisplayWhenEmpty (valueWithDefault->getDefault(), 0.5f); valueWithDefault->onDefaultChange = [this] { textEditor->setTextToDisplayWhenEmpty (valueWithDefault->getDefault(), 0.5f); repaint(); }; } TextPropertyComponent::~TextPropertyComponent() { if (valueWithDefault != nullptr) valueWithDefault->onDefaultChange = nullptr; } void TextPropertyComponent::setText (const String& newText) { textEditor->setText (newText, sendNotificationSync); } String TextPropertyComponent::getText() const { return textEditor->getText(); } Value& TextPropertyComponent::getValue() const { return textEditor->getTextValue(); } void TextPropertyComponent::createEditor (int maxNumChars, bool isEditable) { textEditor.reset (new LabelComp (*this, maxNumChars, isMultiLine, isEditable)); addAndMakeVisible (textEditor.get()); if (isMultiLine) { textEditor->setJustificationType (Justification::topLeft); preferredHeight = 100; } } void TextPropertyComponent::refresh() { textEditor->setText (getText(), dontSendNotification); } void TextPropertyComponent::textWasEdited() { auto newText = textEditor->getText(); if (getText() != newText) setText (newText); callListeners(); } void TextPropertyComponent::addListener (TextPropertyComponent::Listener* l) { listenerList.add (l); } void TextPropertyComponent::removeListener (TextPropertyComponent::Listener* l) { listenerList.remove (l); } void TextPropertyComponent::callListeners() { Component::BailOutChecker checker (this); listenerList.callChecked (checker, [this] (Listener& l) { l.textPropertyComponentChanged (this); }); } void TextPropertyComponent::colourChanged() { PropertyComponent::colourChanged(); textEditor->updateColours(); } void TextPropertyComponent::setInterestedInFileDrag (bool isInterested) { if (textEditor != nullptr) textEditor->setInterestedInFileDrag (isInterested); } void TextPropertyComponent::setEditable (bool isEditable) { if (textEditor != nullptr) textEditor->setEditable (isEditable, isEditable); } } // namespace juce