Skip to content

Commit

Permalink
Merge pull request #62 from ffAudio/develop
Browse files Browse the repository at this point in the history
Release 1.3.3
  • Loading branch information
ffAudio committed Jul 1, 2021
2 parents f4a6cb2 + 9a329d5 commit 41e7f2b
Show file tree
Hide file tree
Showing 35 changed files with 605 additions and 204 deletions.
44 changes: 44 additions & 0 deletions DESIGNING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Designing in foleys_gui_magic
=============================

General Setup
-------------

PluginGuiMagic is a layout engine and editor to create GUIs in JUCE without writing code.
The whole GUI is defined as a DOM tree of Components and CSS instructions of visual properties.

Each Component is wrapped into a GuiItem that adds decorations to or around the Components.
The main decorations is a margin, padding and a border, as well as a caption.
The background can be an image, a colour or a colour gradient.

The different panels are hierarchically organised. Each View contains other View containers or Components.
Every View has a property `display` which selects, how the children are layouted. By default this is set to `FlexBox`.
If you set it to `Content` you can drag each child individually to any place. The number is reflected in the
property view in the side panel under `Node`. By adding a % as suffix the positions and dimensions will become relative
to the parent View instead of in absolute pixels. the third option is Tabbed, which will create a tab bar for the child Views or Components.

CSS Classes
-----------

You can set all properties to the nodes. But the properties are looked up traversing the DOM.
Every Component or View can be assigned a list of CSS classes, so the properties are looked up in those classes too.
The classes have a switch, if the properties shall be used by the children as well or just by the class referencing the CSS class.

Responsive design
-----------------

The layout is managed via FlexBox, which offers a lot of options how to react to resizing.

Additionally the CSS classes have switches to be turned on or off. You can connect that for instance to a checkbox to turn
the visibility on or off. Another method to change the layout rules is to set a min size or max size to a CSS class, which also
switches the class off if the plugin size is smaller than the min size or greater than the max size.

Saving and loading
------------------

To save the GUI use the File menu in the toolbox. You can also use `clear` in the File menu to start from scratch or
use `default` to create a default GUI from the parameters and visualisers found in the processor.

To get the GUI into the product make sure the BinaryData is recreated (which happens automatically in cmake but
using the Projucer you need to save the Project there and recompile.

1 change: 1 addition & 0 deletions Editor/foleys_PropertiesEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ void PropertiesEditor::addNodeProperties()
array.add (new juce::TextPropertyComponent (styleItem.getPropertyAsValue (IDs::maxWidth, &undo), IDs::maxWidth.toString(), 8, false));
array.add (new juce::TextPropertyComponent (styleItem.getPropertyAsValue (IDs::minHeight, &undo), IDs::minHeight.toString(), 8, false));
array.add (new juce::TextPropertyComponent (styleItem.getPropertyAsValue (IDs::maxHeight, &undo), IDs::maxHeight.toString(), 8, false));
array.add (new juce::TextPropertyComponent (styleItem.getPropertyAsValue (IDs::aspect, &undo), IDs::aspect.toString(), 8, false));
}

auto classNames = builder.getStylesheet().getAllClassesNames();
Expand Down
1 change: 0 additions & 1 deletion Editor/foleys_StyleChoicePropertyComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ class StyleChoicePropertyComponent : public StylePropertyComponent,

void valueChanged (juce::Value& value) override;

SettableProperty::PropertyType type = SettableProperty::Choice;
juce::StringArray choices;
std::function<void(juce::ComboBox&)> menuCreationLambda;
juce::Value proxy;
Expand Down
12 changes: 12 additions & 0 deletions Editor/foleys_StylePropertyComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ StylePropertyComponent::StylePropertyComponent (MagicGUIBuilder& builderToUse, j
node.removeProperty (property, &builder.getUndoManager());
refresh();
};

node.addListener (this);
}

StylePropertyComponent::~StylePropertyComponent()
{
node.removeListener (this);
}

juce::var StylePropertyComponent::lookupValue()
Expand Down Expand Up @@ -131,5 +138,10 @@ void StylePropertyComponent::mouseDoubleClick (const juce::MouseEvent&)
builder.getMagicToolBox().setNodeToEdit (inheritedFrom);
}

void StylePropertyComponent::valueTreePropertyChanged (juce::ValueTree& tree, const juce::Identifier& changedProperty)
{
if (tree == node && property == changedProperty)
refresh();
}

} // namespace foleys
6 changes: 5 additions & 1 deletion Editor/foleys_StylePropertyComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ namespace foleys
{


class StylePropertyComponent : public juce::PropertyComponent
class StylePropertyComponent : public juce::PropertyComponent,
private juce::ValueTree::Listener
{
public:
StylePropertyComponent (MagicGUIBuilder& builder, juce::Identifier property, juce::ValueTree& node);
~StylePropertyComponent() override;

void paint (juce::Graphics& g) override;
void resized() override;
Expand All @@ -65,6 +67,8 @@ class StylePropertyComponent : public juce::PropertyComponent
juce::TextButton remove { "X" };

private:
void valueTreePropertyChanged (juce::ValueTree& treeWhosePropertyHasChanged,
const juce::Identifier& changedProperty) override;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StylePropertyComponent)
};
Expand Down
1 change: 0 additions & 1 deletion Editor/foleys_ToolBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ ToolBox::ToolBox (juce::Component* parentToUse, MagicGUIBuilder& builderToContro
file.addItem ("Save XML", [&] { saveDialog(); });
file.addSeparator();
file.addItem ("Clear", [&] { builder.clearGUI(); });
file.addItem ("Default", [&] { builder.resetToDefaultGUI(); });
file.addSeparator();
file.addItem ("Refresh", [&] { builder.updateComponents(); });
file.show();
Expand Down
12 changes: 1 addition & 11 deletions General/foleys_MagicGUIBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ void MagicGUIBuilder::updateStylesheet()
{
auto stylesNode = getConfigTree().getOrCreateChildWithName (IDs::styles, &undo);
if (stylesNode.getNumChildren() == 0)
stylesNode.appendChild (magicState.createDefaultStylesheet(), &undo);
stylesNode.appendChild (DefaultGuiTrees::createDefaultStylesheet(), &undo);

auto selectedName = stylesNode.getProperty (IDs::selected, {}).toString();
if (selectedName.isNotEmpty())
Expand All @@ -115,16 +115,6 @@ void MagicGUIBuilder::clearGUI()
updateComponents();
}

void MagicGUIBuilder::resetToDefaultGUI()
{
auto guiNode = getConfigTree().getOrCreateChildWithName (IDs::view, &undo);
guiNode.removeAllChildren (&undo);
guiNode.removeAllProperties (&undo);
guiNode.copyPropertiesAndChildrenFrom (magicState.createDefaultGUITree(), &undo);

updateComponents();
}

void MagicGUIBuilder::showOverlayDialog (std::unique_ptr<juce::Component> dialog)
{
if (parent == nullptr)
Expand Down
5 changes: 0 additions & 5 deletions General/foleys_MagicGUIBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,6 @@ class MagicGUIBuilder : public juce::ChangeListener
*/
void clearGUI();

/**
Remove the current GUI and replaces it with a generated default
*/
void resetToDefaultGUI();

/**
This is used to display a dialog box. It is called by the GUI editor, but in future it might be reached
using the configured GUI.
Expand Down
29 changes: 28 additions & 1 deletion General/foleys_MagicJUCEFactories.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ class SliderItem : public GuiItem
static const juce::Identifier pValue;
static const juce::Identifier pMinValue;
static const juce::Identifier pMaxValue;
static const juce::Identifier pInterval;
static const juce::Identifier pSuffix;

static const juce::Identifier pFilmStrip;
static const juce::Identifier pNumImages;

SliderItem (MagicGUIBuilder& builder, const juce::ValueTree& node) : GuiItem (builder, node)
{
Expand Down Expand Up @@ -106,8 +111,12 @@ class SliderItem : public GuiItem

double minValue = getProperty (pMinValue);
double maxValue = getProperty (pMaxValue);
double interval = getProperty (pInterval);
if (maxValue > minValue)
slider.setRange (minValue, maxValue);
slider.setRange (minValue, maxValue, interval);

auto suffix = getProperty (pSuffix).toString();
slider.setTextValueSuffix (suffix);

auto valueID = configNode.getProperty (pValue, juce::String()).toString();
if (valueID.isNotEmpty())
Expand All @@ -116,6 +125,16 @@ class SliderItem : public GuiItem
auto paramID = getControlledParameterID ({});
if (paramID.isNotEmpty())
attachment = getMagicState().createAttachment (paramID, slider);

auto filmStripName = getProperty (pFilmStrip).toString();
if (filmStripName.isNotEmpty())
{
auto filmStrip = Resources::getImage (filmStripName);
slider.setFilmStrip (filmStrip);
}

int numFilmImages = getProperty (pNumImages);
slider.setNumImages (numFilmImages, false);
}

std::vector<SettableProperty> getSettableProperties() const override
Expand All @@ -128,6 +147,10 @@ class SliderItem : public GuiItem
props.push_back ({ configNode, pValue, SettableProperty::Choice, 1.0f, magicBuilder.createPropertiesMenuLambda() });
props.push_back ({ configNode, pMinValue, SettableProperty::Number, 0.0f, {} });
props.push_back ({ configNode, pMaxValue, SettableProperty::Number, 2.0f, {} });
props.push_back ({ configNode, pInterval, SettableProperty::Number, 0.0f, {} });
props.push_back ({ configNode, pSuffix, SettableProperty::Text, {}, {} });
props.push_back ({ configNode, pFilmStrip, SettableProperty::Choice, 0.0f, magicBuilder.createChoicesMenuLambda(Resources::getResourceFileNames()) });
props.push_back ({ configNode, pNumImages, SettableProperty::Number, 0.0f, {} });

return props;
}
Expand Down Expand Up @@ -155,6 +178,10 @@ const juce::StringArray SliderItem::pTextBoxPositions { "no-textbox", "textbox-a
const juce::Identifier SliderItem::pValue { "value" };
const juce::Identifier SliderItem::pMinValue { "min-value" };
const juce::Identifier SliderItem::pMaxValue { "max-value" };
const juce::Identifier SliderItem::pInterval { "interval" };
const juce::Identifier SliderItem::pSuffix { "suffix" };
const juce::Identifier SliderItem::pFilmStrip { "filmstrip" };
const juce::Identifier SliderItem::pNumImages { "num-filmstrip-images" };


//==============================================================================
Expand Down
29 changes: 14 additions & 15 deletions General/foleys_MagicPluginEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,9 @@ MagicPluginEditor::MagicPluginEditor (MagicProcessorState& stateToUse, std::uniq
builder->registerJUCELookAndFeels();
}

#if FOLEYS_SAVE_EDITED_GUI_IN_PLUGIN_STATE
auto guiTree = processorState.getValueTree().getChildWithName ("magic");
if (guiTree.isValid())
setConfigTree (guiTree);
else
builder->createGUI (*this);
#else // FOLEYS_SAVE_EDITED_GUI_IN_PLUGIN_STATE
auto guiTree = processorState.getGuiTree();
if (guiTree.isValid())
setConfigTree (guiTree);
#endif // FOLEYS_SAVE_EDITED_GUI_IN_PLUGIN_STATE

updateSize();

Expand Down Expand Up @@ -108,24 +100,31 @@ void MagicPluginEditor::updateSize()
int minHeight = rootNode.getProperty (IDs::minHeight, 10);
int maxWidth = rootNode.getProperty (IDs::maxWidth, maximalBounds.getWidth());
int maxHeight = rootNode.getProperty (IDs::maxHeight, maximalBounds.getHeight());
double aspect = rootNode.getProperty (IDs::aspect, 0.0);
setResizable (resizable, resizeCorner);
setResizeLimits (minWidth, minHeight, maxWidth, maxHeight);
if (aspect > 0.0)
getConstrainer()->setFixedAspectRatio (aspect);
}

setSize (width, height);
}

void MagicPluginEditor::setConfigTree (const juce::ValueTree& gui)
void MagicPluginEditor::setConfigTree (const juce::ValueTree& config)
{
jassert (config.isValid() && config.hasType(IDs::magic));

// Set default values
auto rootNode = gui.getChildWithName (IDs::view);
auto& undo = builder->getUndoManager();
if (! rootNode.hasProperty (IDs::resizable)) rootNode.setProperty (IDs::resizable, true, &undo);
if (! rootNode.hasProperty (IDs::resizeCorner)) rootNode.setProperty (IDs::resizeCorner, true, &undo);
auto rootNode = config.getChildWithName (IDs::view);

processorState.setGuiValueTree (gui);
builder->createGUI (*this);
if (rootNode.isValid())
{
auto& undo = builder->getUndoManager();
if (!rootNode.hasProperty(IDs::resizable)) rootNode.setProperty (IDs::resizable, true, &undo);
if (!rootNode.hasProperty(IDs::resizeCorner)) rootNode.setProperty (IDs::resizeCorner, true, &undo);
}

builder->createGUI (*this);
updateSize();
}

Expand Down
4 changes: 2 additions & 2 deletions General/foleys_MagicPluginEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ class MagicPluginEditor : public juce::AudioProcessorEditor,
/**
Setup a GUI from a previously stored ValueTree
@param gui the ValueTree that defines the GUI of the editor
@param gui the ValueTree that defines the Stylesheet, colour palette and GUI components of the editor
*/
void setConfigTree (const juce::ValueTree& gui);
void setConfigTree (const juce::ValueTree& config);

/**
Grants access to the MagicGUIBuilder
Expand Down
22 changes: 22 additions & 0 deletions General/foleys_MagicProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,35 @@ void MagicProcessor::initialiseBuilder (MagicGUIBuilder& builder)
builder.registerJUCELookAndFeels();
}

juce::ValueTree MagicProcessor::createGuiValueTree()
{
juce::ValueTree magic { IDs::magic };
magic.appendChild (DefaultGuiTrees::createDefaultStylesheet(), nullptr);

juce::ValueTree rootNode {IDs::view, {{ IDs::id, IDs::root }}};

auto plotView = DefaultGuiTrees::createPlotView (magicState);
if (plotView.isValid())
rootNode.appendChild (plotView, nullptr);

auto params = DefaultGuiTrees::createProcessorGui (getParameterTree());
rootNode.appendChild (params, nullptr);

magic.appendChild (rootNode, nullptr);

return magic;
}

juce::AudioProcessorEditor* MagicProcessor::createEditor()
{
magicState.updateParameterMap();

auto builder = std::make_unique<MagicGUIBuilder>(magicState);
initialiseBuilder (*builder);

if (magicState.getGuiTree().getChildWithName (IDs::view).isValid() == false)
magicState.setGuiValueTree (createGuiValueTree());

return new MagicPluginEditor (magicState, std::move (builder));
}

Expand Down
10 changes: 10 additions & 0 deletions General/foleys_MagicProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ class MagicProcessor : public juce::AudioProcessor
*/
virtual void initialiseBuilder (MagicGUIBuilder& builder);

/**
This method is called to create the GUI. The default implementation will come up with a ValueTree containing
a default Stylesheet and populate the GUI components from the AudioProcessorParameters it finds using
getParameterTree() as well as getting the MagicPlotSources.
You can override this method with your bespoke algorithm to create a ValueTree or to load your ValueTree
from BinaryData.
*/
virtual juce::ValueTree createGuiValueTree();

/**
If there is anything you need to do after a new state was loaded you can override this method
*/
Expand Down
1 change: 1 addition & 0 deletions General/foleys_StringDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ namespace IDs
static juce::Identifier maxHeight { "max-height" };
static juce::Identifier width { "width" };
static juce::Identifier height { "height" };
static juce::Identifier aspect { "aspect" };

static juce::Identifier posX { "pos-x" };
static juce::Identifier posY { "pos-y" };
Expand Down
Loading

0 comments on commit 41e7f2b

Please sign in to comment.