git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce

subrepo:
  subdir:   "deps/juce"
  merged:   "b13f9084e"
upstream:
  origin:   "https://github.com/essej/JUCE.git"
  branch:   "sono6good"
  commit:   "b13f9084e"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"
This commit is contained in:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View File

@ -0,0 +1,389 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class ButtonHandler : public ComponentTypeHandler
{
public:
ButtonHandler (const String& typeDescription_,
const String& className_,
const std::type_info& componentClass,
const int defaultWidth_,
const int defaultHeight_)
: ComponentTypeHandler (typeDescription_, className_, componentClass,
defaultWidth_, defaultHeight_)
{}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* b = dynamic_cast<Button*> (component))
{
props.add (new ButtonTextProperty (b, document));
props.add (new ButtonCallbackProperty (b, document));
props.add (new ButtonRadioGroupProperty (b, document));
props.add (new ButtonConnectedEdgeProperty ("connected left", Button::ConnectedOnLeft, b, document));
props.add (new ButtonConnectedEdgeProperty ("connected right", Button::ConnectedOnRight, b, document));
props.add (new ButtonConnectedEdgeProperty ("connected top", Button::ConnectedOnTop, b, document));
props.add (new ButtonConnectedEdgeProperty ("connected bottom", Button::ConnectedOnBottom, b, document));
}
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
Button* const b = dynamic_cast<Button*> (comp);
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("buttonText", b->getButtonText());
e->setAttribute ("connectedEdges", b->getConnectedEdgeFlags());
e->setAttribute ("needsCallback", needsButtonListener (b));
e->setAttribute ("radioGroupId", b->getRadioGroupId());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
Button* const b = dynamic_cast<Button*> (comp);
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
b->setButtonText (xml.getStringAttribute ("buttonText", b->getButtonText()));
b->setConnectedEdges (xml.getIntAttribute ("connectedEdges", 0));
setNeedsButtonListener (b, xml.getBoolAttribute ("needsCallback", true));
b->setRadioGroupId (xml.getIntAttribute ("radioGroupId", 0));
return true;
}
String getCreationParameters (GeneratedCode&, Component* component) override
{
return quotedString (component->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
Button* const b = dynamic_cast<Button*> (component);
if (b->getButtonText() != b->getName())
{
code.constructorCode
<< memberVariableName << "->setButtonText ("
<< quotedString (b->getButtonText(), code.shouldUseTransMacro()) << ");\n";
}
if (b->getConnectedEdgeFlags() != 0)
{
StringArray flags;
if (b->isConnectedOnLeft())
flags.add ("juce::Button::ConnectedOnLeft");
if (b->isConnectedOnRight())
flags.add ("juce::Button::ConnectedOnRight");
if (b->isConnectedOnTop())
flags.add ("juce::Button::ConnectedOnTop");
if (b->isConnectedOnBottom())
flags.add ("juce::Button::ConnectedOnBottom");
String s;
s << memberVariableName << "->setConnectedEdges ("
<< flags.joinIntoString (" | ") << ");\n";
code.constructorCode += s;
}
if (b->getRadioGroupId() != 0)
code.constructorCode << memberVariableName << "->setRadioGroupId ("
<< b->getRadioGroupId() << ");\n";
if (needsButtonListener (component))
code.constructorCode << memberVariableName << "->addListener (this);\n";
}
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
{
ComponentTypeHandler::fillInGeneratedCode (component, code);
if (needsButtonListener (component))
{
String& callback = code.getCallbackCode ("public juce::Button::Listener",
"void",
"buttonClicked (juce::Button* buttonThatWasClicked)",
true);
if (callback.isNotEmpty())
callback << "else ";
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
const String userCodeComment ("UserButtonCode_" + memberVariableName);
callback
<< "if (buttonThatWasClicked == " << memberVariableName << ".get())\n"
<< "{\n //[" << userCodeComment << "] -- add your button handler code here..\n //[/" << userCodeComment << "]\n}\n";
}
}
static bool needsButtonListener (Component* button)
{
return button->getProperties().getWithDefault ("generateListenerCallback", true);
}
static void setNeedsButtonListener (Component* button, const bool shouldDoCallback)
{
button->getProperties().set ("generateListenerCallback", shouldDoCallback);
}
private:
//==============================================================================
class ButtonTextProperty : public ComponentTextProperty <Button>
{
public:
ButtonTextProperty (Button* button_, JucerDocument& doc)
: ComponentTextProperty <Button> ("text", 100, false, button_, doc)
{
}
void setText (const String& newText) override
{
document.perform (new ButtonTextChangeAction (component, *document.getComponentLayout(), newText),
"Change button text");
}
String getText() const override
{
return component->getButtonText();
}
private:
class ButtonTextChangeAction : public ComponentUndoableAction <Button>
{
public:
ButtonTextChangeAction (Button* const comp, ComponentLayout& l, const String& newName_)
: ComponentUndoableAction <Button> (comp, l),
newName (newName_)
{
oldName = comp->getButtonText();
}
bool perform()
{
showCorrectTab();
getComponent()->setButtonText (newName);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setButtonText (oldName);
changed();
return true;
}
String newName, oldName;
};
};
class ButtonCallbackProperty : public ComponentBooleanProperty <Button>
{
public:
ButtonCallbackProperty (Button* b, JucerDocument& doc)
: ComponentBooleanProperty <Button> ("callback", "Generate ButtonListener", "Generate ButtonListener", b, doc)
{
}
void setState (bool newState)
{
document.perform (new ButtonCallbackChangeAction (component, *document.getComponentLayout(), newState),
"Change button callback");
}
bool getState() const { return needsButtonListener (component); }
private:
class ButtonCallbackChangeAction : public ComponentUndoableAction <Button>
{
public:
ButtonCallbackChangeAction (Button* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <Button> (comp, l),
newState (newState_)
{
oldState = needsButtonListener (comp);
}
bool perform()
{
showCorrectTab();
setNeedsButtonListener (getComponent(), newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
setNeedsButtonListener (getComponent(), oldState);
changed();
return true;
}
bool newState, oldState;
};
};
class ButtonRadioGroupProperty : public ComponentTextProperty <Button>
{
public:
ButtonRadioGroupProperty (Button* const button_, JucerDocument& doc)
: ComponentTextProperty <Button> ("radio group", 10, false, button_, doc)
{
}
void setText (const String& newText) override
{
document.perform (new ButtonRadioGroupChangeAction (component, *document.getComponentLayout(), newText.getIntValue()),
"Change radio group ID");
}
String getText() const override
{
return String (component->getRadioGroupId());
}
private:
class ButtonRadioGroupChangeAction : public ComponentUndoableAction <Button>
{
public:
ButtonRadioGroupChangeAction (Button* const comp, ComponentLayout& l, const int newId_)
: ComponentUndoableAction <Button> (comp, l),
newId (newId_)
{
oldId = comp->getRadioGroupId();
}
bool perform()
{
showCorrectTab();
getComponent()->setRadioGroupId (newId);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setRadioGroupId (oldId);
changed();
return true;
}
int newId, oldId;
};
};
class ButtonConnectedEdgeProperty : public ComponentBooleanProperty <Button>
{
public:
ButtonConnectedEdgeProperty (const String& name, const int flag_,
Button* b, JucerDocument& doc)
: ComponentBooleanProperty <Button> (name, "Connected", "Connected", b, doc),
flag (flag_)
{
}
void setState (bool newState)
{
document.perform (new ButtonConnectedChangeAction (component, *document.getComponentLayout(), flag, newState),
"Change button connected edges");
}
bool getState() const
{
return (component->getConnectedEdgeFlags() & flag) != 0;
}
private:
const int flag;
class ButtonConnectedChangeAction : public ComponentUndoableAction <Button>
{
public:
ButtonConnectedChangeAction (Button* const comp, ComponentLayout& l, const int flag_, const bool newState_)
: ComponentUndoableAction <Button> (comp, l),
flag (flag_),
newState (newState_)
{
oldState = ((comp->getConnectedEdgeFlags() & flag) != 0);
}
bool perform()
{
showCorrectTab();
if (newState)
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
else
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
changed();
return true;
}
bool undo()
{
showCorrectTab();
if (oldState)
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() | flag);
else
getComponent()->setConnectedEdges (getComponent()->getConnectedEdgeFlags() & ~flag);
changed();
return true;
}
const int flag;
bool newState, oldState;
};
};
};

View File

@ -0,0 +1,446 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class ComboBoxHandler : public ComponentTypeHandler
{
public:
ComboBoxHandler()
: ComponentTypeHandler ("Combo Box", "juce::ComboBox", typeid (ComboBox), 150, 24)
{}
Component* createNewComponent (JucerDocument*) override
{
return new ComboBox ("new combo box");
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
if (auto* const c = dynamic_cast<ComboBox*> (comp))
{
if (auto* e = ComponentTypeHandler::createXmlFor (comp, layout))
{
e->setAttribute ("editable", c->isTextEditable());
e->setAttribute ("layout", c->getJustificationType().getFlags());
e->setAttribute ("items", c->getProperties() ["items"].toString());
e->setAttribute ("textWhenNonSelected", c->getTextWhenNothingSelected());
e->setAttribute ("textWhenNoItems", c->getTextWhenNoChoicesAvailable());
return e;
}
}
return nullptr;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
ComboBox defaultBox;
if (ComboBox* const c = dynamic_cast<ComboBox*> (comp))
{
c->setEditableText (xml.getBoolAttribute ("editable", defaultBox.isTextEditable()));
c->setJustificationType (Justification (xml.getIntAttribute ("layout", defaultBox.getJustificationType().getFlags())));
c->getProperties().set ("items", xml.getStringAttribute ("items", String()));
c->setTextWhenNothingSelected (xml.getStringAttribute ("textWhenNonSelected", defaultBox.getTextWhenNothingSelected()));
c->setTextWhenNoChoicesAvailable (xml.getStringAttribute ("textWhenNoItems", defaultBox.getTextWhenNoChoicesAvailable()));
updateItems (c);
return true;
}
return false;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* c = dynamic_cast<ComboBox*> (component))
{
props.add (new ComboItemsProperty (c, document));
props.add (new ComboEditableProperty (c, document));
props.add (new ComboJustificationProperty (c, document));
props.add (new ComboTextWhenNoneSelectedProperty (c, document));
props.add (new ComboTextWhenNoItemsProperty (c, document));
}
}
String getCreationParameters (GeneratedCode&, Component* component) override
{
return quotedString (component->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
ComboBox* const c = dynamic_cast<ComboBox*> (component);
if (c == nullptr)
{
jassertfalse;
return;
}
String s;
s << memberVariableName << "->setEditableText (" << CodeHelpers::boolLiteral (c->isTextEditable()) << ");\n"
<< memberVariableName << "->setJustificationType (" << CodeHelpers::justificationToCode (c->getJustificationType()) << ");\n"
<< memberVariableName << "->setTextWhenNothingSelected (" << quotedString (c->getTextWhenNothingSelected(), code.shouldUseTransMacro()) << ");\n"
<< memberVariableName << "->setTextWhenNoChoicesAvailable (" << quotedString (c->getTextWhenNoChoicesAvailable(), code.shouldUseTransMacro()) << ");\n";
StringArray lines;
lines.addLines (c->getProperties() ["items"].toString());
int itemId = 1;
for (int i = 0; i < lines.size(); ++i)
{
if (lines[i].trim().isEmpty())
s << memberVariableName << "->addSeparator();\n";
else
s << memberVariableName << "->addItem ("
<< quotedString (lines[i], code.shouldUseTransMacro()) << ", " << itemId++ << ");\n";
}
if (needsCallback (component))
s << memberVariableName << "->addListener (this);\n";
s << '\n';
code.constructorCode += s;
}
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
{
ComponentTypeHandler::fillInGeneratedCode (component, code);
if (needsCallback (component))
{
String& callback = code.getCallbackCode ("public juce::ComboBox::Listener",
"void",
"comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)",
true);
if (callback.trim().isNotEmpty())
callback << "else ";
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
const String userCodeComment ("UserComboBoxCode_" + memberVariableName);
callback
<< "if (comboBoxThatHasChanged == " << memberVariableName << ".get())\n"
<< "{\n //[" << userCodeComment << "] -- add your combo box handling code here..\n //[/" << userCodeComment << "]\n}\n";
}
}
static void updateItems (ComboBox* c)
{
StringArray lines;
lines.addLines (c->getProperties() ["items"].toString());
c->clear();
int itemId = 1;
for (int i = 0; i < lines.size(); ++i)
{
if (lines[i].trim().isEmpty())
c->addSeparator();
else
c->addItem (lines[i], itemId++);
}
}
static bool needsCallback (Component*)
{
return true; // xxx should be configurable
}
private:
class ComboEditableProperty : public ComponentBooleanProperty <ComboBox>
{
public:
ComboEditableProperty (ComboBox* comp, JucerDocument& doc)
: ComponentBooleanProperty <ComboBox> ("editable", "Text is editable", "Text is editable", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new ComboEditableChangeAction (component, *document.getComponentLayout(), newState),
"Change combo box editability");
}
bool getState() const
{
return component->isTextEditable();
}
private:
class ComboEditableChangeAction : public ComponentUndoableAction <ComboBox>
{
public:
ComboEditableChangeAction (ComboBox* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <ComboBox> (comp, l),
newState (newState_)
{
oldState = comp->isTextEditable();
}
bool perform()
{
showCorrectTab();
getComponent()->setEditableText (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setEditableText (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class ComboJustificationProperty : public JustificationProperty
{
public:
ComboJustificationProperty (ComboBox* comp, JucerDocument& doc)
: JustificationProperty ("text layout", false),
component (comp),
document (doc)
{
}
void setJustification (Justification newJustification)
{
document.perform (new ComboJustifyChangeAction (component, *document.getComponentLayout(), newJustification),
"Change combo box justification");
}
Justification getJustification() const { return component->getJustificationType(); }
private:
ComboBox* const component;
JucerDocument& document;
class ComboJustifyChangeAction : public ComponentUndoableAction <ComboBox>
{
public:
ComboJustifyChangeAction (ComboBox* const comp, ComponentLayout& l, Justification newState_)
: ComponentUndoableAction <ComboBox> (comp, l),
newState (newState_),
oldState (comp->getJustificationType())
{
}
bool perform()
{
showCorrectTab();
getComponent()->setJustificationType (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setJustificationType (oldState);
changed();
return true;
}
Justification newState, oldState;
};
};
//==============================================================================
class ComboItemsProperty : public ComponentTextProperty <ComboBox>
{
public:
ComboItemsProperty (ComboBox* comp, JucerDocument& doc)
: ComponentTextProperty <ComboBox> ("items", 10000, true, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new ComboItemsChangeAction (component, *document.getComponentLayout(), newText),
"Change combo box items");
}
String getText() const override
{
return component->getProperties() ["items"];
}
private:
class ComboItemsChangeAction : public ComponentUndoableAction <ComboBox>
{
public:
ComboItemsChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <ComboBox> (comp, l),
newState (newState_)
{
oldState = comp->getProperties() ["items"];
}
bool perform()
{
showCorrectTab();
getComponent()->getProperties().set ("items", newState);
ComboBoxHandler::updateItems (getComponent());
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->getProperties().set ("items", oldState);
ComboBoxHandler::updateItems (getComponent());
changed();
return true;
}
String newState, oldState;
};
};
//==============================================================================
class ComboTextWhenNoneSelectedProperty : public ComponentTextProperty <ComboBox>
{
public:
ComboTextWhenNoneSelectedProperty (ComboBox* comp, JucerDocument& doc)
: ComponentTextProperty <ComboBox> ("text when none selected", 200, false, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new ComboNonSelTextChangeAction (component, *document.getComponentLayout(), newText),
"Change combo box text when nothing selected");
}
String getText() const override
{
return component->getTextWhenNothingSelected();
}
private:
class ComboNonSelTextChangeAction : public ComponentUndoableAction <ComboBox>
{
public:
ComboNonSelTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <ComboBox> (comp, l),
newState (newState_)
{
oldState = comp->getTextWhenNothingSelected();
}
bool perform()
{
showCorrectTab();
getComponent()->setTextWhenNothingSelected (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTextWhenNothingSelected (oldState);
changed();
return true;
}
String newState, oldState;
};
};
//==============================================================================
class ComboTextWhenNoItemsProperty : public ComponentTextProperty <ComboBox>
{
public:
ComboTextWhenNoItemsProperty (ComboBox* comp, JucerDocument& doc)
: ComponentTextProperty <ComboBox> ("text when no items", 200, false, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new ComboNoItemTextChangeAction (component, *document.getComponentLayout(), newText),
"Change combo box 'no items' text");
}
String getText() const override
{
return component->getTextWhenNoChoicesAvailable();
}
private:
class ComboNoItemTextChangeAction : public ComponentUndoableAction <ComboBox>
{
public:
ComboNoItemTextChangeAction (ComboBox* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <ComboBox> (comp, l),
newState (newState_)
{
oldState = comp->getTextWhenNoChoicesAvailable();
}
bool perform()
{
showCorrectTab();
getComponent()->setTextWhenNoChoicesAvailable (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTextWhenNoChoicesAvailable (oldState);
changed();
return true;
}
String newState, oldState;
};
};
};

View File

@ -0,0 +1,178 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
#include "jucer_ComponentTypeHandler.h"
#include "jucer_ComponentUndoableAction.h"
#include "../Properties/jucer_ComponentTextProperty.h"
//==============================================================================
class ComponentNameProperty : public ComponentTextProperty <Component>
{
public:
ComponentNameProperty (Component* comp, JucerDocument& doc)
: ComponentTextProperty <Component> ("name", 40, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new CompNameChangeAction (component, *document.getComponentLayout(), newText),
"Change component name");
}
String getText() const override
{
return component->getName();
}
private:
class CompNameChangeAction : public ComponentUndoableAction <Component>
{
public:
CompNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
: ComponentUndoableAction <Component> (comp, l),
newName (nm), oldName (comp->getName())
{
}
bool perform()
{
showCorrectTab();
getComponent()->setName (newName);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setName (oldName);
changed();
return true;
}
String newName, oldName;
};
};
//==============================================================================
class ComponentMemberNameProperty : public ComponentTextProperty <Component>
{
public:
ComponentMemberNameProperty (Component* comp, JucerDocument& doc)
: ComponentTextProperty <Component> ("member name", 40, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new CompMemberNameChangeAction (component, *document.getComponentLayout(), newText),
"Change component member name");
}
String getText() const override
{
return document.getComponentLayout()->getComponentMemberVariableName (component);
}
private:
class CompMemberNameChangeAction : public ComponentUndoableAction <Component>
{
public:
CompMemberNameChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
: ComponentUndoableAction <Component> (comp, l),
newName (nm), oldName (layout.getComponentMemberVariableName (comp))
{
}
bool perform()
{
showCorrectTab();
layout.setComponentMemberVariableName (getComponent(), newName);
return true;
}
bool undo()
{
showCorrectTab();
layout.setComponentMemberVariableName (getComponent(), oldName);
return true;
}
String newName, oldName;
};
};
//==============================================================================
class ComponentVirtualClassProperty : public ComponentTextProperty <Component>
{
public:
ComponentVirtualClassProperty (Component* comp, JucerDocument& doc)
: ComponentTextProperty <Component> ("virtual class", 40, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new CompVirtualClassChangeAction (component, *document.getComponentLayout(), newText),
"Change component virtual class name");
}
String getText() const override
{
return document.getComponentLayout()->getComponentVirtualClassName (component);
}
private:
class CompVirtualClassChangeAction : public ComponentUndoableAction <Component>
{
public:
CompVirtualClassChangeAction (Component* const comp, ComponentLayout& l, const String& nm)
: ComponentUndoableAction <Component> (comp, l),
newName (nm), oldName (layout.getComponentVirtualClassName (comp))
{
}
bool perform()
{
showCorrectTab();
layout.setComponentVirtualClassName (getComponent(), newName);
return true;
}
bool undo()
{
showCorrectTab();
layout.setComponentVirtualClassName (getComponent(), oldName);
return true;
}
String newName, oldName;
};
};

View File

@ -0,0 +1,638 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#include "../../Application/jucer_Headers.h"
#include "../../Application/jucer_Application.h"
#include "../jucer_ObjectTypes.h"
#include "../jucer_UtilityFunctions.h"
#include "../UI/jucer_JucerCommandIDs.h"
#include "../UI/jucer_ComponentOverlayComponent.h"
#include "jucer_ComponentNameProperty.h"
#include "../Properties/jucer_PositionPropertyBase.h"
#include "../Properties/jucer_ComponentColourProperty.h"
#include "../UI/jucer_TestComponent.h"
static String getTypeInfoName (const std::type_info& info)
{
#if JUCE_MSVC
return info.raw_name();
#else
return info.name();
#endif
}
//==============================================================================
ComponentTypeHandler::ComponentTypeHandler (const String& typeName_,
const String& className_,
const std::type_info& componentClass_,
const int defaultWidth_,
const int defaultHeight_)
: typeName (typeName_),
className (className_),
componentClassRawName (getTypeInfoName (componentClass_)),
defaultWidth (defaultWidth_),
defaultHeight (defaultHeight_)
{
}
Component* ComponentTypeHandler::createCopyOf (JucerDocument* document, Component& existing)
{
jassert (getHandlerFor (existing) == this);
Component* const newOne = createNewComponent (document);
std::unique_ptr<XmlElement> xml (createXmlFor (&existing, document->getComponentLayout()));
if (xml != nullptr)
restoreFromXml (*xml, newOne, document->getComponentLayout());
return newOne;
}
ComponentOverlayComponent* ComponentTypeHandler::createOverlayComponent (Component* child, ComponentLayout& layout)
{
return new ComponentOverlayComponent (child, layout);
}
static void dummyMenuCallback (int, int) {}
void ComponentTypeHandler::showPopupMenu (Component*, ComponentLayout& layout)
{
PopupMenu m;
ApplicationCommandManager* commandManager = &ProjucerApplication::getCommandManager();
m.addCommandItem (commandManager, JucerCommandIDs::toFront);
m.addCommandItem (commandManager, JucerCommandIDs::toBack);
m.addSeparator();
if (layout.getSelectedSet().getNumSelected() > 1)
{
m.addCommandItem (commandManager, JucerCommandIDs::alignTop);
m.addCommandItem (commandManager, JucerCommandIDs::alignRight);
m.addCommandItem (commandManager, JucerCommandIDs::alignBottom);
m.addCommandItem (commandManager, JucerCommandIDs::alignLeft);
m.addSeparator();
}
m.addCommandItem (commandManager, StandardApplicationCommandIDs::cut);
m.addCommandItem (commandManager, StandardApplicationCommandIDs::copy);
m.addCommandItem (commandManager, StandardApplicationCommandIDs::paste);
m.addCommandItem (commandManager, StandardApplicationCommandIDs::del);
m.showMenuAsync (PopupMenu::Options(),
ModalCallbackFunction::create (dummyMenuCallback, 0));
}
JucerDocument* ComponentTypeHandler::findParentDocument (Component* component)
{
Component* p = component->getParentComponent();
while (p != nullptr)
{
if (JucerDocumentEditor* const ed = dynamic_cast<JucerDocumentEditor*> (p))
return ed->getDocument();
if (TestComponent* const t = dynamic_cast<TestComponent*> (p))
return t->getDocument();
p = p->getParentComponent();
}
return nullptr;
}
//==============================================================================
bool ComponentTypeHandler::canHandle (Component& component) const
{
return componentClassRawName == getTypeInfoName (typeid (component));
}
ComponentTypeHandler* ComponentTypeHandler::getHandlerFor (Component& component)
{
for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
if (ObjectTypes::componentTypeHandlers[i]->canHandle (component))
return ObjectTypes::componentTypeHandlers[i];
jassertfalse;
return nullptr;
}
ComponentTypeHandler* ComponentTypeHandler::getHandlerForXmlTag (const String& tagName)
{
for (int i = 0; i < ObjectTypes::numComponentTypes; ++i)
if (ObjectTypes::componentTypeHandlers[i]->getXmlTagName().equalsIgnoreCase (tagName))
return ObjectTypes::componentTypeHandlers[i];
return nullptr;
}
XmlElement* ComponentTypeHandler::createXmlFor (Component* comp, const ComponentLayout* layout)
{
XmlElement* e = new XmlElement (getXmlTagName());
e->setAttribute ("name", comp->getName());
e->setAttribute ("id", String::toHexString (getComponentId (comp)));
e->setAttribute ("memberName", comp->getProperties() ["memberName"].toString());
e->setAttribute ("virtualName", comp->getProperties() ["virtualName"].toString());
e->setAttribute ("explicitFocusOrder", comp->getExplicitFocusOrder());
RelativePositionedRectangle pos (getComponentPosition (comp));
pos.updateFromComponent (*comp, layout);
pos.applyToXml (*e);
if (SettableTooltipClient* const ttc = dynamic_cast<SettableTooltipClient*> (comp))
if (ttc->getTooltip().isNotEmpty())
e->setAttribute ("tooltip", ttc->getTooltip());
for (int i = 0; i < colours.size(); ++i)
{
if (comp->isColourSpecified (colours[i]->colourId))
{
e->setAttribute (colours[i]->xmlTagName,
comp->findColour (colours[i]->colourId).toString());
}
}
return e;
}
bool ComponentTypeHandler::restoreFromXml (const XmlElement& xml,
Component* comp,
const ComponentLayout* layout)
{
jassert (xml.hasTagName (getXmlTagName()));
if (! xml.hasTagName (getXmlTagName()))
return false;
comp->setName (xml.getStringAttribute ("name", comp->getName()));
setComponentId (comp, xml.getStringAttribute ("id").getHexValue64());
comp->getProperties().set ("memberName", xml.getStringAttribute ("memberName"));
comp->getProperties().set ("virtualName", xml.getStringAttribute ("virtualName"));
comp->setExplicitFocusOrder (xml.getIntAttribute ("explicitFocusOrder"));
RelativePositionedRectangle currentPos (getComponentPosition (comp));
currentPos.updateFromComponent (*comp, layout);
RelativePositionedRectangle rpr;
rpr.restoreFromXml (xml, currentPos);
jassert (layout != nullptr);
setComponentPosition (comp, rpr, layout);
if (SettableTooltipClient* const ttc = dynamic_cast<SettableTooltipClient*> (comp))
ttc->setTooltip (xml.getStringAttribute ("tooltip"));
for (int i = 0; i < colours.size(); ++i)
{
const String col (xml.getStringAttribute (colours[i]->xmlTagName, String()));
if (col.isNotEmpty())
comp->setColour (colours[i]->colourId, Colour::fromString (col));
}
return true;
}
//==============================================================================
int64 ComponentTypeHandler::getComponentId (Component* comp)
{
if (comp == nullptr)
return 0;
int64 compId = comp->getProperties() ["jucerCompId"].toString().getHexValue64();
if (compId == 0)
{
compId = Random::getSystemRandom().nextInt64();
setComponentId (comp, compId);
}
return compId;
}
void ComponentTypeHandler::setComponentId (Component* comp, const int64 newID)
{
jassert (comp != nullptr);
if (newID != 0)
comp->getProperties().set ("jucerCompId", String::toHexString (newID));
}
RelativePositionedRectangle ComponentTypeHandler::getComponentPosition (Component* comp)
{
RelativePositionedRectangle rp;
rp.rect = PositionedRectangle (comp->getProperties() ["pos"]);
rp.relativeToX = comp->getProperties() ["relativeToX"].toString().getHexValue64();
rp.relativeToY = comp->getProperties() ["relativeToY"].toString().getHexValue64();
rp.relativeToW = comp->getProperties() ["relativeToW"].toString().getHexValue64();
rp.relativeToH = comp->getProperties() ["relativeToH"].toString().getHexValue64();
return rp;
}
void ComponentTypeHandler::setComponentPosition (Component* comp,
const RelativePositionedRectangle& newPos,
const ComponentLayout* layout)
{
comp->getProperties().set ("pos", newPos.rect.toString());
comp->getProperties().set ("relativeToX", String::toHexString (newPos.relativeToX));
comp->getProperties().set ("relativeToY", String::toHexString (newPos.relativeToY));
comp->getProperties().set ("relativeToW", String::toHexString (newPos.relativeToW));
comp->getProperties().set ("relativeToH", String::toHexString (newPos.relativeToH));
comp->setBounds (newPos.getRectangle (Rectangle<int> (0, 0, comp->getParentWidth(), comp->getParentHeight()),
layout));
}
//==============================================================================
class TooltipProperty : public ComponentTextProperty <Component>
{
public:
TooltipProperty (Component* comp, JucerDocument& doc)
: ComponentTextProperty<Component> ("tooltip", 1024, true, comp, doc)
{
}
String getText() const override
{
SettableTooltipClient* ttc = dynamic_cast<SettableTooltipClient*> (component);
return ttc->getTooltip();
}
void setText (const String& newText) override
{
document.perform (new SetTooltipAction (component, *document.getComponentLayout(), newText),
"Change tooltip");
}
private:
class SetTooltipAction : public ComponentUndoableAction <Component>
{
public:
SetTooltipAction (Component* const comp, ComponentLayout& l, const String& newValue_)
: ComponentUndoableAction<Component> (comp, l),
newValue (newValue_)
{
SettableTooltipClient* ttc = dynamic_cast<SettableTooltipClient*> (comp);
jassert (ttc != nullptr);
oldValue = ttc->getTooltip();
}
bool perform()
{
showCorrectTab();
if (SettableTooltipClient* ttc = dynamic_cast<SettableTooltipClient*> (getComponent()))
{
ttc->setTooltip (newValue);
changed();
return true;
}
return false;
}
bool undo()
{
showCorrectTab();
if (SettableTooltipClient* ttc = dynamic_cast<SettableTooltipClient*> (getComponent()))
{
ttc->setTooltip (oldValue);
changed();
return true;
}
return false;
}
String newValue, oldValue;
};
};
//==============================================================================
class ComponentPositionProperty : public PositionPropertyBase
{
public:
ComponentPositionProperty (Component* comp,
JucerDocument& doc,
const String& name,
ComponentPositionDimension dimension_)
: PositionPropertyBase (comp, name, dimension_,
true, true,
doc.getComponentLayout()),
document (doc)
{
document.addChangeListener (this);
}
~ComponentPositionProperty()
{
document.removeChangeListener (this);
}
void setPosition (const RelativePositionedRectangle& newPos)
{
auto* l = document.getComponentLayout();
if (l->getSelectedSet().getNumSelected() > 1)
positionOtherSelectedComponents (ComponentTypeHandler::getComponentPosition (component), newPos);
l->setComponentPosition (component, newPos, true);
}
RelativePositionedRectangle getPosition() const
{
return ComponentTypeHandler::getComponentPosition (component);
}
private:
JucerDocument& document;
void positionOtherSelectedComponents (const RelativePositionedRectangle& oldPos, const RelativePositionedRectangle& newPos)
{
for (auto* s : document.getComponentLayout()->getSelectedSet())
{
if (s != component)
{
auto currentPos = ComponentTypeHandler::getComponentPosition (s);
auto diff = 0.0;
if (dimension == ComponentPositionDimension::componentX)
{
diff = newPos.rect.getX() - oldPos.rect.getX();
currentPos.rect.setX (currentPos.rect.getX() + diff);
}
else if (dimension == ComponentPositionDimension::componentY)
{
diff = newPos.rect.getY() - oldPos.rect.getY();
currentPos.rect.setY (currentPos.rect.getY() + diff);
}
else if (dimension == ComponentPositionDimension::componentWidth)
{
diff = newPos.rect.getWidth() - oldPos.rect.getWidth();
currentPos.rect.setWidth (currentPos.rect.getWidth() + diff);
}
else if (dimension == ComponentPositionDimension::componentHeight)
{
diff = newPos.rect.getHeight() - oldPos.rect.getHeight();
currentPos.rect.setHeight (currentPos.rect.getHeight() + diff);
}
document.getComponentLayout()->setComponentPosition (s, currentPos, true);
}
}
}
};
//==============================================================================
class FocusOrderProperty : public ComponentTextProperty <Component>
{
public:
FocusOrderProperty (Component* comp, JucerDocument& doc)
: ComponentTextProperty <Component> ("focus order", 8, false, comp, doc)
{
}
String getText() const override
{
return String (component->getExplicitFocusOrder());
}
void setText (const String& newText) override
{
document.perform (new SetFocusOrderAction (component, *document.getComponentLayout(), jmax (0, newText.getIntValue())),
"Change focus order");
}
private:
class SetFocusOrderAction : public ComponentUndoableAction <Component>
{
public:
SetFocusOrderAction (Component* const comp, ComponentLayout& l, const int newOrder_)
: ComponentUndoableAction <Component> (comp, l),
newValue (newOrder_)
{
oldValue = comp->getExplicitFocusOrder();
}
bool perform()
{
showCorrectTab();
getComponent()->setExplicitFocusOrder (newValue);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setExplicitFocusOrder (oldValue);
changed();
return true;
}
int newValue, oldValue;
};
};
//==============================================================================
void ComponentTypeHandler::getEditableProperties (Component* component,
JucerDocument& document,
Array<PropertyComponent*>& props,
bool multipleSelected)
{
if (! multipleSelected)
{
props.add (new ComponentMemberNameProperty (component, document));
props.add (new ComponentNameProperty (component, document));
props.add (new ComponentVirtualClassProperty (component, document));
if (dynamic_cast<SettableTooltipClient*> (component) != nullptr)
props.add (new TooltipProperty (component, document));
props.add (new FocusOrderProperty (component, document));
}
props.add (new ComponentPositionProperty (component, document, "x", ComponentPositionProperty::componentX));
props.add (new ComponentPositionProperty (component, document, "y", ComponentPositionProperty::componentY));
props.add (new ComponentPositionProperty (component, document, "width", ComponentPositionProperty::componentWidth));
props.add (new ComponentPositionProperty (component, document, "height", ComponentPositionProperty::componentHeight));
}
void ComponentTypeHandler::addPropertiesToPropertyPanel (Component* comp, JucerDocument& document,
PropertyPanel& panel, bool multipleSelected)
{
Array <PropertyComponent*> props;
getEditableProperties (comp, document, props, multipleSelected);
panel.addSection (getClassName (comp), props);
}
void ComponentTypeHandler::registerEditableColour (int colourId,
const String& colourIdCode,
const String& colourName, const String& xmlTagName)
{
ComponentColourInfo* const c = new ComponentColourInfo();
c->colourId = colourId;
c->colourIdCode = colourIdCode;
c->colourName = colourName;
c->xmlTagName = xmlTagName;
colours.add (c);
}
void ComponentTypeHandler::addColourProperties (Component* component,
JucerDocument& document,
Array<PropertyComponent*>& props)
{
for (int i = 0; i < colours.size(); ++i)
props.add (new ComponentColourIdProperty (component, document,
colours[i]->colourId,
colours[i]->colourName,
true));
}
String ComponentTypeHandler::getColourIntialisationCode (Component* component,
const String& objectName)
{
String s;
for (int i = 0; i < colours.size(); ++i)
{
if (component->isColourSpecified (colours[i]->colourId))
{
s << objectName << "->setColour ("
<< colours[i]->colourIdCode
<< ", "
<< CodeHelpers::colourToCode (component->findColour (colours[i]->colourId))
<< ");\n";
}
}
return s;
}
//==============================================================================
void ComponentTypeHandler::fillInGeneratedCode (Component* component, GeneratedCode& code)
{
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
fillInMemberVariableDeclarations (code, component, memberVariableName);
fillInCreationCode (code, component, memberVariableName);
fillInDeletionCode (code, component, memberVariableName);
fillInResizeCode (code, component, memberVariableName);
}
void ComponentTypeHandler::fillInMemberVariableDeclarations (GeneratedCode& code, Component* component, const String& memberVariableName)
{
String clsName (component->getProperties() ["virtualName"].toString());
if (clsName.isNotEmpty())
clsName = build_tools::makeValidIdentifier (clsName, false, false, true);
else
clsName = getClassName (component);
code.privateMemberDeclarations
<< "std::unique_ptr<" << clsName << "> " << memberVariableName << ";\n";
}
void ComponentTypeHandler::fillInResizeCode (GeneratedCode& code, Component* component, const String& memberVariableName)
{
const RelativePositionedRectangle pos (getComponentPosition (component));
String x, y, w, h, r;
positionToCode (pos, code.document->getComponentLayout(), x, y, w, h);
r << memberVariableName << "->setBounds ("
<< x << ", " << y << ", " << w << ", " << h << ");\n";
if (pos.rect.isPositionAbsolute() && ! code.document->getComponentLayout()->isComponentPositionRelative (component))
code.constructorCode += r + "\n";
else
code.getCallbackCode (String(), "void", "resized()", false) += r;
}
String ComponentTypeHandler::getCreationParameters (GeneratedCode&, Component*)
{
return {};
}
void ComponentTypeHandler::fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName)
{
String params (getCreationParameters (code, component));
const String virtualName (component->getProperties() ["virtualName"].toString());
String s;
s << memberVariableName << ".reset (new ";
if (virtualName.isNotEmpty())
s << build_tools::makeValidIdentifier (virtualName, false, false, true);
else
s << getClassName (component);
if (params.isEmpty())
{
s << "());\n";
}
else
{
StringArray lines;
lines.addLines (params);
params = lines.joinIntoString ("\n" + String::repeatedString (" ", s.length() + 2));
s << " (" << params << "));\n";
}
s << "addAndMakeVisible (" << memberVariableName << ".get());\n";
if (auto* ttc = dynamic_cast<SettableTooltipClient*> (component))
{
if (ttc->getTooltip().isNotEmpty())
{
s << memberVariableName << "->setTooltip ("
<< quotedString (ttc->getTooltip(), code.shouldUseTransMacro())
<< ");\n";
}
}
if (component != nullptr && component->getExplicitFocusOrder() > 0)
s << memberVariableName << "->setExplicitFocusOrder ("
<< component->getExplicitFocusOrder()
<< ");\n";
code.constructorCode += s;
}
void ComponentTypeHandler::fillInDeletionCode (GeneratedCode& code, Component*,
const String& memberVariableName)
{
code.destructorCode
<< memberVariableName << " = nullptr;\n";
}

View File

@ -0,0 +1,147 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
class ComponentOverlayComponent;
class ComponentLayout;
#include "../jucer_GeneratedCode.h"
#include "../UI/jucer_RelativePositionedRectangle.h"
//==============================================================================
/**
Base class for handlers that can understand the properties of all the component classes.
*/
class ComponentTypeHandler
{
public:
//==============================================================================
ComponentTypeHandler (const String& typeDescription_,
const String& className_,
const std::type_info& componentClass,
const int defaultWidth_,
const int defaultHeight_);
virtual ~ComponentTypeHandler() {}
//==============================================================================
virtual bool canHandle (Component& component) const;
static ComponentTypeHandler* getHandlerFor (Component& component);
//==============================================================================
virtual String getXmlTagName() const noexcept
{
if (className.startsWith ("juce::"))
return className.substring (6).toUpperCase();
return className.toUpperCase();
}
static ComponentTypeHandler* getHandlerForXmlTag (const String& tagName);
virtual XmlElement* createXmlFor (Component* component, const ComponentLayout* layout);
virtual bool restoreFromXml (const XmlElement& xml, Component* component, const ComponentLayout* layout);
virtual void getEditableProperties (Component* component,
JucerDocument& document,
Array<PropertyComponent*>& props,
bool multipleSelected);
virtual void addPropertiesToPropertyPanel (Component* component,
JucerDocument& document,
PropertyPanel& panel,
bool multipleSelected);
void registerEditableColour (int colourId,
const String& colourIdCode,
const String& colourName,
const String& xmlTagName);
#define registerColour(colourId, colourName, xmlTagName) \
registerEditableColour (colourId, #colourId, colourName, xmlTagName)
void addColourProperties (Component* component,
JucerDocument& document,
Array<PropertyComponent*>& props);
String getColourIntialisationCode (Component* component,
const String& objectName);
//==============================================================================
virtual Component* createNewComponent (JucerDocument*) = 0;
virtual Component* createCopyOf (JucerDocument*, Component& existing);
virtual ComponentOverlayComponent* createOverlayComponent (Component* child, ComponentLayout& layout);
virtual void showPopupMenu (Component* component,
ComponentLayout& layout);
//==============================================================================
// Code-generation methods:
virtual void fillInGeneratedCode (Component* component, GeneratedCode& code);
virtual void fillInMemberVariableDeclarations (GeneratedCode&, Component*, const String& memberVariableName);
virtual void fillInResizeCode (GeneratedCode&, Component*, const String& memberVariableName);
virtual void fillInCreationCode (GeneratedCode&, Component*, const String& memberVariableName);
virtual String getCreationParameters (GeneratedCode&, Component*);
virtual void fillInDeletionCode (GeneratedCode&, Component*, const String& memberVariableName);
//==============================================================================
const String& getTypeName() const noexcept { return typeName; }
virtual String getClassName (Component*) const { return className; }
int getDefaultWidth() const noexcept { return defaultWidth; }
int getDefaultHeight() const noexcept { return defaultHeight; }
static int64 getComponentId (Component* comp);
static void setComponentId (Component* comp, const int64 newID);
static RelativePositionedRectangle getComponentPosition (Component* comp);
static void setComponentPosition (Component* comp,
const RelativePositionedRectangle& newPos,
const ComponentLayout* layout);
static JucerDocument* findParentDocument (Component* component);
protected:
//==============================================================================
const String typeName, className, virtualClass, componentClassRawName;
int defaultWidth, defaultHeight;
struct ComponentColourInfo
{
int colourId;
String colourIdCode, colourName, xmlTagName;
};
OwnedArray<ComponentColourInfo> colours;
private:
JUCE_DECLARE_NON_COPYABLE (ComponentTypeHandler)
};

View File

@ -0,0 +1,75 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
#include "../UI/jucer_JucerDocumentEditor.h"
//==============================================================================
template <class ComponentType>
class ComponentUndoableAction : public UndoableAction
{
public:
ComponentUndoableAction (ComponentType* const comp,
ComponentLayout& layout_)
: layout (layout_),
componentIndex (layout_.indexOfComponent (comp))
{
jassert (comp != nullptr);
jassert (componentIndex >= 0);
}
ComponentType* getComponent() const
{
ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex));
jassert (c != nullptr);
return c;
}
int getSizeInUnits() { return 2; }
protected:
ComponentLayout& layout;
const int componentIndex;
void changed() const
{
jassert (layout.getDocument() != nullptr);
layout.getDocument()->changed();
}
void showCorrectTab() const
{
if (JucerDocumentEditor* const ed = JucerDocumentEditor::getActiveDocumentHolder())
ed->showLayout();
if (layout.getSelectedSet().getNumSelected() == 0)
if (ComponentType* const c = dynamic_cast<ComponentType*> (layout.getComponent (componentIndex)))
layout.getSelectedSet().selectOnly (getComponent());
}
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentUndoableAction)
};

View File

@ -0,0 +1,241 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class GenericComponent : public Component
{
public:
GenericComponent()
: Component ("new component"),
actualClassName ("juce::Component")
{
}
void paint (Graphics& g) override
{
g.fillAll (Colours::white.withAlpha (0.25f));
g.setColour (Colours::black.withAlpha (0.5f));
g.drawRect (getLocalBounds());
g.drawLine (0.0f, 0.0f, (float) getWidth(), (float) getHeight());
g.drawLine (0.0f, (float) getHeight(), (float) getWidth(), 0.0f);
g.setFont (14.0f);
g.drawText (actualClassName, 0, 0, getWidth(), getHeight() / 2, Justification::centred, true);
}
void setClassName (const String& newName)
{
if (actualClassName != newName)
{
actualClassName = newName;
repaint();
}
}
void setParams (const String& newParams)
{
if (constructorParams != newParams)
{
constructorParams = newParams;
repaint();
}
}
String actualClassName, constructorParams;
};
//==============================================================================
class GenericComponentHandler : public ComponentTypeHandler
{
public:
GenericComponentHandler()
: ComponentTypeHandler ("Generic Component", "GenericComponent", typeid (GenericComponent), 150, 24)
{}
Component* createNewComponent (JucerDocument*) override
{
return new GenericComponent();
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("class", ((GenericComponent*) comp)->actualClassName);
e->setAttribute ("params", ((GenericComponent*) comp)->constructorParams);
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
((GenericComponent*) comp)->actualClassName = xml.getStringAttribute ("class", "juce::Component");
((GenericComponent*) comp)->constructorParams = xml.getStringAttribute ("params", String());
return true;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
props.add (new GenericCompClassProperty (dynamic_cast<GenericComponent*> (component), document));
props.add (new GenericCompParamsProperty (dynamic_cast<GenericComponent*> (component), document));
}
String getClassName (Component* comp) const override
{
return static_cast<GenericComponent*> (comp)->actualClassName;
}
String getCreationParameters (GeneratedCode&, Component* comp) override
{
return static_cast<GenericComponent*> (comp)->constructorParams;
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
if (component->getName().isNotEmpty())
code.constructorCode
<< memberVariableName << "->setName ("
<< quotedString (component->getName(), false)
<< ");\n\n";
else
code.constructorCode << "\n";
}
private:
class GenericCompClassProperty : public ComponentTextProperty <GenericComponent>
{
public:
GenericCompClassProperty (GenericComponent* comp, JucerDocument& doc)
: ComponentTextProperty <GenericComponent> ("class", 300, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new GenericCompClassChangeAction (component, *document.getComponentLayout(),
build_tools::makeValidIdentifier (newText, false, false, true)),
"Change generic component class");
}
String getText() const override
{
return component->actualClassName;
}
private:
class GenericCompClassChangeAction : public ComponentUndoableAction <GenericComponent>
{
public:
GenericCompClassChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <GenericComponent> (comp, l),
newState (newState_)
{
oldState = comp->actualClassName;
}
bool perform()
{
showCorrectTab();
getComponent()->setClassName (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setClassName (oldState);
changed();
return true;
}
String newState, oldState;
};
};
class GenericCompParamsProperty : public ComponentTextProperty <GenericComponent>
{
public:
GenericCompParamsProperty (GenericComponent* comp, JucerDocument& doc)
: ComponentTextProperty <GenericComponent> ("constructor params", 1024, true, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new GenericCompParamsChangeAction (component, *document.getComponentLayout(), newText),
"Change generic component class");
}
String getText() const override
{
return component->constructorParams;
}
private:
class GenericCompParamsChangeAction : public ComponentUndoableAction <GenericComponent>
{
public:
GenericCompParamsChangeAction (GenericComponent* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <GenericComponent> (comp, l),
newState (newState_)
{
oldState = comp->constructorParams;
}
bool perform()
{
showCorrectTab();
getComponent()->setParams (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setParams (oldState);
changed();
return true;
}
String newState, oldState;
};
};
};

View File

@ -0,0 +1,237 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class GroupComponentHandler : public ComponentTypeHandler
{
public:
GroupComponentHandler()
: ComponentTypeHandler ("Group Box", "juce::GroupComponent", typeid (GroupComponent), 200, 150)
{
registerColour (juce::GroupComponent::outlineColourId, "outline", "outlinecol");
registerColour (juce::GroupComponent::textColourId, "text", "textcol");
}
Component* createNewComponent (JucerDocument*) override
{
return new GroupComponent ("new group", "group");
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
GroupComponent* const g = (GroupComponent*) comp;
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("title", g->getText());
GroupComponent defaultComp;
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
e->setAttribute ("textpos", g->getTextLabelPosition().getFlags());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
GroupComponent* const g = (GroupComponent*) comp;
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
g->setText (xml.getStringAttribute ("title", g->getText()));
g->setTextLabelPosition (Justification (xml.getIntAttribute ("textpos", g->getTextLabelPosition().getFlags())));
return true;
}
String getCreationParameters (GeneratedCode& code, Component* component) override
{
GroupComponent* g = dynamic_cast<GroupComponent*> (component);
return quotedString (component->getName(), false)
+ ",\n"
+ quotedString (g->getText(), code.shouldUseTransMacro());
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
GroupComponent* const g = dynamic_cast<GroupComponent*> (component);
String s;
GroupComponent defaultComp;
if (g->getTextLabelPosition().getFlags() != defaultComp.getTextLabelPosition().getFlags())
{
s << memberVariableName << "->setTextLabelPosition ("
<< CodeHelpers::justificationToCode (g->getTextLabelPosition())
<< ");\n";
}
s << getColourIntialisationCode (component, memberVariableName)
<< '\n';
code.constructorCode += s;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* gc = dynamic_cast<GroupComponent*> (component))
{
props.add (new GroupTitleProperty (gc, document));
props.add (new GroupJustificationProperty (gc, document));
}
addColourProperties (component, document, props);
}
private:
//==============================================================================
class GroupTitleProperty : public ComponentTextProperty <GroupComponent>
{
public:
GroupTitleProperty (GroupComponent* comp, JucerDocument& doc)
: ComponentTextProperty <GroupComponent> ("text", 200, false, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new GroupTitleChangeAction (component, *document.getComponentLayout(), newText),
"Change group title");
}
String getText() const override
{
return component->getText();
}
private:
class GroupTitleChangeAction : public ComponentUndoableAction <GroupComponent>
{
public:
GroupTitleChangeAction (GroupComponent* const comp, ComponentLayout& l, const String& newName_)
: ComponentUndoableAction <GroupComponent> (comp, l),
newName (newName_)
{
oldName = comp->getText();
}
bool perform()
{
showCorrectTab();
getComponent()->setText (newName);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setText (oldName);
changed();
return true;
}
String newName, oldName;
};
};
//==============================================================================
class GroupJustificationProperty : public JustificationProperty,
private ChangeListener
{
public:
GroupJustificationProperty (GroupComponent* const group_, JucerDocument& doc)
: JustificationProperty ("layout", true),
group (group_),
document (doc)
{
document.addChangeListener (this);
}
~GroupJustificationProperty() override
{
document.removeChangeListener (this);
}
void setJustification (Justification newJustification) override
{
document.perform (new GroupJustifyChangeAction (group, *document.getComponentLayout(), newJustification),
"Change text label position");
}
Justification getJustification() const override
{
return group->getTextLabelPosition();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
GroupComponent* const group;
JucerDocument& document;
class GroupJustifyChangeAction : public ComponentUndoableAction <GroupComponent>
{
public:
GroupJustifyChangeAction (GroupComponent* const comp, ComponentLayout& l, Justification newState_)
: ComponentUndoableAction <GroupComponent> (comp, l),
newState (newState_),
oldState (comp->getTextLabelPosition())
{
}
bool perform()
{
showCorrectTab();
getComponent()->setTextLabelPosition (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTextLabelPosition (oldState);
changed();
return true;
}
Justification newState, oldState;
};
};
};

View File

@ -0,0 +1,149 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class HyperlinkButtonHandler : public ButtonHandler
{
public:
HyperlinkButtonHandler()
: ButtonHandler ("Hyperlink Button", "juce::HyperlinkButton", typeid (HyperlinkButton), 150, 24)
{
registerColour (juce::HyperlinkButton::textColourId, "text", "textCol");
}
Component* createNewComponent (JucerDocument*) override
{
HyperlinkButton* hb = new HyperlinkButton ("new hyperlink", URL ("http://www.juce.com"));
setNeedsButtonListener (hb, false);
return hb;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* hb = dynamic_cast<HyperlinkButton*> (component))
props.add (new HyperlinkURLProperty (hb, document));
addColourProperties (component, document, props);
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
HyperlinkButton* const hb = (HyperlinkButton*) comp;
XmlElement* const e = ButtonHandler::createXmlFor (comp, layout);
e->setAttribute ("url", hb->getURL().toString (false));
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
HyperlinkButton* const hb = (HyperlinkButton*) comp;
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
return false;
hb->setURL (URL (xml.getStringAttribute ("url", hb->getURL().toString (false))));
return true;
}
String getCreationParameters (GeneratedCode& code, Component* comp) override
{
HyperlinkButton* const hb = dynamic_cast<HyperlinkButton*> (comp);
return quotedString (hb->getButtonText(), code.shouldUseTransMacro())
+ ",\nURL ("
+ quotedString (hb->getURL().toString (false), false)
+ ")";
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
code.constructorCode << getColourIntialisationCode (component, memberVariableName)
<< '\n';
}
private:
//==============================================================================
class HyperlinkURLProperty : public ComponentTextProperty <HyperlinkButton>
{
public:
HyperlinkURLProperty (HyperlinkButton* comp, JucerDocument& doc)
: ComponentTextProperty <HyperlinkButton> ("URL", 512, false, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new HyperlinkURLChangeAction (component, *document.getComponentLayout(), URL::createWithoutParsing (newText)),
"Change hyperlink URL");
}
String getText() const override
{
return component->getURL().toString (false);
}
private:
class HyperlinkURLChangeAction : public ComponentUndoableAction <HyperlinkButton>
{
public:
HyperlinkURLChangeAction (HyperlinkButton* const comp, ComponentLayout& l, const URL& newState_)
: ComponentUndoableAction <HyperlinkButton> (comp, l),
newState (newState_)
{
oldState = comp->getURL();
}
bool perform()
{
showCorrectTab();
getComponent()->setURL (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setURL (oldState);
changed();
return true;
}
URL newState, oldState;
};
};
};

View File

@ -0,0 +1,526 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class ImageButtonHandler : public ButtonHandler
{
public:
enum ImageRole
{
normalImage = 0,
overImage = 1,
downImage = 2
};
//==============================================================================
ImageButtonHandler()
: ButtonHandler ("Image Button", "juce::ImageButton", typeid (ImageButton), 150, 24)
{
}
Component* createNewComponent (JucerDocument*) override
{
return new ImageButton ("new button");
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
addColourProperties (component, document, props);
if (auto* ib = dynamic_cast<ImageButton*> (component))
{
auto& layout = *document.getComponentLayout();
props.add (new ImageButtonProportionProperty (layout, ib));
props.add (new ImageButtonResourceProperty (layout, ib, normalImage, "normal image"));
props.add (new ImageButtonOpacityProperty (layout, ib, "opacity", normalImage));
props.add (new ImageButtonColourProperty (layout, ib, "overlay col.", normalImage));
props.add (new ImageButtonResourceProperty (layout, ib, overImage, "over image"));
props.add (new ImageButtonOpacityProperty (layout, ib, "opacity", overImage));
props.add (new ImageButtonColourProperty (layout, ib, "overlay col.", overImage));
props.add (new ImageButtonResourceProperty (layout, ib, downImage, "down image"));
props.add (new ImageButtonOpacityProperty (layout, ib, "opacity", downImage));
props.add (new ImageButtonColourProperty (layout, ib, "overlay col.", downImage));
}
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
XmlElement* e = ButtonHandler::createXmlFor (comp, layout);
ImageButton* const ib = (ImageButton*) comp;
e->setAttribute ("keepProportions", doesImageKeepProportions (ib));
e->setAttribute ("resourceNormal", getImageResource (ib, normalImage));
e->setAttribute ("opacityNormal", getImageOpacity (ib, normalImage));
e->setAttribute ("colourNormal", getImageColour (ib, normalImage).toString());
e->setAttribute ("resourceOver", getImageResource (ib, overImage));
e->setAttribute ("opacityOver", getImageOpacity (ib, overImage));
e->setAttribute ("colourOver", getImageColour (ib, overImage).toString());
e->setAttribute ("resourceDown", getImageResource (ib, downImage));
e->setAttribute ("opacityDown", getImageOpacity (ib, downImage));
e->setAttribute ("colourDown", getImageColour (ib, downImage).toString());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
return false;
ImageButton* const ib = (ImageButton*) comp;
ComponentLayout& l = const_cast<ComponentLayout&> (*layout);
setImageKeepProportions (l, ib, xml.getBoolAttribute ("keepProportions", true), false);
setImageResource (l, ib, normalImage, xml.getStringAttribute ("resourceNormal", String()), false);
setImageOpacity (l, ib, normalImage, (float) xml.getDoubleAttribute ("opacityNormal", 1.0f), false);
setImageColour (l, ib, normalImage, Colour::fromString (xml.getStringAttribute ("colourNormal", "0")), false);
setImageResource (l, ib, overImage, xml.getStringAttribute ("resourceOver", String()), false);
setImageOpacity (l, ib, overImage, (float) xml.getDoubleAttribute ("opacityOver", 1.0f), false);
setImageColour (l, ib, overImage, Colour::fromString (xml.getStringAttribute ("colourOver", "0")), false);
setImageResource (l, ib, downImage, xml.getStringAttribute ("resourceDown", String()), false);
setImageOpacity (l, ib, downImage, (float) xml.getDoubleAttribute ("opacityDown", 1.0f), false);
setImageColour (l, ib, downImage, Colour::fromString (xml.getStringAttribute ("colourDown", "0")), false);
return true;
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
ImageButton* const ib = dynamic_cast<ImageButton*> (component);
String s;
s << getColourIntialisationCode (component, memberVariableName)
<< '\n';
const String indent (String::repeatedString (" ", memberVariableName.length() + 13));
s << memberVariableName << "->setImages (false, true, "
<< CodeHelpers::boolLiteral (doesImageKeepProportions (ib)) << ",\n"
<< indent
<< getImageCreationCode (ib, normalImage) << ", "
<< CodeHelpers::floatLiteral (getImageOpacity (ib, normalImage), 3) << ", "
<< CodeHelpers::colourToCode (getImageColour (ib, normalImage)) << ",\n"
<< indent
<< getImageCreationCode (ib, overImage) << ", "
<< CodeHelpers::floatLiteral (getImageOpacity (ib, overImage), 3) << ", "
<< CodeHelpers::colourToCode (getImageColour (ib, overImage)) << ",\n"
<< indent
<< getImageCreationCode (ib, downImage) << ", "
<< CodeHelpers::floatLiteral (getImageOpacity (ib, downImage), 3) << ", "
<< CodeHelpers::colourToCode (getImageColour (ib, downImage))
<< ");\n";
code.constructorCode += s;
}
static String getImageCreationCode (ImageButton* ib, const ImageRole role)
{
const String resName (getImageResource (ib, role));
if (resName.isEmpty())
return "juce::Image()";
return "juce::ImageCache::getFromMemory (" + resName + ", " + resName + "Size)";
}
//==============================================================================
class ImageButtonResourceProperty : public ImageResourceProperty<ImageButton>
{
public:
ImageButtonResourceProperty (ComponentLayout& layout_, ImageButton* const owner_, const ImageRole role_, const String& name)
: ImageResourceProperty<ImageButton> (*layout_.getDocument(), owner_, name, true),
role (role_),
layout (layout_)
{
}
void setResource (const String& newName)
{
setImageResource (layout, element, role, newName, true);
}
String getResource() const
{
return getImageResource (element, role);
}
private:
const ImageRole role;
ComponentLayout& layout;
};
class SetImageResourceAction : public ComponentUndoableAction<ImageButton>
{
public:
SetImageResourceAction (ImageButton* const button,
ComponentLayout& layout_,
const ImageRole role_,
const String& newResource_)
: ComponentUndoableAction<ImageButton> (button, layout_),
newResource (newResource_),
role (role_)
{
oldResource = ImageButtonHandler::getImageResource (button, role_);
}
bool perform() override
{
showCorrectTab();
ImageButtonHandler::setImageResource (layout, getComponent(), role, newResource, false);
return true;
}
bool undo() override
{
showCorrectTab();
ImageButtonHandler::setImageResource (layout, getComponent(), role, oldResource, false);
return true;
}
private:
String newResource, oldResource;
const ImageRole role;
};
//==============================================================================
static void setImageResource (ComponentLayout& layout, ImageButton* button, const ImageRole role, const String& newName, const bool undoable)
{
jassert (role < 3);
if (role < 3 && getImageResource (button, role) != newName)
{
if (undoable)
{
layout.getDocument()->perform (new SetImageResourceAction (button, layout, role, newName),
"Change image resource");
}
else
{
button->getProperties().set ("resource" + String ((int) role), newName);
updateButtonImages (*layout.getDocument(), button);
layout.changed();
}
}
}
static String getImageResource (ImageButton* button, const ImageRole role)
{
jassert (role < 3);
return button->getProperties() ["resource" + String ((int) role)].toString();
}
//==============================================================================
class SetImageKeepsPropAction : public ComponentUndoableAction<ImageButton>
{
public:
SetImageKeepsPropAction (ImageButton* const button,
ComponentLayout& layout_,
const bool newState_)
: ComponentUndoableAction<ImageButton> (button, layout_),
newState (newState_)
{
oldState = ImageButtonHandler::doesImageKeepProportions (button);
}
bool perform() override
{
showCorrectTab();
ImageButtonHandler::setImageKeepProportions (layout, getComponent(), newState, false);
return true;
}
bool undo() override
{
showCorrectTab();
ImageButtonHandler::setImageKeepProportions (layout, getComponent(), oldState, false);
return true;
}
private:
bool newState, oldState;
};
static bool doesImageKeepProportions (ImageButton* button)
{
return button->getProperties().getWithDefault ("keepImageProp", true);
}
static void setImageKeepProportions (ComponentLayout& layout, ImageButton* button, const bool newState, const bool undoable)
{
if (undoable)
{
layout.perform (new SetImageKeepsPropAction (button, layout, newState), "change imagebutton proportion mode");
}
else
{
button->getProperties().set ("keepImageProp", newState);
updateButtonImages (*layout.getDocument(), button);
layout.changed();
}
}
class ImageButtonProportionProperty : public ComponentBooleanProperty<ImageButton>
{
public:
ImageButtonProportionProperty (ComponentLayout& layout_, ImageButton* const owner_)
: ComponentBooleanProperty<ImageButton> ("proportional", "maintain image proportions", "scale to fit",
owner_, *layout_.getDocument()),
layout (layout_)
{
}
void setState (bool newState)
{
setImageKeepProportions (layout, component, newState, true);
}
bool getState() const
{
return doesImageKeepProportions (component);
}
private:
ComponentLayout& layout;
};
//==============================================================================
class SetImageOpacityAction : public ComponentUndoableAction<ImageButton>
{
public:
SetImageOpacityAction (ImageButton* const button,
ComponentLayout& layout_,
const ImageRole role_,
const float newState_)
: ComponentUndoableAction<ImageButton> (button, layout_),
role (role_),
newState (newState_)
{
oldState = ImageButtonHandler::getImageOpacity (button, role_);
}
bool perform() override
{
showCorrectTab();
ImageButtonHandler::setImageOpacity (layout, getComponent(), role, newState, false);
return true;
}
bool undo() override
{
showCorrectTab();
ImageButtonHandler::setImageOpacity (layout, getComponent(), role, oldState, false);
return true;
}
private:
const ImageRole role;
float newState, oldState;
};
static float getImageOpacity (ImageButton* button, const ImageRole role)
{
return (float) button->getProperties().getWithDefault ("imageOpacity" + String ((int) role), 1.0f);
}
static void setImageOpacity (ComponentLayout& layout, ImageButton* button, const ImageRole role, const float opacity, const bool undoable)
{
if (undoable)
{
layout.perform (new SetImageOpacityAction (button, layout, role, opacity), "change imagebutton opacity");
}
else
{
button->getProperties().set ("imageOpacity" + String ((int) role), opacity);
updateButtonImages (*layout.getDocument(), button);
layout.changed();
}
}
class ImageButtonOpacityProperty : public SliderPropertyComponent
{
public:
ImageButtonOpacityProperty (ComponentLayout& layout_, ImageButton* const owner_,
const String& name, const ImageRole role_)
: SliderPropertyComponent (name, 0.0, 1.0, 0.0),
owner (owner_),
layout (layout_),
role (role_)
{
}
void setValue (double newValue) override
{
setImageOpacity (layout, owner, role, (float) newValue, true);
}
double getValue() const override
{
return getImageOpacity (owner, role);
}
private:
ImageButton* const owner;
ComponentLayout& layout;
const ImageRole role;
};
//==============================================================================
class SetImageColourAction : public ComponentUndoableAction<ImageButton>
{
public:
SetImageColourAction (ImageButton* const button,
ComponentLayout& layout_,
const ImageRole role_,
Colour newState_)
: ComponentUndoableAction<ImageButton> (button, layout_),
role (role_),
newState (newState_)
{
oldState = ImageButtonHandler::getImageColour (button, role_);
}
bool perform() override
{
showCorrectTab();
ImageButtonHandler::setImageColour (layout, getComponent(), role, newState, false);
return true;
}
bool undo() override
{
showCorrectTab();
ImageButtonHandler::setImageColour (layout, getComponent(), role, oldState, false);
return true;
}
private:
const ImageRole role;
Colour newState, oldState;
};
static Colour getImageColour (ImageButton* button, const ImageRole role)
{
return Colour::fromString (button->getProperties().getWithDefault ("imageColour" + String ((int) role), "0").toString());
}
static void setImageColour (ComponentLayout& layout, ImageButton* button,
const ImageRole role, Colour colour, const bool undoable)
{
if (undoable)
{
layout.perform (new SetImageColourAction (button, layout, role, colour), "change imagebutton colour");
}
else
{
button->getProperties().set ("imageColour" + String ((int) role), colour.toString());
updateButtonImages (*layout.getDocument(), button);
layout.changed();
}
}
class ImageButtonColourProperty : public JucerColourPropertyComponent,
private ChangeListener
{
public:
ImageButtonColourProperty (ComponentLayout& layout_, ImageButton* const owner_,
const String& name, const ImageRole role_)
: JucerColourPropertyComponent (name, false),
owner (owner_),
layout (layout_),
role (role_)
{
layout_.getDocument()->addChangeListener (this);
}
~ImageButtonColourProperty() override
{
layout.getDocument()->removeChangeListener (this);
}
void setColour (Colour newColour) override
{
setImageColour (layout, owner, role, newColour, true);
}
Colour getColour() const override
{
return getImageColour (owner, role);
}
void resetToDefault() override {}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
ImageButton* const owner;
ComponentLayout& layout;
const ImageRole role;
};
//==============================================================================
static void updateButtonImages (JucerDocument& document, ImageButton* const ib)
{
Image norm = document.getResources().getImageFromCache (getImageResource (ib, normalImage));
Image over = document.getResources().getImageFromCache (getImageResource (ib, overImage));
Image down = document.getResources().getImageFromCache (getImageResource (ib, downImage));
ib->setImages (false, true, doesImageKeepProportions (ib),
norm,
getImageOpacity (ib, normalImage),
getImageColour (ib, normalImage),
over,
getImageOpacity (ib, overImage),
getImageColour (ib, overImage),
down,
getImageOpacity (ib, downImage),
getImageColour (ib, downImage));
}
};

View File

@ -0,0 +1,273 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
#include "../UI/jucer_TestComponent.h"
#include "../Properties/jucer_FilePropertyComponent.h"
#include "../Properties/jucer_ComponentTextProperty.h"
#include "jucer_ComponentUndoableAction.h"
#include "../../Project/UI/jucer_ProjectContentComponent.h"
//==============================================================================
class JucerComponentHandler : public ComponentTypeHandler
{
public:
JucerComponentHandler()
: ComponentTypeHandler ("Projucer Component", "xxx",
typeid (TestComponent), 300, 200)
{}
Component* createNewComponent (JucerDocument* doc) override
{
return new TestComponent (doc, nullptr, false);
}
String getXmlTagName() const noexcept override { return "JUCERCOMP"; }
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
TestComponent* const tc = dynamic_cast<TestComponent*> (comp);
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("sourceFile", tc->getFilename());
e->setAttribute ("constructorParams", tc->getConstructorParams());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
auto tc = dynamic_cast<TestComponent*> (comp);
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
tc->setFilename (xml.getStringAttribute ("sourceFile", tc->getFilename()));
tc->setConstructorParams (xml.getStringAttribute ("constructorParams"));
return true;
}
String getClassName (Component* comp) const override
{
auto tc = dynamic_cast<TestComponent*> (comp);
String jucerCompClassName;
if (tc->getDocument() != nullptr)
jucerCompClassName = tc->getDocument()->getClassName();
if (jucerCompClassName.isEmpty())
jucerCompClassName = "juce::Component";
return jucerCompClassName;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto tc = dynamic_cast<TestComponent*> (component))
{
props.add (new JucerCompFileProperty (tc, document));
props.add (new ConstructorParamsProperty (tc, document));
props.add (new JucerCompOpenDocProperty (tc));
}
}
String getCreationParameters (GeneratedCode&, Component* component) override
{
return dynamic_cast<TestComponent*> (component)->getConstructorParams().trim();
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
if (auto tc = dynamic_cast<TestComponent*> (component))
code.includeFilesH.add (tc->findFile().withFileExtension (".h"));
else
jassertfalse;
}
//==============================================================================
class JucerCompFileChangeAction : public ComponentUndoableAction <TestComponent>
{
public:
JucerCompFileChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <TestComponent> (comp, l),
newState (newState_)
{
oldState = comp->getFilename();
}
bool perform()
{
showCorrectTab();
getComponent()->setFilename (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setFilename (oldState);
changed();
return true;
}
String newState, oldState;
};
static void setJucerComponentFile (JucerDocument& document, TestComponent* comp, const String& newFilename)
{
jassert (comp != nullptr);
if (comp != nullptr)
document.perform (new JucerCompFileChangeAction (comp, *document.getComponentLayout(), newFilename),
"Change Projucer component file");
}
private:
//==============================================================================
class JucerCompFileProperty : public FilePropertyComponent,
private ChangeListener
{
public:
JucerCompFileProperty (TestComponent* const comp, JucerDocument& doc)
: FilePropertyComponent ("Jucer file", false, true),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~JucerCompFileProperty() override
{
document.removeChangeListener (this);
}
void setFile (const File& newFile) override
{
setJucerComponentFile (document, component,
newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
.replaceCharacter ('\\', '/'));
}
File getFile() const override
{
return component->findFile();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
TestComponent* const component;
JucerDocument& document;
};
//==============================================================================
struct JucerCompOpenDocProperty : public ButtonPropertyComponent
{
JucerCompOpenDocProperty (TestComponent* const c)
: ButtonPropertyComponent ("edit", false),
component (c)
{
}
void buttonClicked()
{
if (ProjectContentComponent* const pcc = findParentComponentOfClass<ProjectContentComponent>())
pcc->showEditorForFile (component->findFile(), true);
}
String getButtonText() const
{
return "Open file for editing";
}
TestComponent* const component;
};
//==============================================================================
struct ConstructorParamsProperty : public ComponentTextProperty <TestComponent>
{
ConstructorParamsProperty (TestComponent* comp, JucerDocument& doc)
: ComponentTextProperty <TestComponent> ("constructor params", 512, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new ConstructorParamChangeAction (component, *document.getComponentLayout(), newText),
"Change Viewport content constructor params");
}
String getText() const override
{
return component->getConstructorParams();
}
private:
struct ConstructorParamChangeAction : public ComponentUndoableAction <TestComponent>
{
ConstructorParamChangeAction (TestComponent* const comp, ComponentLayout& l, const String& newValue_)
: ComponentUndoableAction <TestComponent> (comp, l),
newValue (newValue_)
{
oldValue = comp->getConstructorParams();
}
bool perform()
{
showCorrectTab();
getComponent()->setConstructorParams (newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setConstructorParams (oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
String newValue, oldValue;
};
};
};

View File

@ -0,0 +1,774 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class LabelHandler : public ComponentTypeHandler
{
public:
LabelHandler()
: ComponentTypeHandler ("Label", "juce::Label", typeid (Label), 150, 24)
{
registerColour (juce::Label::backgroundColourId, "background", "bkgCol");
registerColour (juce::Label::textColourId, "text", "textCol");
registerColour (juce::Label::outlineColourId, "outline", "outlineCol");
registerColour (juce::TextEditor::textColourId, "editor text", "edTextCol");
registerColour (juce::TextEditor::backgroundColourId, "editor bkg", "edBkgCol");
registerColour (juce::TextEditor::highlightColourId, "highlight", "hiliteCol");
}
Component* createNewComponent (JucerDocument*) override
{
return new Label ("new label", "label text");
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
Label* const l = dynamic_cast<Label*> (comp);
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("labelText", l->getText());
e->setAttribute ("editableSingleClick", l->isEditableOnSingleClick());
e->setAttribute ("editableDoubleClick", l->isEditableOnDoubleClick());
e->setAttribute ("focusDiscardsChanges", l->doesLossOfFocusDiscardChanges());
e->setAttribute ("fontname", l->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()).toString());
e->setAttribute ("fontsize", roundToInt (l->getFont().getHeight() * 100.0) / 100.0);
e->setAttribute ("kerning", roundToInt (l->getFont().getExtraKerningFactor() * 1000.0) / 1000.0);
e->setAttribute ("bold", l->getFont().isBold());
e->setAttribute ("italic", l->getFont().isItalic());
e->setAttribute ("justification", l->getJustificationType().getFlags());
if (l->getFont().getTypefaceStyle() != "Regular")
{
e->setAttribute ("typefaceStyle", l->getFont().getTypefaceStyle());
}
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
Label* const l = dynamic_cast<Label*> (comp);
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
Label defaultLabel;
Font font;
font.setHeight ((float) xml.getDoubleAttribute ("fontsize", 15.0));
font.setBold (xml.getBoolAttribute ("bold", false));
font.setItalic (xml.getBoolAttribute ("italic", false));
font.setExtraKerningFactor ((float) xml.getDoubleAttribute ("kerning", 0.0));
auto fontStyle = xml.getStringAttribute ("typefaceStyle");
if (! fontStyle.isEmpty())
font.setTypefaceStyle (fontStyle);
l->setFont (font);
l->getProperties().set ("typefaceName", xml.getStringAttribute ("fontname", FontPropertyComponent::getDefaultFont()));
updateLabelFont (l);
l->setJustificationType (Justification (xml.getIntAttribute ("justification", Justification::centred)));
l->setText (xml.getStringAttribute ("labelText", "Label Text"), dontSendNotification);
l->setEditable (xml.getBoolAttribute ("editableSingleClick", defaultLabel.isEditableOnSingleClick()),
xml.getBoolAttribute ("editableDoubleClick", defaultLabel.isEditableOnDoubleClick()),
xml.getBoolAttribute ("focusDiscardsChanges", defaultLabel.doesLossOfFocusDiscardChanges()));
return true;
}
static void updateLabelFont (Label* label)
{
Font f (label->getFont());
f = FontPropertyComponent::applyNameToFont (label->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()), f);
label->setFont (f);
}
String getCreationParameters (GeneratedCode& code, Component* component) override
{
Label* const l = dynamic_cast<Label*> (component);
return quotedString (component->getName(), false)
+ ",\n"
+ quotedString (l->getText(), code.shouldUseTransMacro());
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
Label* const l = dynamic_cast<Label*> (component);
String s;
s << memberVariableName << "->setFont ("
<< FontPropertyComponent::getCompleteFontCode (l->getFont(), l->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont()))
<< ");\n"
<< memberVariableName << "->setJustificationType ("
<< CodeHelpers::justificationToCode (l->getJustificationType())
<< ");\n"
<< memberVariableName << "->setEditable ("
<< CodeHelpers::boolLiteral (l->isEditableOnSingleClick()) << ", "
<< CodeHelpers::boolLiteral (l->isEditableOnDoubleClick()) << ", "
<< CodeHelpers::boolLiteral (l->doesLossOfFocusDiscardChanges()) << ");\n"
<< getColourIntialisationCode (component, memberVariableName);
if (needsCallback (component))
s << memberVariableName << "->addListener (this);\n";
s << '\n';
code.constructorCode += s;
}
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
{
ComponentTypeHandler::fillInGeneratedCode (component, code);
if (needsCallback (component))
{
String& callback = code.getCallbackCode ("public juce::Label::Listener",
"void",
"labelTextChanged (juce::Label* labelThatHasChanged)",
true);
if (callback.trim().isNotEmpty())
callback << "else ";
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
const String userCodeComment ("UserLabelCode_" + memberVariableName);
callback
<< "if (labelThatHasChanged == " << memberVariableName << ".get())\n"
<< "{\n //[" << userCodeComment << "] -- add your label text handling code here..\n //[/" << userCodeComment << "]\n}\n";
}
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* const l = dynamic_cast<Label*> (component))
{
props.add (new LabelTextProperty (l, document));
props.add (new LabelJustificationProperty (l, document));
props.add (new FontNameProperty (l, document));
props.add (new FontStyleProperty (l, document));
props.add (new FontSizeProperty (l, document));
props.add (new FontKerningProperty (l, document));
props.add (new LabelEditableProperty (l, document));
if (l->isEditableOnDoubleClick() || l->isEditableOnSingleClick())
props.add (new LabelLossOfFocusProperty (l, document));
}
addColourProperties (component, document, props);
}
static bool needsCallback (Component* label)
{
return ((Label*) label)->isEditableOnSingleClick()
|| ((Label*) label)->isEditableOnDoubleClick(); // xxx should be configurable
}
private:
//==============================================================================
class LabelTextProperty : public ComponentTextProperty <Label>
{
public:
LabelTextProperty (Label* comp, JucerDocument& doc)
: ComponentTextProperty <Label> ("text", 10000, true, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new LabelTextChangeAction (component, *document.getComponentLayout(), newText),
"Change Label text");
}
String getText() const override
{
return component->getText();
}
private:
class LabelTextChangeAction : public ComponentUndoableAction <Label>
{
public:
LabelTextChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->getText();
}
bool perform()
{
showCorrectTab();
getComponent()->setText (newState, dontSendNotification);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setText (oldState, dontSendNotification);
changed();
return true;
}
String newState, oldState;
};
};
//==============================================================================
class LabelEditableProperty : public ComponentChoiceProperty <Label>
{
public:
LabelEditableProperty (Label* comp, JucerDocument& doc)
: ComponentChoiceProperty <Label> ("editing", comp, doc)
{
choices.add ("read-only");
choices.add ("edit on single-click");
choices.add ("edit on double-click");
}
void setIndex (int newIndex)
{
document.perform (new LabelEditableChangeAction (component, *document.getComponentLayout(), newIndex),
"Change Label editability");
}
int getIndex() const
{
return component->isEditableOnSingleClick()
? 1
: (component->isEditableOnDoubleClick() ? 2 : 0);
}
private:
class LabelEditableChangeAction : public ComponentUndoableAction <Label>
{
public:
LabelEditableChangeAction (Label* const comp, ComponentLayout& l, const int newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->isEditableOnSingleClick()
? 1
: (comp->isEditableOnDoubleClick() ? 2 : 0);
}
bool perform()
{
showCorrectTab();
getComponent()->setEditable (newState == 1, newState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
changed();
layout.getSelectedSet().changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setEditable (oldState == 1, oldState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
changed();
layout.getSelectedSet().changed();
return true;
}
int newState, oldState;
};
};
//==============================================================================
class LabelLossOfFocusProperty : public ComponentChoiceProperty <Label>
{
public:
LabelLossOfFocusProperty (Label* comp, JucerDocument& doc)
: ComponentChoiceProperty <Label> ("focus", comp, doc)
{
choices.add ("loss of focus discards changes");
choices.add ("loss of focus commits changes");
}
void setIndex (int newIndex)
{
document.perform (new LabelFocusLossChangeAction (component, *document.getComponentLayout(), newIndex == 0),
"Change Label focus behaviour");
}
int getIndex() const
{
return component->doesLossOfFocusDiscardChanges() ? 0 : 1;
}
private:
class LabelFocusLossChangeAction : public ComponentUndoableAction <Label>
{
public:
LabelFocusLossChangeAction (Label* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->doesLossOfFocusDiscardChanges();
}
bool perform()
{
showCorrectTab();
getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
getComponent()->isEditableOnDoubleClick(),
newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
getComponent()->isEditableOnDoubleClick(),
oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class LabelJustificationProperty : public JustificationProperty,
private ChangeListener
{
public:
LabelJustificationProperty (Label* const label_, JucerDocument& doc)
: JustificationProperty ("layout", false),
label (label_),
document (doc)
{
document.addChangeListener (this);
}
~LabelJustificationProperty() override
{
document.removeChangeListener (this);
}
void setJustification (Justification newJustification) override
{
document.perform (new LabelJustifyChangeAction (label, *document.getComponentLayout(), newJustification),
"Change Label justification");
}
Justification getJustification() const override
{
return label->getJustificationType();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
Label* const label;
JucerDocument& document;
class LabelJustifyChangeAction : public ComponentUndoableAction <Label>
{
public:
LabelJustifyChangeAction (Label* const comp, ComponentLayout& l, Justification newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_),
oldState (comp->getJustificationType())
{
}
bool perform()
{
showCorrectTab();
getComponent()->setJustificationType (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setJustificationType (oldState);
changed();
return true;
}
Justification newState, oldState;
};
};
//==============================================================================
class FontNameProperty : public FontPropertyComponent,
private ChangeListener
{
public:
FontNameProperty (Label* const label_, JucerDocument& doc)
: FontPropertyComponent ("font"),
label (label_),
document (doc)
{
document.addChangeListener (this);
}
~FontNameProperty() override
{
document.removeChangeListener (this);
}
void setTypefaceName (const String& newFontName) override
{
document.perform (new FontNameChangeAction (label, *document.getComponentLayout(), newFontName),
"Change Label typeface");
}
String getTypefaceName() const override
{
return label->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
}
private:
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
Label* const label;
JucerDocument& document;
class FontNameChangeAction : public ComponentUndoableAction <Label>
{
public:
FontNameChangeAction (Label* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->getProperties().getWithDefault ("typefaceName", FontPropertyComponent::getDefaultFont());
}
bool perform()
{
showCorrectTab();
getComponent()->getProperties().set ("typefaceName", newState);
LabelHandler::updateLabelFont (getComponent());
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->getProperties().set ("typefaceName", oldState);
LabelHandler::updateLabelFont (getComponent());
changed();
return true;
}
String newState, oldState;
};
};
//==============================================================================
class FontSizeProperty : public SliderPropertyComponent,
private ChangeListener
{
public:
FontSizeProperty (Label* const label_, JucerDocument& doc)
: SliderPropertyComponent ("size", 1.0, 250.0, 0.1, 0.3),
label (label_),
document (doc)
{
document.addChangeListener (this);
}
~FontSizeProperty() override
{
document.removeChangeListener (this);
}
void setValue (double newValue) override
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new FontSizeChangeAction (label, *document.getComponentLayout(), (float) newValue),
"Change Label font size");
}
double getValue() const override
{
return label->getFont().getHeight();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
Label* const label;
JucerDocument& document;
class FontSizeChangeAction : public ComponentUndoableAction <Label>
{
public:
FontSizeChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->getFont().getHeight();
}
bool perform()
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setHeight ((float) newState);
getComponent()->setFont (f);
changed();
return true;
}
bool undo()
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setHeight ((float) oldState);
getComponent()->setFont (f);
changed();
return true;
}
float newState, oldState;
};
};
//==============================================================================
class FontStyleProperty : public ChoicePropertyComponent,
private ChangeListener
{
public:
FontStyleProperty (Label* const label_, JucerDocument& doc)
: ChoicePropertyComponent ("style"),
label (label_),
document (doc)
{
document.addChangeListener (this);
updateStylesList (label->getFont());
}
~FontStyleProperty() override
{
document.removeChangeListener (this);
}
void updateStylesList (const Font& newFont)
{
if (getNumChildComponents() > 0)
{
if (auto cb = dynamic_cast<ComboBox*> (getChildComponent (0)))
cb->clear();
getChildComponent (0)->setVisible (false);
removeAllChildren();
}
choices.clear();
choices.add ("Regular");
choices.add ("Bold");
choices.add ("Italic");
choices.add ("Bold Italic");
choices.mergeArray (newFont.getAvailableStyles());
refresh();
}
void setIndex (int newIndex) override
{
Font f (label->getFont());
if (f.getAvailableStyles().contains (choices[newIndex]))
{
f.setBold (false);
f.setItalic (false);
f.setTypefaceStyle (choices[newIndex]);
}
else
{
f.setTypefaceStyle ("Regular");
f.setBold (newIndex == 1 || newIndex == 3);
f.setItalic (newIndex == 2 || newIndex == 3);
}
document.perform (new FontStyleChangeAction (label, *document.getComponentLayout(), f),
"Change Label font style");
}
int getIndex() const override
{
auto f = label->getFont();
const auto typefaceIndex = choices.indexOf (f.getTypefaceStyle());
if (typefaceIndex == -1)
{
if (f.isBold() && f.isItalic())
return 3;
else if (f.isBold())
return 1;
else if (f.isItalic())
return 2;
return 0;
}
return typefaceIndex;
}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
updateStylesList (label->getFont());
}
Label* const label;
JucerDocument& document;
class FontStyleChangeAction : public ComponentUndoableAction <Label>
{
public:
FontStyleChangeAction (Label* const comp, ComponentLayout& l, const Font& newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->getFont();
}
bool perform()
{
showCorrectTab();
getComponent()->setFont (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setFont (oldState);
changed();
return true;
}
Font newState, oldState;
};
};
//==============================================================================
class FontKerningProperty : public SliderPropertyComponent,
private ChangeListener
{
public:
FontKerningProperty (Label* const label_, JucerDocument& doc)
: SliderPropertyComponent ("kerning", -0.5, 0.5, 0.001),
label (label_),
document (doc)
{
document.addChangeListener (this);
}
~FontKerningProperty() override
{
document.removeChangeListener (this);
}
void setValue (double newValue) override
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new FontKerningChangeAction (label, *document.getComponentLayout(), (float) newValue),
"Change Label font kerning");
}
double getValue() const override
{
return label->getFont().getExtraKerningFactor();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
Label* const label;
JucerDocument& document;
class FontKerningChangeAction : public ComponentUndoableAction <Label>
{
public:
FontKerningChangeAction (Label* const comp, ComponentLayout& l, const float newState_)
: ComponentUndoableAction <Label> (comp, l),
newState (newState_)
{
oldState = comp->getFont().getExtraKerningFactor();
}
bool perform()
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setExtraKerningFactor ((float) newState);
getComponent()->setFont (f);
changed();
return true;
}
bool undo()
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setExtraKerningFactor ((float) oldState);
getComponent()->setFont (f);
changed();
return true;
}
float newState, oldState;
};
};
};

View File

@ -0,0 +1,709 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
static const Slider::SliderStyle sliderStyleTypes[] =
{
Slider::LinearHorizontal,
Slider::LinearVertical,
Slider::LinearBar,
Slider::LinearBarVertical,
Slider::Rotary,
Slider::RotaryHorizontalDrag,
Slider::RotaryVerticalDrag,
Slider::RotaryHorizontalVerticalDrag,
Slider::IncDecButtons,
Slider::TwoValueHorizontal,
Slider::TwoValueVertical,
Slider::ThreeValueHorizontal,
Slider::ThreeValueVertical
};
static const Slider::TextEntryBoxPosition sliderTextBoxPositions[] =
{
Slider::NoTextBox,
Slider::TextBoxLeft,
Slider::TextBoxRight,
Slider::TextBoxAbove,
Slider::TextBoxBelow
};
struct SliderHandler : public ComponentTypeHandler
{
SliderHandler()
: ComponentTypeHandler ("Slider", "juce::Slider", typeid (Slider), 150, 24)
{
registerColour (juce::Slider::backgroundColourId, "background", "bkgcol");
registerColour (juce::Slider::thumbColourId, "thumb", "thumbcol");
registerColour (juce::Slider::trackColourId, "track", "trackcol");
registerColour (juce::Slider::rotarySliderFillColourId, "rotary fill", "rotarysliderfill");
registerColour (juce::Slider::rotarySliderOutlineColourId, "rotary outln", "rotaryslideroutline");
registerColour (juce::Slider::textBoxTextColourId, "textbox text", "textboxtext");
registerColour (juce::Slider::textBoxBackgroundColourId, "textbox bkgd", "textboxbkgd");
registerColour (juce::Slider::textBoxHighlightColourId, "textbox highlt", "textboxhighlight");
registerColour (juce::Slider::textBoxOutlineColourId, "textbox outln", "textboxoutline");
}
Component* createNewComponent (JucerDocument*) override
{
return new Slider ("new slider");
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
Slider* s = dynamic_cast<Slider*> (comp);
e->setAttribute ("min", s->getMinimum());
e->setAttribute ("max", s->getMaximum());
e->setAttribute ("int", s->getInterval());
e->setAttribute ("style", sliderStyleToString (s->getSliderStyle()));
e->setAttribute ("textBoxPos", textBoxPosToString (s->getTextBoxPosition()));
e->setAttribute ("textBoxEditable", s->isTextBoxEditable());
e->setAttribute ("textBoxWidth", s->getTextBoxWidth());
e->setAttribute ("textBoxHeight", s->getTextBoxHeight());
e->setAttribute ("skewFactor", s->getSkewFactor());
e->setAttribute ("needsCallback", needsSliderListener (s));
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
Slider* const s = dynamic_cast<Slider*> (comp);
s->setRange (xml.getDoubleAttribute ("min", 0.0),
xml.getDoubleAttribute ("max", 10.0),
xml.getDoubleAttribute ("int", 0.0));
s->setSliderStyle (sliderStringToStyle (xml.getStringAttribute ("style", "LinearHorizontal")));
s->setTextBoxStyle (stringToTextBoxPos (xml.getStringAttribute ("textBoxPos", "TextBoxLeft")),
! xml.getBoolAttribute ("textBoxEditable", true),
xml.getIntAttribute ("textBoxWidth", 80),
xml.getIntAttribute ("textBoxHeight", 20));
s->setSkewFactor (xml.getDoubleAttribute ("skewFactor", 1.0));
setNeedsSliderListener (s, xml.getBoolAttribute ("needsCallback", true));
return true;
}
String getCreationParameters (GeneratedCode&, Component* component) override
{
return quotedString (component->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
Slider* const s = dynamic_cast<Slider*> (component);
String r;
r << memberVariableName << "->setRange ("
<< s->getMinimum() << ", " << s->getMaximum() << ", " << s->getInterval()
<< ");\n"
<< memberVariableName << "->setSliderStyle (juce::Slider::"
<< sliderStyleToString (s->getSliderStyle()) << ");\n"
<< memberVariableName << "->setTextBoxStyle (juce::Slider::"
<< textBoxPosToString (s->getTextBoxPosition())
<< ", " << CodeHelpers::boolLiteral (! s->isTextBoxEditable())
<< ", " << s->getTextBoxWidth() << ", " << s->getTextBoxHeight() << ");\n"
<< getColourIntialisationCode (component, memberVariableName);
if (needsSliderListener (component))
r << memberVariableName << "->addListener (this);\n";
if (s->getSkewFactor() != 1.0)
r << memberVariableName << "->setSkewFactor (" << s->getSkewFactor() << ");\n";
r << '\n';
code.constructorCode += r;
}
void fillInGeneratedCode (Component* component, GeneratedCode& code) override
{
ComponentTypeHandler::fillInGeneratedCode (component, code);
if (needsSliderListener (component))
{
String& callback = code.getCallbackCode ("public juce::Slider::Listener",
"void",
"sliderValueChanged (juce::Slider* sliderThatWasMoved)",
true);
if (callback.isNotEmpty())
callback << "else ";
const String memberVariableName (code.document->getComponentLayout()->getComponentMemberVariableName (component));
const String userCodeComment ("UserSliderCode_" + memberVariableName);
callback
<< "if (sliderThatWasMoved == " << memberVariableName << ".get())\n"
<< "{\n //[" << userCodeComment << "] -- add your slider handling code here..\n //[/" << userCodeComment << "]\n}\n";
}
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* s = dynamic_cast<Slider*> (component))
{
props.add (new SliderRangeProperty (s, document, "minimum", 0));
props.add (new SliderRangeProperty (s, document, "maximum", 1));
props.add (new SliderRangeProperty (s, document, "interval", 2));
props.add (new SliderTypeProperty (s, document));
props.add (new SliderTextboxProperty (s, document));
props.add (new SliderTextboxEditableProperty (s, document));
props.add (new SliderTextboxSizeProperty (s, document, true));
props.add (new SliderTextboxSizeProperty (s, document, false));
props.add (new SliderSkewProperty (s, document));
props.add (new SliderCallbackProperty (s, document));
}
addColourProperties (component, document, props);
}
static bool needsSliderListener (Component* slider)
{
return slider->getProperties().getWithDefault ("generateListenerCallback", true);
}
static void setNeedsSliderListener (Component* slider, bool shouldDoCallback)
{
slider->getProperties().set ("generateListenerCallback", shouldDoCallback);
}
private:
//==============================================================================
struct SliderTypeProperty : public ComponentChoiceProperty<Slider>
{
SliderTypeProperty (Slider* slider, JucerDocument& doc)
: ComponentChoiceProperty<Slider> ("type", slider, doc)
{
choices.add ("Linear Horizontal");
choices.add ("Linear Vertical");
choices.add ("Linear Bar Horizontal");
choices.add ("Linear Bar Vertical");
choices.add ("Rotary");
choices.add ("Rotary HorizontalDrag");
choices.add ("Rotary VerticalDrag");
choices.add ("Rotary HorizontalVerticalDrag");
choices.add ("Inc/Dec Buttons");
choices.add ("Two Value Horizontal");
choices.add ("Two Value Vertical");
choices.add ("Three Value Horizontal");
choices.add ("Three Value Vertical");
}
void setIndex (int newIndex) override
{
if (newIndex >= 0 && newIndex < numElementsInArray (sliderStyleTypes))
document.perform (new SliderTypeChangeAction (component, *document.getComponentLayout(),
sliderStyleTypes[newIndex]),
"Change Slider style");
}
int getIndex() const override
{
for (int i = 0; i < numElementsInArray (sliderStyleTypes); ++i)
if (sliderStyleTypes[i] == dynamic_cast<Slider*> (component)->getSliderStyle())
return i;
return -1;
}
private:
struct SliderTypeChangeAction : public ComponentUndoableAction<Slider>
{
SliderTypeChangeAction (Slider* comp, ComponentLayout& l, Slider::SliderStyle newState_)
: ComponentUndoableAction<Slider> (comp, l),
newState (newState_)
{
oldState = comp->getSliderStyle();
}
bool perform() override
{
showCorrectTab();
getComponent()->setSliderStyle (newState);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
getComponent()->setSliderStyle (oldState);
changed();
return true;
}
Slider::SliderStyle newState, oldState;
};
};
//==============================================================================
struct SliderTextboxProperty : public ComponentChoiceProperty<Slider>
{
SliderTextboxProperty (Slider* slider, JucerDocument& doc)
: ComponentChoiceProperty<Slider> ("text position", slider, doc)
{
choices.add ("No text box");
choices.add ("Text box on left");
choices.add ("Text box on right");
choices.add ("Text box above");
choices.add ("Text box below");
}
void setIndex (int newIndex) override
{
if (newIndex >= 0 && newIndex < numElementsInArray (sliderTextBoxPositions))
document.perform (new SliderTextBoxChangeAction (component, *document.getComponentLayout(),
sliderTextBoxPositions[newIndex]),
"Change Slider textbox");
}
int getIndex() const override
{
for (int i = 0; i < numElementsInArray (sliderTextBoxPositions); ++i)
if (sliderTextBoxPositions[i] == component->getTextBoxPosition())
return i;
return -1;
}
private:
struct SliderTextBoxChangeAction : public ComponentUndoableAction<Slider>
{
SliderTextBoxChangeAction (Slider* comp, ComponentLayout& l, Slider::TextEntryBoxPosition newState_)
: ComponentUndoableAction<Slider> (comp, l),
newState (newState_)
{
oldState = comp->getTextBoxPosition();
}
bool perform() override
{
showCorrectTab();
getComponent()->setTextBoxStyle (newState,
! getComponent()->isTextBoxEditable(),
getComponent()->getTextBoxWidth(),
getComponent()->getTextBoxHeight());
changed();
return true;
}
bool undo() override
{
showCorrectTab();
getComponent()->setTextBoxStyle (oldState,
! getComponent()->isTextBoxEditable(),
getComponent()->getTextBoxWidth(),
getComponent()->getTextBoxHeight());
changed();
return true;
}
Slider::TextEntryBoxPosition newState, oldState;
};
};
//==============================================================================
struct SliderTextboxEditableProperty : public ComponentBooleanProperty<Slider>
{
SliderTextboxEditableProperty (Slider* slider, JucerDocument& doc)
: ComponentBooleanProperty<Slider> ("text box mode", "Editable", "Editable", slider, doc)
{
}
void setState (bool newState) override
{
document.perform (new SliderEditableChangeAction (component, *document.getComponentLayout(), newState),
"Change Slider editability");
}
bool getState() const override
{
return component->isTextBoxEditable();
}
private:
struct SliderEditableChangeAction : public ComponentUndoableAction<Slider>
{
SliderEditableChangeAction (Slider* const comp, ComponentLayout& l, bool newState_)
: ComponentUndoableAction<Slider> (comp, l),
newState (newState_)
{
oldState = comp->isTextBoxEditable();
}
bool perform() override
{
showCorrectTab();
getComponent()->setTextBoxIsEditable (newState);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
getComponent()->setTextBoxIsEditable (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
struct SliderCallbackProperty : public ComponentBooleanProperty<Slider>
{
SliderCallbackProperty (Slider* s, JucerDocument& doc)
: ComponentBooleanProperty<Slider> ("callback", "Generate SliderListener",
"Generate SliderListener", s, doc)
{
}
void setState (bool newState) override
{
document.perform (new SliderCallbackChangeAction (component, *document.getComponentLayout(), newState),
"Change button callback");
}
bool getState() const override { return needsSliderListener (component); }
struct SliderCallbackChangeAction : public ComponentUndoableAction<Slider>
{
SliderCallbackChangeAction (Slider* comp, ComponentLayout& l, bool newState_)
: ComponentUndoableAction<Slider> (comp, l),
newState (newState_)
{
oldState = needsSliderListener (comp);
}
bool perform() override
{
showCorrectTab();
setNeedsSliderListener (getComponent(), newState);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
setNeedsSliderListener (getComponent(), oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
struct SliderTextboxSizeProperty : public ComponentTextProperty<Slider>
{
SliderTextboxSizeProperty (Slider* slider, JucerDocument& doc, bool isWidth_)
: ComponentTextProperty<Slider> (isWidth_ ? "text box width" : "text box height",
12, false, slider, doc),
isWidth (isWidth_)
{
}
void setText (const String& newText) override
{
document.perform (new SliderBoxSizeChangeAction (component, *document.getComponentLayout(), isWidth, newText.getIntValue()),
"Change Slider textbox size");
}
String getText() const override
{
return String (isWidth ? component->getTextBoxWidth()
: component->getTextBoxHeight());
}
private:
const bool isWidth;
struct SliderBoxSizeChangeAction : public ComponentUndoableAction<Slider>
{
SliderBoxSizeChangeAction (Slider* const comp, ComponentLayout& l, bool isWidth_, int newSize_)
: ComponentUndoableAction<Slider> (comp, l),
isWidth (isWidth_),
newSize (newSize_)
{
oldSize = isWidth ? comp->getTextBoxWidth()
: comp->getTextBoxHeight();
}
bool perform() override
{
showCorrectTab();
Slider& c = *getComponent();
if (isWidth)
c.setTextBoxStyle (c.getTextBoxPosition(),
! c.isTextBoxEditable(),
newSize,
c.getTextBoxHeight());
else
c.setTextBoxStyle (c.getTextBoxPosition(),
! c.isTextBoxEditable(),
c.getTextBoxWidth(),
newSize);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
Slider& c = *getComponent();
if (isWidth)
c.setTextBoxStyle (c.getTextBoxPosition(),
! c.isTextBoxEditable(),
oldSize,
c.getTextBoxHeight());
else
c.setTextBoxStyle (c.getTextBoxPosition(),
! c.isTextBoxEditable(),
c.getTextBoxWidth(),
oldSize);
changed();
return true;
}
bool isWidth;
int newSize, oldSize;
};
};
//==============================================================================
struct SliderRangeProperty : public ComponentTextProperty<Slider>
{
SliderRangeProperty (Slider* slider, JucerDocument& doc,
const String& name, int rangeParam_)
: ComponentTextProperty<Slider> (name, 15, false, slider, doc),
rangeParam (rangeParam_)
{
}
void setText (const String& newText) override
{
double state [3];
state [0] = component->getMinimum();
state [1] = component->getMaximum();
state [2] = component->getInterval();
state [rangeParam] = newText.getDoubleValue();
document.perform (new SliderRangeChangeAction (component, *document.getComponentLayout(), state),
"Change Slider range");
}
String getText() const override
{
if (auto* s = dynamic_cast<Slider*> (component))
{
switch (rangeParam)
{
case 0: return String (s->getMinimum());
case 1: return String (s->getMaximum());
case 2: return String (s->getInterval());
default: jassertfalse; break;
}
}
return {};
}
private:
const int rangeParam;
struct SliderRangeChangeAction : public ComponentUndoableAction<Slider>
{
SliderRangeChangeAction (Slider* comp, ComponentLayout& l, const double newState_[3])
: ComponentUndoableAction<Slider> (comp, l)
{
newState[0] = newState_ [0];
newState[1] = newState_ [1];
newState[2] = newState_ [2];
oldState[0] = comp->getMinimum();
oldState[1] = comp->getMaximum();
oldState[2] = comp->getInterval();
}
bool perform() override
{
showCorrectTab();
getComponent()->setRange (newState[0], newState[1], newState[2]);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
getComponent()->setRange (oldState[0], oldState[1], oldState[2]);
changed();
return true;
}
double newState[3], oldState[3];
};
};
//==============================================================================
struct SliderSkewProperty : public ComponentTextProperty<Slider>
{
SliderSkewProperty (Slider* slider, JucerDocument& doc)
: ComponentTextProperty<Slider> ("skew factor", 12, false, slider, doc)
{
}
void setText (const String& newText) override
{
const double skew = jlimit (0.001, 1000.0, newText.getDoubleValue());
document.perform (new SliderSkewChangeAction (component, *document.getComponentLayout(), skew),
"Change Slider skew");
}
String getText() const override
{
if (auto* s = dynamic_cast<Slider*> (component))
return String (s->getSkewFactor());
return {};
}
struct SliderSkewChangeAction : public ComponentUndoableAction<Slider>
{
SliderSkewChangeAction (Slider* comp, ComponentLayout& l, double newValue_)
: ComponentUndoableAction<Slider> (comp, l)
{
newValue = newValue_;
oldValue = comp->getSkewFactor();
}
bool perform() override
{
showCorrectTab();
getComponent()->setSkewFactor (newValue);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
getComponent()->setSkewFactor (oldValue);
changed();
return true;
}
double newValue, oldValue;
};
};
//==============================================================================
static String sliderStyleToString (Slider::SliderStyle style)
{
switch (style)
{
case Slider::LinearHorizontal: return "LinearHorizontal";
case Slider::LinearVertical: return "LinearVertical";
case Slider::LinearBar: return "LinearBar";
case Slider::LinearBarVertical: return "LinearBarVertical";
case Slider::Rotary: return "Rotary";
case Slider::RotaryHorizontalDrag: return "RotaryHorizontalDrag";
case Slider::RotaryVerticalDrag: return "RotaryVerticalDrag";
case Slider::RotaryHorizontalVerticalDrag: return "RotaryHorizontalVerticalDrag";
case Slider::IncDecButtons: return "IncDecButtons";
case Slider::TwoValueHorizontal: return "TwoValueHorizontal";
case Slider::TwoValueVertical: return "TwoValueVertical";
case Slider::ThreeValueHorizontal: return "ThreeValueHorizontal";
case Slider::ThreeValueVertical: return "ThreeValueVertical";
default: jassertfalse; break;
}
return {};
}
static Slider::SliderStyle sliderStringToStyle (const String& s)
{
for (int i = 0; i < numElementsInArray (sliderStyleTypes); ++i)
if (s == sliderStyleToString (sliderStyleTypes[i]))
return sliderStyleTypes[i];
jassertfalse;
return Slider::LinearHorizontal;
}
static String textBoxPosToString (const Slider::TextEntryBoxPosition pos)
{
switch (pos)
{
case Slider::NoTextBox: return "NoTextBox";
case Slider::TextBoxLeft: return "TextBoxLeft";
case Slider::TextBoxRight: return "TextBoxRight";
case Slider::TextBoxAbove: return "TextBoxAbove";
case Slider::TextBoxBelow: return "TextBoxBelow";
default: jassertfalse; break;
}
return {};
}
static Slider::TextEntryBoxPosition stringToTextBoxPos (const String& s)
{
for (int i = 0; i < numElementsInArray (sliderTextBoxPositions); ++i)
if (s == textBoxPosToString (sliderTextBoxPositions[i]))
return sliderTextBoxPositions[i];
jassertfalse;
return Slider::TextBoxLeft;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class TextButtonHandler : public ButtonHandler
{
public:
TextButtonHandler()
: ButtonHandler ("Text Button", "juce::TextButton", typeid (TextButton), 150, 24)
{
registerColour (juce::TextButton::buttonColourId, "background (normal)", "bgColOff");
registerColour (juce::TextButton::buttonOnColourId, "background (on)", "bgColOn");
registerColour (juce::TextButton::textColourOffId, "text colour (normal)", "textCol");
registerColour (juce::TextButton::textColourOnId, "text colour (on)", "textColOn");
}
Component* createNewComponent (JucerDocument*) override
{
return new TextButton ("new button", String());
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
addColourProperties (component, document, props);
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
return ButtonHandler::createXmlFor (comp, layout);
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
return ButtonHandler::restoreFromXml (xml, comp, layout);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
String s;
s << getColourIntialisationCode (component, memberVariableName)
<< '\n';
code.constructorCode += s;
}
};

View File

@ -0,0 +1,433 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class TextEditorHandler : public ComponentTypeHandler
{
public:
TextEditorHandler()
: ComponentTypeHandler ("Text Editor", "juce::TextEditor", typeid (TextEditor), 150, 24)
{
registerColour (juce::TextEditor::textColourId, "text", "textcol");
registerColour (juce::TextEditor::backgroundColourId, "background", "bkgcol");
registerColour (juce::TextEditor::highlightColourId, "highlight", "hilitecol");
registerColour (juce::TextEditor::outlineColourId, "outline", "outlinecol");
registerColour (juce::TextEditor::shadowColourId, "shadow", "shadowcol");
registerColour (juce::CaretComponent::caretColourId, "caret", "caretcol");
}
Component* createNewComponent (JucerDocument*) override
{
return new TextEditor ("new text editor");
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
XmlElement* e = ComponentTypeHandler::createXmlFor (comp, layout);
TextEditor* te = (TextEditor*) comp;
e->setAttribute ("initialText", comp->getProperties() ["initialText"].toString());
e->setAttribute ("multiline", te->isMultiLine());
e->setAttribute ("retKeyStartsLine", te->getReturnKeyStartsNewLine());
e->setAttribute ("readonly", te->isReadOnly());
e->setAttribute ("scrollbars", te->areScrollbarsShown());
e->setAttribute ("caret", te->isCaretVisible());
e->setAttribute ("popupmenu", te->isPopupMenuEnabled());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
TextEditor* te = (TextEditor*) comp;
TextEditor defaultEditor;
te->setMultiLine (xml.getBoolAttribute ("multiline", defaultEditor.isMultiLine()));
te->setReturnKeyStartsNewLine (xml.getBoolAttribute ("retKeyStartsLine", defaultEditor.getReturnKeyStartsNewLine()));
te->setReadOnly (xml.getBoolAttribute ("readonly", defaultEditor.isReadOnly()));
te->setScrollbarsShown (xml.getBoolAttribute ("scrollbars", defaultEditor.areScrollbarsShown()));
te->setCaretVisible (xml.getBoolAttribute ("caret", defaultEditor.isCaretVisible()));
te->setPopupMenuEnabled (xml.getBoolAttribute ("popupmenu", defaultEditor.isPopupMenuEnabled()));
const String initialText (xml.getStringAttribute ("initialText"));
te->setText (initialText, false);
te->getProperties().set ("initialText", initialText);
return true;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* t = dynamic_cast<TextEditor*> (component))
{
props.add (new TextEditorInitialTextProperty (t, document));
props.add (new TextEditorMultiLineProperty (t, document));
props.add (new TextEditorReadOnlyProperty (t, document));
props.add (new TextEditorScrollbarsProperty (t, document));
props.add (new TextEditorCaretProperty (t, document));
props.add (new TextEditorPopupMenuProperty (t, document));
addColourProperties (t, document, props);
}
}
String getCreationParameters (GeneratedCode&, Component* component) override
{
return quotedString (component->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
if (auto* te = dynamic_cast<TextEditor*> (component))
{
String s;
s << memberVariableName << "->setMultiLine (" << CodeHelpers::boolLiteral (te->isMultiLine()) << ");\n"
<< memberVariableName << "->setReturnKeyStartsNewLine (" << CodeHelpers::boolLiteral (te->getReturnKeyStartsNewLine()) << ");\n"
<< memberVariableName << "->setReadOnly (" << CodeHelpers::boolLiteral (te->isReadOnly()) << ");\n"
<< memberVariableName << "->setScrollbarsShown (" << CodeHelpers::boolLiteral (te->areScrollbarsShown()) << ");\n"
<< memberVariableName << "->setCaretVisible (" << CodeHelpers::boolLiteral (te->isCaretVisible()) << ");\n"
<< memberVariableName << "->setPopupMenuEnabled (" << CodeHelpers::boolLiteral (te->isPopupMenuEnabled()) << ");\n"
<< getColourIntialisationCode (component, memberVariableName)
<< memberVariableName << "->setText (" << quotedString (te->getProperties() ["initialText"].toString(), code.shouldUseTransMacro()) << ");\n\n";
code.constructorCode += s;
}
}
private:
//==============================================================================
class TextEditorMultiLineProperty : public ComponentChoiceProperty <TextEditor>
{
public:
TextEditorMultiLineProperty (TextEditor* comp, JucerDocument& doc)
: ComponentChoiceProperty <TextEditor> ("mode", comp, doc)
{
choices.add ("single line");
choices.add ("multi-line, return key starts new line");
choices.add ("multi-line, return key disabled");
}
void setIndex (int newIndex)
{
document.perform (new TextEditorMultilineChangeAction (component, *document.getComponentLayout(), newIndex),
"Change TextEditor multiline mode");
}
int getIndex() const
{
return component->isMultiLine() ? (component->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
}
private:
class TextEditorMultilineChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorMultilineChangeAction (TextEditor* const comp, ComponentLayout& l, const int newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->isMultiLine() ? (comp->getReturnKeyStartsNewLine() ? 1 : 2) : 0;
}
bool perform()
{
showCorrectTab();
getComponent()->setMultiLine (newState > 0);
getComponent()->setReturnKeyStartsNewLine (newState == 1);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setMultiLine (oldState > 0);
getComponent()->setReturnKeyStartsNewLine (oldState == 1);
changed();
return true;
}
int newState, oldState;
};
};
//==============================================================================
class TextEditorReadOnlyProperty : public ComponentBooleanProperty <TextEditor>
{
public:
TextEditorReadOnlyProperty (TextEditor* comp, JucerDocument& doc)
: ComponentBooleanProperty <TextEditor> ("editable", "Editable", "Editable", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new TextEditorReadonlyChangeAction (component, *document.getComponentLayout(), ! newState),
"Change TextEditor read-only mode");
}
bool getState() const { return ! component->isReadOnly(); }
private:
class TextEditorReadonlyChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorReadonlyChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->isReadOnly();
}
bool perform()
{
showCorrectTab();
getComponent()->setReadOnly (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setReadOnly (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class TextEditorScrollbarsProperty : public ComponentBooleanProperty <TextEditor>
{
public:
TextEditorScrollbarsProperty (TextEditor* comp, JucerDocument& doc)
: ComponentBooleanProperty <TextEditor> ("scrollbars", "Scrollbars enabled", "Scrollbars enabled", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new TextEditorScrollbarChangeAction (component, *document.getComponentLayout(), newState),
"Change TextEditor scrollbars");
}
bool getState() const { return component->areScrollbarsShown(); }
private:
class TextEditorScrollbarChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorScrollbarChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->areScrollbarsShown();
}
bool perform()
{
showCorrectTab();
getComponent()->setScrollbarsShown (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setScrollbarsShown (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class TextEditorCaretProperty : public ComponentBooleanProperty <TextEditor>
{
public:
TextEditorCaretProperty (TextEditor* comp, JucerDocument& doc)
: ComponentBooleanProperty <TextEditor> ("caret", "Caret visible", "Caret visible", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new TextEditorCaretChangeAction (component, *document.getComponentLayout(), newState),
"Change TextEditor caret");
}
bool getState() const { return component->isCaretVisible(); }
private:
class TextEditorCaretChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorCaretChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->isCaretVisible();
}
bool perform()
{
showCorrectTab();
getComponent()->setCaretVisible (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setCaretVisible (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class TextEditorPopupMenuProperty : public ComponentBooleanProperty <TextEditor>
{
public:
TextEditorPopupMenuProperty (TextEditor* comp, JucerDocument& doc)
: ComponentBooleanProperty <TextEditor> ("popup menu", "Popup menu enabled", "Popup menu enabled", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new TextEditorPopupMenuChangeAction (component, *document.getComponentLayout(), newState),
"Change TextEditor popup menu");
}
bool getState() const { return component->isPopupMenuEnabled(); }
private:
class TextEditorPopupMenuChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorPopupMenuChangeAction (TextEditor* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->isPopupMenuEnabled();
}
bool perform()
{
showCorrectTab();
getComponent()->setPopupMenuEnabled (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setPopupMenuEnabled (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class TextEditorInitialTextProperty : public ComponentTextProperty <TextEditor>
{
public:
TextEditorInitialTextProperty (TextEditor* comp, JucerDocument& doc)
: ComponentTextProperty <TextEditor> ("initial text", 10000, true, comp, doc)
{}
void setText (const String& newText) override
{
document.perform (new TextEditorInitialTextChangeAction (component, *document.getComponentLayout(), newText),
"Change TextEditor initial text");
}
String getText() const override
{
return component->getProperties() ["initialText"];
}
private:
class TextEditorInitialTextChangeAction : public ComponentUndoableAction <TextEditor>
{
public:
TextEditorInitialTextChangeAction (TextEditor* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <TextEditor> (comp, l),
newState (newState_)
{
oldState = comp->getProperties() ["initialText"];
}
bool perform()
{
showCorrectTab();
getComponent()->setText (newState, false);
getComponent()->getProperties().set ("initialText", newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setText (oldState, false);
getComponent()->getProperties().set ("initialText", oldState);
changed();
return true;
}
String newState, oldState;
};
};
};

View File

@ -0,0 +1,146 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class ToggleButtonHandler : public ButtonHandler
{
public:
ToggleButtonHandler()
: ButtonHandler ("Toggle Button", "juce::ToggleButton", typeid (ToggleButton), 150, 24)
{
registerColour (juce::ToggleButton::textColourId, "text colour", "txtcol");
}
Component* createNewComponent (JucerDocument*) override
{
return new ToggleButton ("new toggle button");
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ButtonHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
if (auto* tb = dynamic_cast<ToggleButton*> (component))
props.add (new ToggleButtonStateProperty (tb, document));
addColourProperties (component, document, props);
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
ToggleButton* tb = (ToggleButton*) comp;
XmlElement* e = ButtonHandler::createXmlFor (comp, layout);
e->setAttribute ("state", tb->getToggleState());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
ToggleButton* const tb = (ToggleButton*) comp;
if (! ButtonHandler::restoreFromXml (xml, comp, layout))
return false;
tb->setToggleState (xml.getBoolAttribute ("state", false), dontSendNotification);
return true;
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
ButtonHandler::fillInCreationCode (code, component, memberVariableName);
ToggleButton* const tb = dynamic_cast<ToggleButton*> (component);
String s;
if (tb->getToggleState())
s << memberVariableName << "->setToggleState (true, juce::dontSendNotification);\n";
s << getColourIntialisationCode (component, memberVariableName)
<< '\n';
code.constructorCode += s;
}
private:
class ToggleButtonStateProperty : public ComponentBooleanProperty <ToggleButton>
{
public:
ToggleButtonStateProperty (ToggleButton* button_, JucerDocument& doc)
: ComponentBooleanProperty <ToggleButton> ("initial state", "on", "off", button_, doc)
{
}
void setState (bool newState)
{
document.perform (new ToggleStateChangeAction (component, *document.getComponentLayout(), newState),
"Change ToggleButton state");
}
bool getState() const
{
return component->getToggleState();
}
private:
class ToggleStateChangeAction : public ComponentUndoableAction <ToggleButton>
{
public:
ToggleStateChangeAction (ToggleButton* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <ToggleButton> (comp, l),
newState (newState_)
{
oldState = comp->getToggleState();
}
bool perform()
{
showCorrectTab();
getComponent()->setToggleState (newState, dontSendNotification);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setToggleState (oldState, dontSendNotification);
changed();
return true;
}
bool newState, oldState;
};
};
};

View File

@ -0,0 +1,265 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class TreeViewHandler : public ComponentTypeHandler
{
public:
TreeViewHandler()
: ComponentTypeHandler ("TreeView", "juce::TreeView", typeid (DemoTreeView), 150, 150)
{
registerColour (juce::TreeView::backgroundColourId, "background", "backgroundColour");
registerColour (juce::TreeView::linesColourId, "lines", "linecol");
}
Component* createNewComponent (JucerDocument*) override
{
return new DemoTreeView();
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
TreeView* const t = dynamic_cast<TreeView*> (comp);
XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("rootVisible", t->isRootItemVisible());
e->setAttribute ("openByDefault", t->areItemsOpenByDefault());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
TreeView defaultTreeView;
TreeView* const t = dynamic_cast<TreeView*> (comp);
t->setRootItemVisible (xml.getBoolAttribute ("rootVisible", defaultTreeView.isRootItemVisible()));
t->setDefaultOpenness (xml.getBoolAttribute ("openByDefault", defaultTreeView.areItemsOpenByDefault()));
return true;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
auto* t = dynamic_cast<TreeView*> (component);
props.add (new TreeViewRootItemProperty (t, document));
props.add (new TreeViewRootOpennessProperty (t, document));
addColourProperties (t, document, props);
}
String getCreationParameters (GeneratedCode&, Component* comp) override
{
return quotedString (comp->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
TreeView defaultTreeView;
TreeView* const t = dynamic_cast<TreeView*> (component);
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
if (defaultTreeView.isRootItemVisible() != t->isRootItemVisible())
{
code.constructorCode
<< memberVariableName << "->setRootItemVisible ("
<< CodeHelpers::boolLiteral (t->isRootItemVisible()) << ");\n";
}
if (defaultTreeView.areItemsOpenByDefault() != t->areItemsOpenByDefault())
{
code.constructorCode
<< memberVariableName << "->setDefaultOpenness ("
<< CodeHelpers::boolLiteral (t->areItemsOpenByDefault()) << ");\n";
}
code.constructorCode << getColourIntialisationCode (component, memberVariableName);
code.constructorCode << "\n";
}
private:
//==============================================================================
class DemoTreeView : public TreeView
{
public:
DemoTreeView()
: TreeView ("new treeview")
{
setRootItem (new DemoTreeViewItem ("Demo root node", 4));
}
~DemoTreeView()
{
deleteRootItem();
}
private:
class DemoTreeViewItem : public TreeViewItem
{
public:
DemoTreeViewItem (const String& name_, const int numItems)
: name (name_)
{
for (int i = 0; i < numItems; ++i)
addSubItem (new DemoTreeViewItem ("Demo sub-node " + String (i), numItems - 1));
}
void paintItem (Graphics& g, int width, int height) override
{
if (isSelected())
g.fillAll (Colours::lightblue);
g.setColour (Colours::black);
g.setFont ((float) height * 0.7f);
g.drawText (name, 4, 0, width - 4, height, Justification::centredLeft, true);
}
bool mightContainSubItems() override
{
return true;
}
const String name;
};
};
//==============================================================================
class TreeViewRootItemProperty : public ComponentBooleanProperty <TreeView>
{
public:
TreeViewRootItemProperty (TreeView* comp, JucerDocument& doc)
: ComponentBooleanProperty <TreeView> ("show root item", "Root item visible", "Root item visible", comp, doc)
{
}
void setState (bool newState)
{
document.perform (new TreeviewRootChangeAction (component, *document.getComponentLayout(), newState),
"Change TreeView root item");
}
bool getState() const
{
return component->isRootItemVisible();
}
private:
class TreeviewRootChangeAction : public ComponentUndoableAction <TreeView>
{
public:
TreeviewRootChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TreeView> (comp, l),
newState (newState_)
{
oldState = comp->isRootItemVisible();
}
bool perform()
{
showCorrectTab();
getComponent()->setRootItemVisible (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setRootItemVisible (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
//==============================================================================
class TreeViewRootOpennessProperty : public ComponentChoiceProperty <TreeView>
{
public:
TreeViewRootOpennessProperty (TreeView* comp, JucerDocument& doc)
: ComponentChoiceProperty <TreeView> ("default openness", comp, doc)
{
choices.add ("Items open by default");
choices.add ("Items closed by default");
}
void setIndex (int newIndex)
{
document.perform (new TreeviewOpennessChangeAction (component, *document.getComponentLayout(), newIndex == 0),
"Change TreeView openness");
}
int getIndex() const
{
return component->areItemsOpenByDefault() ? 0 : 1;
}
private:
class TreeviewOpennessChangeAction : public ComponentUndoableAction <TreeView>
{
public:
TreeviewOpennessChangeAction (TreeView* const comp, ComponentLayout& l, const bool newState_)
: ComponentUndoableAction <TreeView> (comp, l),
newState (newState_)
{
oldState = comp->areItemsOpenByDefault();
}
bool perform()
{
showCorrectTab();
getComponent()->setDefaultOpenness (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setDefaultOpenness (oldState);
changed();
return true;
}
bool newState, oldState;
};
};
};

View File

@ -0,0 +1,666 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
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 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-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.
==============================================================================
*/
#pragma once
//==============================================================================
class ViewportHandler : public ComponentTypeHandler
{
public:
ViewportHandler()
: ComponentTypeHandler ("Viewport", "juce::Viewport", typeid (UpdatingViewport), 150, 150)
{}
Component* createNewComponent (JucerDocument*) override
{
Viewport* const v = new UpdatingViewport ("new viewport");
v->setViewedComponent (new ViewportDemoContentComp());
return v;
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
Viewport* const v = dynamic_cast<Viewport*> (comp);
XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
e->setAttribute ("vscroll", v->isVerticalScrollBarShown());
e->setAttribute ("hscroll", v->isHorizontalScrollBarShown());
e->setAttribute ("scrollbarThickness", v->getScrollBarThickness());
e->setAttribute ("contentType", getViewportContentType (v));
e->setAttribute ("jucerFile", getViewportJucerComponentFile (v));
e->setAttribute ("contentClass", getViewportGenericComponentClass (v));
e->setAttribute ("constructorParams", getViewportConstructorParams (v));
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
Viewport defaultViewport;
Viewport* const v = dynamic_cast<Viewport*> (comp);
v->setScrollBarsShown (xml.getBoolAttribute ("vscroll", defaultViewport.isVerticalScrollBarShown()),
xml.getBoolAttribute ("hscroll", defaultViewport.isHorizontalScrollBarShown()));
v->setScrollBarThickness (xml.getIntAttribute ("scrollbarThickness", defaultViewport.getScrollBarThickness()));
setViewportJucerComponentFile (v, xml.getStringAttribute ("jucerFile", String()));
setViewportGenericComponentClass (v, xml.getStringAttribute ("contentClass"));
setViewportContentType (v, xml.getIntAttribute ("contentType", 0));
setViewportConstructorParams (v, xml.getStringAttribute ("constructorParams"));
return true;
}
void getEditableProperties (Component* component, JucerDocument& document,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, document, props, multipleSelected);
if (multipleSelected)
return;
auto* v = dynamic_cast<Viewport*> (component);
props.add (new ViewportScrollbarShownProperty (v, document, true));
props.add (new ViewportScrollbarShownProperty (v, document, false));
props.add (new ViewportScrollbarSizeProperty (v, document));
props.add (new ViewportContentTypeProperty (v, document));
if (getViewportContentType (v) == 1)
{
props.add (new ViewportJucerFileProperty (v, document));
props.add (new ConstructorParamsProperty (v, document));
}
else if (getViewportContentType (v) == 2)
{
props.add (new ViewportContentClassProperty (v, document));
props.add (new ConstructorParamsProperty (v, document));
}
}
String getCreationParameters (GeneratedCode&, Component* comp) override
{
return quotedString (comp->getName(), false);
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
Viewport defaultViewport;
Viewport* const v = dynamic_cast<Viewport*> (component);
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
if (defaultViewport.isVerticalScrollBarShown() != v->isVerticalScrollBarShown()
|| defaultViewport.isHorizontalScrollBarShown() != v->isHorizontalScrollBarShown())
{
code.constructorCode
<< memberVariableName << "->setScrollBarsShown ("
<< CodeHelpers::boolLiteral (v->isVerticalScrollBarShown()) << ", "
<< CodeHelpers::boolLiteral (v->isHorizontalScrollBarShown()) << ");\n";
}
if (defaultViewport.getScrollBarThickness() != v->getScrollBarThickness())
{
code.constructorCode
<< memberVariableName << "->setScrollBarThickness ("
<< v->getScrollBarThickness() << ");\n";
}
if (getViewportContentType (v) != 0)
{
String classNm (getViewportGenericComponentClass (v));
if (getViewportContentType (v) == 1)
{
File file;
const String filename (getViewportJucerComponentFile (v));
if (filename.isNotEmpty())
file = code.document->getCppFile().getSiblingFile (filename);
std::unique_ptr<JucerDocument> doc (JucerDocument::createForCppFile (nullptr, file));
if (doc != nullptr)
{
code.includeFilesCPP.add (File::createFileWithoutCheckingPath (doc->getHeaderFile()
.getRelativePathFrom (code.document->getCppFile().getParentDirectory())
.replaceCharacter ('\\', '/')));
classNm = doc->getClassName();
}
else
{
classNm = String();
}
}
if (classNm.isNotEmpty())
{
code.constructorCode
<< memberVariableName << "->setViewedComponent (new "
<< classNm;
if (getViewportConstructorParams (v).trim().isNotEmpty())
{
code.constructorCode << " (" << getViewportConstructorParams (v).trim() << "));\n";
}
else
{
code.constructorCode << "());\n";
}
}
}
code.constructorCode << "\n";
}
static void updateViewportContentComp (Viewport* vp)
{
if (getViewportContentType (vp) == 1)
{
JucerDocument* doc = findParentDocument (vp);
auto tc = new TestComponent (doc, nullptr, false);
tc->setFilename (getViewportJucerComponentFile (vp));
tc->setToInitialSize();
vp->setViewedComponent (tc);
}
else
{
vp->setViewedComponent (new ViewportDemoContentComp());
}
}
static int getViewportContentType (Viewport* vp)
{
return vp->getProperties() ["contentType"];
}
static void setViewportContentType (Viewport* vp, int newValue)
{
if (newValue != getViewportContentType (vp))
{
vp->getProperties().set ("contentType", newValue);
updateViewportContentComp (vp);
}
}
static String getViewportJucerComponentFile (Viewport* vp)
{
return vp->getProperties() ["jucerFile"].toString();
}
static void setViewportJucerComponentFile (Viewport* vp, const String& file)
{
if (file != getViewportJucerComponentFile (vp))
{
vp->getProperties().set ("jucerFile", file);
updateViewportContentComp (vp);
}
}
static String getViewportGenericComponentClass (Viewport* vp)
{
return vp->getProperties() ["contentClass"].toString();
}
static void setViewportGenericComponentClass (Viewport* vp, const String& name)
{
if (name != getViewportGenericComponentClass (vp))
{
vp->getProperties().set ("contentClass", name);
updateViewportContentComp (vp);
}
}
static String getViewportConstructorParams (Viewport* vp)
{
return vp->getProperties() ["constructorParams"].toString();
}
static void setViewportConstructorParams (Viewport* vp, const String& newParams)
{
if (newParams != getViewportConstructorParams (vp))
{
vp->getProperties().set ("constructorParams", newParams);
updateViewportContentComp (vp);
}
}
private:
//==============================================================================
class UpdatingViewport : public Viewport
{
public:
UpdatingViewport (const String& name)
: Viewport (name)
{
}
void parentHierarchyChanged()
{
Viewport::parentHierarchyChanged();
updateViewportContentComp (this);
}
};
//==============================================================================
struct ViewportDemoContentComp : public Component
{
ViewportDemoContentComp()
{
setSize (2048, 2048);
}
void paint (Graphics& g) override
{
g.fillCheckerBoard (getLocalBounds().toFloat(), 50.0f, 50.0f,
Colours::lightgrey.withAlpha (0.5f),
Colours::darkgrey.withAlpha (0.5f));
}
};
//==============================================================================
class ViewportScrollbarShownProperty : public ComponentBooleanProperty <Viewport>
{
public:
ViewportScrollbarShownProperty (Viewport* comp, JucerDocument& doc, const bool vertical_)
: ComponentBooleanProperty <Viewport> (vertical_ ? "V scrollbar" : "H scrollbar",
"enabled", "enabled",
comp, doc),
vertical (vertical_)
{
}
void setState (bool newState)
{
document.perform (new ViewportScrollbarChangeAction (component, *document.getComponentLayout(), vertical, newState),
"Change Viewport scrollbar");
}
bool getState() const
{
return vertical ? component->isVerticalScrollBarShown()
: component->isHorizontalScrollBarShown();
}
const bool vertical;
private:
class ViewportScrollbarChangeAction : public ComponentUndoableAction <Viewport>
{
public:
ViewportScrollbarChangeAction (Viewport* const comp, ComponentLayout& l, const bool vertical_, const bool newState_)
: ComponentUndoableAction <Viewport> (comp, l),
vertical (vertical_),
newState (newState_)
{
oldState = vertical ? comp->isVerticalScrollBarShown()
: comp->isHorizontalScrollBarShown();
}
bool perform()
{
showCorrectTab();
if (vertical)
getComponent()->setScrollBarsShown (newState, getComponent()->isHorizontalScrollBarShown());
else
getComponent()->setScrollBarsShown (getComponent()->isVerticalScrollBarShown(), newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
if (vertical)
getComponent()->setScrollBarsShown (oldState, getComponent()->isHorizontalScrollBarShown());
else
getComponent()->setScrollBarsShown (getComponent()->isVerticalScrollBarShown(), oldState);
changed();
return true;
}
bool vertical, newState, oldState;
};
};
//==============================================================================
class ViewportScrollbarSizeProperty : public SliderPropertyComponent,
private ChangeListener
{
public:
ViewportScrollbarSizeProperty (Viewport* comp, JucerDocument& doc)
: SliderPropertyComponent ("scrollbar size", 3.0, 30.0, 1.0, 1.0),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ViewportScrollbarSizeProperty() override
{
document.removeChangeListener (this);
}
void setValue (double newValue) override
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new ViewportScrollbarSizeChangeAction (component, *document.getComponentLayout(), roundToInt (newValue)),
"Change Viewport scrollbar size");
}
double getValue() const override
{
return component->getScrollBarThickness();
}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
Viewport* component;
JucerDocument& document;
private:
class ViewportScrollbarSizeChangeAction : public ComponentUndoableAction <Viewport>
{
public:
ViewportScrollbarSizeChangeAction (Viewport* const comp, ComponentLayout& l, const int newState_)
: ComponentUndoableAction <Viewport> (comp, l),
newState (newState_)
{
oldState = comp->getScrollBarThickness();
}
bool perform()
{
showCorrectTab();
getComponent()->setScrollBarThickness (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setScrollBarThickness (newState);
changed();
return true;
}
int newState, oldState;
};
};
//==============================================================================
class ViewportContentTypeProperty : public ComponentChoiceProperty <Viewport>
{
public:
ViewportContentTypeProperty (Viewport* comp, JucerDocument& doc)
: ComponentChoiceProperty <Viewport> ("content", comp, doc)
{
choices.add ("No content component");
choices.add ("Jucer content component");
choices.add ("Named content component");
}
void setIndex (int newIndex)
{
document.perform (new ViewportContentTypeChangeAction (component, *document.getComponentLayout(), newIndex),
"Change Viewport content type");
}
int getIndex() const
{
return getViewportContentType (component);
}
private:
class ViewportContentTypeChangeAction : public ComponentUndoableAction <Viewport>
{
public:
ViewportContentTypeChangeAction (Viewport* const comp, ComponentLayout& l, const int newValue_)
: ComponentUndoableAction <Viewport> (comp, l),
newValue (newValue_)
{
oldValue = getViewportContentType (comp);
}
bool perform()
{
showCorrectTab();
setViewportContentType (getComponent(), newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
setViewportContentType (getComponent(), oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
int newValue, oldValue;
};
};
//==============================================================================
class ViewportJucerFileProperty : public FilePropertyComponent,
private ChangeListener
{
public:
ViewportJucerFileProperty (Viewport* const comp, JucerDocument& doc)
: FilePropertyComponent ("Jucer file", false, true),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~ViewportJucerFileProperty() override
{
document.removeChangeListener (this);
}
void setFile (const File& newFile) override
{
document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(),
newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
.replaceCharacter ('\\', '/')),
"Change Projucer component file");
}
File getFile() const override
{
auto filename = getViewportJucerComponentFile (component);
if (filename.isEmpty())
return {};
return document.getCppFile().getSiblingFile (filename);
}
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
Viewport* const component;
JucerDocument& document;
class JucerCompFileChangeAction : public ComponentUndoableAction <Viewport>
{
public:
JucerCompFileChangeAction (Viewport* const comp, ComponentLayout& l, const String& newState_)
: ComponentUndoableAction <Viewport> (comp, l),
newState (newState_)
{
oldState = getViewportJucerComponentFile (comp);
}
bool perform()
{
showCorrectTab();
setViewportJucerComponentFile (getComponent(), newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
setViewportJucerComponentFile (getComponent(), oldState);
changed();
return true;
}
String newState, oldState;
};
};
//==============================================================================
class ViewportContentClassProperty : public ComponentTextProperty <Viewport>
{
public:
ViewportContentClassProperty (Viewport* comp, JucerDocument& doc)
: ComponentTextProperty <Viewport> ("content class", 256, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new ViewportClassNameChangeAction (component, *document.getComponentLayout(), newText),
"Change Viewport content class");
}
String getText() const override
{
return getViewportGenericComponentClass (component);
}
private:
class ViewportClassNameChangeAction : public ComponentUndoableAction <Viewport>
{
public:
ViewportClassNameChangeAction (Viewport* const comp, ComponentLayout& l, const String& newValue_)
: ComponentUndoableAction <Viewport> (comp, l),
newValue (newValue_)
{
oldValue = getViewportGenericComponentClass (comp);
}
bool perform()
{
showCorrectTab();
setViewportGenericComponentClass (getComponent(), newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
setViewportGenericComponentClass (getComponent(), oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
String newValue, oldValue;
};
};
//==============================================================================
class ConstructorParamsProperty : public ComponentTextProperty <Viewport>
{
public:
ConstructorParamsProperty (Viewport* comp, JucerDocument& doc)
: ComponentTextProperty <Viewport> ("constructor params", 512, false, comp, doc)
{
}
void setText (const String& newText) override
{
document.perform (new ConstructorParamChangeAction (component, *document.getComponentLayout(), newText),
"Change Viewport content constructor params");
}
String getText() const override
{
return getViewportConstructorParams (component);
}
private:
class ConstructorParamChangeAction : public ComponentUndoableAction <Viewport>
{
public:
ConstructorParamChangeAction (Viewport* const comp, ComponentLayout& l, const String& newValue_)
: ComponentUndoableAction <Viewport> (comp, l),
newValue (newValue_)
{
oldValue = getViewportConstructorParams (comp);
}
bool perform()
{
showCorrectTab();
setViewportConstructorParams (getComponent(), newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
setViewportConstructorParams (getComponent(), oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
String newValue, oldValue;
};
};
};