Browse Source

Added a new ValueWithDefault class to juce_data_structures and extended ChoicePropertyComponent and TextPropertyComponent to use this class

tags/2021-05-28
ed 7 years ago
parent
commit
1d5c75546f
6 changed files with 407 additions and 27 deletions
  1. +1
    -0
      modules/juce_data_structures/juce_data_structures.h
  2. +163
    -0
      modules/juce_data_structures/values/juce_ValueWithDefault.h
  3. +121
    -20
      modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp
  4. +35
    -4
      modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h
  5. +68
    -3
      modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp
  6. +19
    -0
      modules/juce_gui_basics/properties/juce_TextPropertyComponent.h

+ 1
- 0
modules/juce_data_structures/juce_data_structures.h View File

@@ -60,5 +60,6 @@
#include "values/juce_ValueTree.h" #include "values/juce_ValueTree.h"
#include "values/juce_ValueTreeSynchroniser.h" #include "values/juce_ValueTreeSynchroniser.h"
#include "values/juce_CachedValue.h" #include "values/juce_CachedValue.h"
#include "values/juce_ValueWithDefault.h"
#include "app_properties/juce_PropertiesFile.h" #include "app_properties/juce_PropertiesFile.h"
#include "app_properties/juce_ApplicationProperties.h" #include "app_properties/juce_ApplicationProperties.h"

+ 163
- 0
modules/juce_data_structures/values/juce_ValueWithDefault.h View File

@@ -0,0 +1,163 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
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
{
//==============================================================================
/**
This class acts as a wrapper around a property inside a ValueTree.
If the property inside the ValueTree is missing or empty the ValueWithDefault will automatically
return a default value, which can be specified when initialising the ValueWithDefault.
*/
class ValueWithDefault
{
public:
//==============================================================================
/** Creates an unitialised ValueWithDefault. Initialise it using one of the referTo() methods. */
ValueWithDefault() : undoManager (nullptr) {}
/** Creates an ValueWithDefault object. The default value will be an empty var. */
ValueWithDefault (ValueTree& tree, const Identifier& propertyID,
UndoManager* um)
: targetTree (tree),
targetProperty (propertyID),
undoManager (um),
defaultValue()
{
}
/** Creates an ValueWithDefault object. The default value will be defaultToUse. */
ValueWithDefault (ValueTree& tree, const Identifier& propertyID,
UndoManager* um, const var& defaultToUse)
: targetTree (tree),
targetProperty (propertyID),
undoManager (um),
defaultValue (defaultToUse)
{
}
/** Creates a ValueWithDefault object from another ValueWithDefault object. */
ValueWithDefault (const ValueWithDefault& other)
: targetTree (other.targetTree),
targetProperty (other.targetProperty),
undoManager (other.undoManager),
defaultValue (other.defaultValue)
{
}
//==============================================================================
/** Returns the current value of the property. If the property does not exist or is empty,
returns the default value.
*/
var get() const noexcept
{
if (isUsingDefault())
return defaultValue;
return targetTree[targetProperty];
}
/** Returns the current property as a Value object. */
Value getPropertyAsValue() { return targetTree.getPropertyAsValue (targetProperty, undoManager); }
/** Returns the current default value. */
var getDefault() const { return defaultValue; }
/** Sets the default value to a new var. */
void setDefault (const var& newDefault)
{
if (defaultValue != newDefault)
defaultValue = newDefault;
}
/** Returns true if the property does not exist or is empty. */
bool isUsingDefault() const
{
return ! targetTree.hasProperty (targetProperty);
}
/** Resets the property to an empty var. */
void resetToDefault() noexcept
{
targetTree.removeProperty (targetProperty, nullptr);
}
//==============================================================================
/** Sets the property and returns the new ValueWithDefault. This will modify the property in the referenced ValueTree. */
ValueWithDefault& operator= (const var& newValue)
{
setValue (newValue, undoManager);
return *this;
}
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
void setValue (const var& newValue, UndoManager* undoManagerToUse)
{
targetTree.setProperty (targetProperty, newValue, undoManagerToUse);
}
//==============================================================================
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree. */
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um)
{
referToWithDefault (tree, property, um, var());
}
/** Makes the ValueWithDefault refer to the specified property inside the given ValueTree,
and specifies a default value to use.
*/
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const var& defaultVal)
{
referToWithDefault (tree, property, um, defaultVal);
}
//==============================================================================
/** Returns a reference to the ValueTree containing the referenced property. */
ValueTree& getValueTree() noexcept { return targetTree; }
/** Returns the property ID of the referenced property. */
Identifier& getPropertyID() noexcept { return targetProperty; }
private:
//==============================================================================
ValueTree targetTree;
Identifier targetProperty;
UndoManager* undoManager;
var defaultValue;
//==============================================================================
void referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const var& defaultVal)
{
targetTree = v;
targetProperty = i;
undoManager = um;
defaultValue = defaultVal;
}
};
} // namespace juce

+ 121
- 20
modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp View File

@@ -27,30 +27,32 @@
namespace juce namespace juce
{ {
//==============================================================================
class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource, class ChoicePropertyComponent::RemapperValueSource : public Value::ValueSource,
private Value::Listener private Value::Listener
{ {
public: public:
RemapperValueSource (const Value& source, const Array<var>& map) RemapperValueSource (const Value& source, const Array<var>& map)
: sourceValue (source), mappings (map)
: sourceValue (source),
mappings (map)
{ {
sourceValue.addListener (this); sourceValue.addListener (this);
} }
var getValue() const
var getValue() const override
{ {
const var targetValue (sourceValue.getValue());
auto targetValue = sourceValue.getValue();
for (int i = 0; i < mappings.size(); ++i)
if (mappings.getReference(i).equalsWithSameType (targetValue))
return i + 1;
for (auto& map : mappings)
if (map.equalsWithSameType (targetValue))
return mappings.indexOf (map) + 1;
return mappings.indexOf (targetValue) + 1; return mappings.indexOf (targetValue) + 1;
} }
void setValue (const var& newValue)
void setValue (const var& newValue) override
{ {
const var remappedVal (mappings [static_cast<int> (newValue) - 1]);
auto remappedVal = mappings [static_cast<int> (newValue) - 1];
if (! remappedVal.equalsWithSameType (sourceValue)) if (! remappedVal.equalsWithSameType (sourceValue))
sourceValue = remappedVal; sourceValue = remappedVal;
@@ -60,14 +62,66 @@ protected:
Value sourceValue; Value sourceValue;
Array<var> mappings; Array<var> mappings;
void valueChanged (Value&)
{
sendChangeMessage (true);
}
void valueChanged (Value&) override { sendChangeMessage (true); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSource) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSource)
}; };
//==============================================================================
class ChoicePropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource,
private Value::Listener
{
public:
RemapperValueSourceWithDefault (const ValueWithDefault& vwd, const Array<var>& map)
: valueWithDefault (vwd),
sourceValue (valueWithDefault.getPropertyAsValue()),
mappings (map)
{
sourceValue.addListener (this);
}
var getValue() const override
{
if (valueWithDefault.isUsingDefault())
return -1;
auto targetValue = sourceValue.getValue();
for (auto map : mappings)
if (map.equalsWithSameType (targetValue))
return mappings.indexOf (map) + 1;
return mappings.indexOf (targetValue) + 1;
}
void setValue (const var& newValue) override
{
auto newValueInt = static_cast<int> (newValue);
if (newValueInt == -1)
{
valueWithDefault.resetToDefault();
}
else
{
auto remappedVal = mappings [newValueInt - 1];
if (! remappedVal.equalsWithSameType (sourceValue))
valueWithDefault = remappedVal;
}
}
private:
ValueWithDefault valueWithDefault;
Value sourceValue;
Array<var> mappings;
void valueChanged (Value&) override { sendChangeMessage (true); }
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault)
};
//============================================================================== //==============================================================================
ChoicePropertyComponent::ChoicePropertyComponent (const String& name) ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
@@ -76,24 +130,54 @@ ChoicePropertyComponent::ChoicePropertyComponent (const String& name)
{ {
} }
ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
ChoicePropertyComponent::ChoicePropertyComponent (const String& name,
const StringArray& choiceList, const StringArray& choiceList,
const Array<var>& correspondingValues) const Array<var>& correspondingValues)
: PropertyComponent (name), : PropertyComponent (name),
choices (choiceList),
isCustomClass (false)
choices (choiceList)
{ {
// The array of corresponding values must contain one value for each of the items in // The array of corresponding values must contain one value for each of the items in
// the choices array! // the choices array!
jassert (correspondingValues.size() == choices.size()); jassert (correspondingValues.size() == choices.size());
ignoreUnused (correspondingValues);
}
ChoicePropertyComponent::ChoicePropertyComponent (const Value& valueToControl,
const String& name,
const StringArray& choiceList,
const Array<var>& correspondingValues)
: ChoicePropertyComponent (name, choiceList, correspondingValues)
{
createComboBox(); createComboBox();
comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSource (valueToControl, comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSource (valueToControl,
correspondingValues))); correspondingValues)));
} }
ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault valueToControl,
const String& name,
const StringArray& choiceList,
const Array<var>& correspondingValues)
: ChoicePropertyComponent (name, choiceList, correspondingValues)
{
createComboBoxWithDefault (choiceList [correspondingValues.indexOf (valueToControl.getDefault())]);
comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueToControl,
correspondingValues)));
}
ChoicePropertyComponent::ChoicePropertyComponent (ValueWithDefault valueToControl,
const String& name)
: PropertyComponent (name),
choices ({ "Enabled", "Disabled" })
{
createComboBoxWithDefault (valueToControl.getDefault() ? "Enabled" : "Disabled");
comboBox.getSelectedIdAsValue().referTo (Value (new RemapperValueSourceWithDefault (valueToControl,
{ true, false })));
}
ChoicePropertyComponent::~ChoicePropertyComponent() ChoicePropertyComponent::~ChoicePropertyComponent()
{ {
} }
@@ -103,10 +187,27 @@ void ChoicePropertyComponent::createComboBox()
{ {
addAndMakeVisible (comboBox); addAndMakeVisible (comboBox);
for (int i = 0; i < choices.size(); ++i)
for (auto choice : choices)
{
if (choice.isNotEmpty())
comboBox.addItem (choice, choices.indexOf (choice) + 1);
else
comboBox.addSeparator();
}
comboBox.setEditableText (false);
}
void ChoicePropertyComponent::createComboBoxWithDefault (const String& defaultString)
{
addAndMakeVisible (comboBox);
comboBox.addItem ("Default" + (defaultString.isNotEmpty() ? " (" + defaultString + ")" : ""), -1);
for (auto choice : choices)
{ {
if (choices[i].isNotEmpty())
comboBox.addItem (choices[i], i + 1);
if (choice.isNotEmpty())
comboBox.addItem (choice, choices.indexOf (choice) + 1);
else else
comboBox.addSeparator(); comboBox.addSeparator();
} }
@@ -149,7 +250,7 @@ void ChoicePropertyComponent::comboBoxChanged (ComboBox*)
{ {
if (isCustomClass) if (isCustomClass)
{ {
const int newIndex = comboBox.getSelectedId() - 1;
auto newIndex = comboBox.getSelectedId() - 1;
if (newIndex != getIndex()) if (newIndex != getIndex())
setIndex (newIndex); setIndex (newIndex);


+ 35
- 4
modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.h View File

@@ -75,6 +75,31 @@ public:
const StringArray& choices, const StringArray& choices,
const Array<var>& correspondingValues); const Array<var>& correspondingValues);
/** Creates the component using a ValueWithDefault object. This will add an item to the ComboBox for the
default value with an ID of -1.
@param valueToControl the ValueWithDefault object that contains the Value object that the combo box will read and control
@param propertyName the name of the property
@param choices the list of possible values that the drop-down list will contain
@param correspondingValues a list of values corresponding to each item in the 'choices' StringArray.
These are the values that will be read and written to the
valueToControl value. This array must contain the same number of items
as the choices array
*/
ChoicePropertyComponent (ValueWithDefault valueToControl,
const String& propertyName,
const StringArray& choices,
const Array<var>& correspondingValues);
/** Creates the component using a ValueWithDefault object, adding an item to the ComboBox for the
default value with an ID of -1 as well as adding separate "Enabled" and "Disabled" options.
This is useful for simple on/off choices that also need a default value.
*/
ChoicePropertyComponent (ValueWithDefault valueToControl,
const String& propertyName);
/** Destructor. */ /** Destructor. */
~ChoicePropertyComponent(); ~ChoicePropertyComponent();
@@ -95,10 +120,9 @@ public:
/** Returns the list of options. */ /** Returns the list of options. */
const StringArray& getChoices() const; const StringArray& getChoices() const;
//============================================================================== //==============================================================================
/** @internal */ /** @internal */
void refresh();
void refresh() override;
protected: protected:
/** The list of options that will be shown in the combo box. /** The list of options that will be shown in the combo box.
@@ -110,12 +134,19 @@ protected:
StringArray choices; StringArray choices;
private: private:
/** Delegating constructor. */
ChoicePropertyComponent (const String&, const StringArray&, const Array<var>&);
ComboBox comboBox; ComboBox comboBox;
bool isCustomClass;
bool isCustomClass = false;
class RemapperValueSource; class RemapperValueSource;
class RemapperValueSourceWithDefault;
void createComboBox(); void createComboBox();
void comboBoxChanged (ComboBox*);
void createComboBoxWithDefault (const String&);
void comboBoxChanged (ComboBox*) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoicePropertyComponent) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChoicePropertyComponent)
}; };


+ 68
- 3
modules/juce_gui_basics/properties/juce_TextPropertyComponent.cpp View File

@@ -27,6 +27,7 @@
namespace juce namespace juce
{ {
//==============================================================================
class TextPropertyComponent::LabelComp : public Label, class TextPropertyComponent::LabelComp : public Label,
public FileDragAndDropTarget public FileDragAndDropTarget
{ {
@@ -85,11 +86,66 @@ public:
interestedInFileDrag = isInterested; interestedInFileDrag = isInterested;
} }
void setTextToDisplayWhenEmpty (const String& text, float alpha)
{
textToDisplayWhenEmpty = text;
alphaToUseForEmptyText = alpha;
}
void paintOverChildren (Graphics& g) override
{
if (getText().isEmpty() && ! isBeingEdited())
{
auto textArea = getBorderSize().subtractedFrom (getLocalBounds());
auto labelFont = owner.getLookAndFeel().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: private:
TextPropertyComponent& owner; TextPropertyComponent& owner;
int maxChars; int maxChars;
bool isMultiline; bool isMultiline;
bool interestedInFileDrag = true; bool interestedInFileDrag = true;
String textToDisplayWhenEmpty;
float alphaToUseForEmptyText = 0.0f;
};
//==============================================================================
class TextPropertyComponent::RemapperValueSourceWithDefault : public Value::ValueSource
{
public:
RemapperValueSourceWithDefault (const ValueWithDefault& vwd)
: valueWithDefault (vwd)
{
}
var getValue() const override
{
return valueWithDefault.isUsingDefault() ? var() : valueWithDefault.get();
}
void setValue (const var& newValue) override
{
if (newValue.toString().isEmpty())
valueWithDefault.resetToDefault();
else
valueWithDefault = newValue;
}
private:
ValueWithDefault valueWithDefault;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RemapperValueSourceWithDefault)
}; };
//============================================================================== //==============================================================================
@@ -107,13 +163,22 @@ TextPropertyComponent::TextPropertyComponent (const Value& valueToControl,
int maxNumChars, int maxNumChars,
bool isMultiLine, bool isMultiLine,
bool isEditable) bool isEditable)
: PropertyComponent (name)
: TextPropertyComponent (name, maxNumChars, isMultiLine, isEditable)
{ {
createEditor (maxNumChars, isMultiLine, isEditable);
textEditor->getTextValue().referTo (valueToControl); textEditor->getTextValue().referTo (valueToControl);
} }
TextPropertyComponent::TextPropertyComponent (const ValueWithDefault& valueToControl,
const String& name,
int maxNumChars,
bool isMultiLine,
bool isEditable)
: TextPropertyComponent (name, maxNumChars, isMultiLine, isEditable)
{
textEditor->getTextValue().referTo (Value (new RemapperValueSourceWithDefault (valueToControl)));
textEditor->setTextToDisplayWhenEmpty (valueToControl.getDefault(), 0.5f);
}
TextPropertyComponent::~TextPropertyComponent() TextPropertyComponent::~TextPropertyComponent()
{ {
} }


+ 19
- 0
modules/juce_gui_basics/properties/juce_TextPropertyComponent.h View File

@@ -70,6 +70,23 @@ public:
bool isMultiLine, bool isMultiLine,
bool isEditable = true); bool isEditable = true);
/** Creates a text property component with a default value.
@param valueToControl The ValueWithDefault that is controlled by the TextPropertyComponent
@param propertyName The name of the property
@param maxNumChars If not zero, then this specifies the maximum allowable length of
the string. If zero, then the string will have no length limit.
@param isMultiLine Sets whether the text editor allows carriage returns.
@param isEditable Sets whether the text editor is editable. The default is true.
@see TextEditor, setEditable
*/
TextPropertyComponent (const ValueWithDefault& valueToControl,
const String& propertyName,
int maxNumChars,
bool isMultiLine,
bool isEditable = true);
/** Destructor. */ /** Destructor. */
~TextPropertyComponent(); ~TextPropertyComponent();
@@ -144,6 +161,8 @@ public:
virtual void textWasEdited(); virtual void textWasEdited();
private: private:
class RemapperValueSourceWithDefault;
class LabelComp; class LabelComp;
friend class LabelComp; friend class LabelComp;


Loading…
Cancel
Save