1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Projucer/Source/ComponentEditor/Components/jucer_LabelHandler.h
2024-01-30 11:37:30 +00:00

774 lines
27 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - 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 7 End-User License
Agreement and JUCE Privacy Policy.
End User License Agreement: www.juce.com/juce-7-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() override
{
showCorrectTab();
getComponent()->setText (newState, dontSendNotification);
changed();
return true;
}
bool undo() override
{
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) override
{
document.perform (new LabelEditableChangeAction (component, *document.getComponentLayout(), newIndex),
"Change Label editability");
}
int getIndex() const override
{
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() override
{
showCorrectTab();
getComponent()->setEditable (newState == 1, newState >= 1, getComponent()->doesLossOfFocusDiscardChanges());
changed();
layout.getSelectedSet().changed();
return true;
}
bool undo() override
{
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) override
{
document.perform (new LabelFocusLossChangeAction (component, *document.getComponentLayout(), newIndex == 0),
"Change Label focus behaviour");
}
int getIndex() const override
{
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() override
{
showCorrectTab();
getComponent()->setEditable (getComponent()->isEditableOnSingleClick(),
getComponent()->isEditableOnDoubleClick(),
newState);
changed();
return true;
}
bool undo() override
{
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() override
{
showCorrectTab();
getComponent()->setJustificationType (newState);
changed();
return true;
}
bool undo() override
{
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() override
{
showCorrectTab();
getComponent()->getProperties().set ("typefaceName", newState);
LabelHandler::updateLabelFont (getComponent());
changed();
return true;
}
bool undo() override
{
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() override
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setHeight ((float) newState);
getComponent()->setFont (f);
changed();
return true;
}
bool undo() override
{
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() override
{
showCorrectTab();
getComponent()->setFont (newState);
changed();
return true;
}
bool undo() override
{
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() override
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setExtraKerningFactor ((float) newState);
getComponent()->setFont (f);
changed();
return true;
}
bool undo() override
{
showCorrectTab();
Font f (getComponent()->getFont());
f.setExtraKerningFactor ((float) oldState);
getComponent()->setFont (f);
changed();
return true;
}
float newState, oldState;
};
};
};