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_TabbedComponentHandler.h

1193 lines
41 KiB
C++

/*
==============================================================================
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 TabbedComponentHandler : public ComponentTypeHandler
{
public:
TabbedComponentHandler()
: ComponentTypeHandler ("Tabbed Component", "juce::TabbedComponent", typeid (TabbedComponent), 200, 150)
{}
Component* createNewComponent (JucerDocument*) override
{
TabbedComponent* const t = new TabbedComponent (TabbedButtonBar::TabsAtTop);
t->setName ("new tabbed component");
for (int i = 3; --i >= 0;)
addNewTab (t);
return t;
}
XmlElement* createXmlFor (Component* comp, const ComponentLayout* layout) override
{
TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
XmlElement* const e = ComponentTypeHandler::createXmlFor (comp, layout);
if (t->getOrientation() == TabbedButtonBar::TabsAtTop) e->setAttribute ("orientation", "top");
else if (t->getOrientation() == TabbedButtonBar::TabsAtBottom) e->setAttribute ("orientation", "bottom");
else if (t->getOrientation() == TabbedButtonBar::TabsAtLeft) e->setAttribute ("orientation", "left");
else if (t->getOrientation() == TabbedButtonBar::TabsAtRight) e->setAttribute ("orientation", "right");
e->setAttribute ("tabBarDepth", t->getTabBarDepth());
e->setAttribute ("initialTab", t->getCurrentTabIndex());
for (int i = 0; i < t->getNumTabs(); ++i)
e->addChildElement (getTabState (t, i).release());
return e;
}
bool restoreFromXml (const XmlElement& xml, Component* comp, const ComponentLayout* layout) override
{
if (! ComponentTypeHandler::restoreFromXml (xml, comp, layout))
return false;
TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
if (xml.getStringAttribute ("orientation") == "top") t->setOrientation (TabbedButtonBar::TabsAtTop);
else if (xml.getStringAttribute ("orientation") == "bottom") t->setOrientation (TabbedButtonBar::TabsAtBottom);
else if (xml.getStringAttribute ("orientation") == "left") t->setOrientation (TabbedButtonBar::TabsAtLeft);
else if (xml.getStringAttribute ("orientation") == "right") t->setOrientation (TabbedButtonBar::TabsAtRight);
TabbedComponent defaultTabComp (TabbedButtonBar::TabsAtTop);
t->setTabBarDepth (xml.getIntAttribute ("tabBarDepth", defaultTabComp.getTabBarDepth()));
t->clearTabs();
for (auto* e : xml.getChildIterator())
{
addNewTab (t);
restoreTabState (t, t->getNumTabs() - 1, *e);
}
t->setCurrentTabIndex (xml.getIntAttribute ("initialTab", 0));
return true;
}
void getEditableProperties (Component* component, JucerDocument& doc,
Array<PropertyComponent*>& props, bool multipleSelected) override
{
ComponentTypeHandler::getEditableProperties (component, doc, props, multipleSelected);
if (multipleSelected)
return;
if (auto* t = dynamic_cast<TabbedComponent*> (component))
{
props.add (new TabOrientationProperty (t, doc));
props.add (new TabDepthProperty (t, doc));
if (t->getNumTabs() > 0)
props.add (new TabInitialTabProperty (t, doc));
props.add (new TabAddTabProperty (t, doc));
if (t->getNumTabs() > 0)
props.add (new TabRemoveTabProperty (t, doc));
}
}
void addPropertiesToPropertyPanel (Component* comp, JucerDocument& doc,
PropertyPanel& panel, bool multipleSelected) override
{
ComponentTypeHandler::addPropertiesToPropertyPanel (comp, doc, panel, multipleSelected);
TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
for (int i = 0; i < t->getNumTabs(); ++i)
{
Array<PropertyComponent*> properties;
properties.add (new TabNameProperty (t, doc, i));
properties.add (new TabColourProperty (t, doc, i));
properties.add (new TabContentTypeProperty (t, doc, i));
if (isTabUsingJucerComp (t, i))
properties.add (new TabJucerFileProperty (t, doc, i));
else
properties.add (new TabContentClassProperty (t, doc, i));
properties.add (new TabContentConstructorParamsProperty (t, doc, i));
properties.add (new TabMoveProperty (t, doc, i, t->getNumTabs()));
panel.addSection ("Tab " + String (i), properties);
}
}
String getCreationParameters (GeneratedCode&, Component* comp) override
{
TabbedComponent* const t = dynamic_cast<TabbedComponent*> (comp);
switch (t->getOrientation())
{
case TabbedButtonBar::TabsAtTop: return "juce::TabbedButtonBar::TabsAtTop";
case TabbedButtonBar::TabsAtBottom: return "juce::TabbedButtonBar::TabsAtBottom";
case TabbedButtonBar::TabsAtLeft: return "juce::TabbedButtonBar::TabsAtLeft";
case TabbedButtonBar::TabsAtRight: return "juce::TabbedButtonBar::TabsAtRight";
default: jassertfalse; break;
}
return {};
}
void fillInCreationCode (GeneratedCode& code, Component* component, const String& memberVariableName) override
{
TabbedComponent* const t = dynamic_cast<TabbedComponent*> (component);
ComponentTypeHandler::fillInCreationCode (code, component, memberVariableName);
code.constructorCode
<< memberVariableName << "->setTabBarDepth (" << t->getTabBarDepth() << ");\n";
for (int i = 0; i < t->getNumTabs(); ++i)
{
String contentClassName;
if (isTabUsingJucerComp (t, i))
{
File jucerCpp = code.document->getCppFile().getSiblingFile (getTabJucerFile (t, i));
std::unique_ptr<JucerDocument> doc (JucerDocument::createForCppFile (nullptr, jucerCpp));
if (doc != nullptr)
{
code.includeFilesCPP.add (jucerCpp.withFileExtension (".h"));
contentClassName = doc->getClassName();
}
}
else
{
contentClassName = getTabClassName (t, i);
}
code.constructorCode
<< memberVariableName
<< "->addTab ("
<< quotedString (t->getTabNames() [i], code.shouldUseTransMacro())
<< ", "
<< CodeHelpers::colourToCode (t->getTabBackgroundColour (i));
if (contentClassName.isNotEmpty())
{
code.constructorCode << ", new " << contentClassName;
if (getTabConstructorParams (t, i).trim().isNotEmpty())
code.constructorCode << " ";
code.constructorCode << "(" << getTabConstructorParams (t, i).trim() << "), true);\n";
}
else
{
code.constructorCode << ", 0, false);\n";
}
}
code.constructorCode
<< memberVariableName << "->setCurrentTabIndex (" << t->getCurrentTabIndex() << ");\n";
code.constructorCode << "\n";
}
//==============================================================================
static void addNewTab (TabbedComponent* tc, const int insertIndex = -1)
{
tc->addTab ("Tab " + String (tc->getNumTabs()), Colours::lightgrey,
new TabDemoContentComp(), true, insertIndex);
}
//==============================================================================
static std::unique_ptr<XmlElement> getTabState (TabbedComponent* tc, int tabIndex)
{
auto xml = std::make_unique<XmlElement> ("TAB");
xml->setAttribute ("name", tc->getTabNames() [tabIndex]);
xml->setAttribute ("colour", tc->getTabBackgroundColour (tabIndex).toString());
if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
{
xml->setAttribute ("useJucerComp", tdc->isUsingJucerComp);
xml->setAttribute ("contentClassName", tdc->contentClassName);
xml->setAttribute ("constructorParams", tdc->constructorParams);
xml->setAttribute ("jucerComponentFile", tdc->jucerComponentFile);
}
return xml;
}
static void restoreTabState (TabbedComponent* tc, int tabIndex, const XmlElement& xml)
{
tc->setTabName (tabIndex, xml.getStringAttribute ("name", "Tab"));
tc->setTabBackgroundColour (tabIndex, Colour::fromString (xml.getStringAttribute ("colour", Colours::lightgrey.toString())));
if (TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex)))
{
tdc->isUsingJucerComp = xml.getBoolAttribute ("useJucerComp", false);
tdc->contentClassName = xml.getStringAttribute ("contentClassName");
tdc->constructorParams = xml.getStringAttribute ("constructorParams");
tdc->jucerComponentFile = xml.getStringAttribute ("jucerComponentFile");
tdc->updateContent();
}
}
//==============================================================================
static bool isTabUsingJucerComp (TabbedComponent* tc, int tabIndex)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
return tdc != nullptr && tdc->isUsingJucerComp;
}
static void setTabUsingJucerComp (TabbedComponent* tc, int tabIndex, const bool b)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
if (tdc != nullptr)
{
tdc->isUsingJucerComp = b;
tdc->updateContent();
}
}
static String getTabClassName (TabbedComponent* tc, int tabIndex)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
return tdc != nullptr ? tdc->contentClassName : String();
}
static void setTabClassName (TabbedComponent* tc, int tabIndex, const String& newName)
{
TabDemoContentComp* const tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
if (tdc != nullptr)
{
tdc->contentClassName = newName;
tdc->updateContent();
}
}
static String getTabConstructorParams (TabbedComponent* tc, int tabIndex)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
return tdc != nullptr ? tdc->constructorParams : String();
}
static void setTabConstructorParams (TabbedComponent* tc, int tabIndex, const String& newParams)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
if (tdc != nullptr)
{
tdc->constructorParams = newParams;
tdc->updateContent();
}
}
static String getTabJucerFile (TabbedComponent* tc, int tabIndex)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
return tdc != nullptr ? tdc->jucerComponentFile : String();
}
static void setTabJucerFile (TabbedComponent* tc, int tabIndex, const String& newFile)
{
auto tdc = dynamic_cast<TabDemoContentComp*> (tc->getTabContentComponent (tabIndex));
jassert (tdc != nullptr);
if (tdc != nullptr)
{
tdc->jucerComponentFile = newFile;
tdc->updateContent();
}
}
private:
//==============================================================================
class TabDemoContentComp : public Component
{
public:
TabDemoContentComp()
: isUsingJucerComp (false)
{
setSize (2048, 2048);
}
void paint (Graphics& g) override
{
if (jucerComp == nullptr)
g.fillCheckerBoard (getLocalBounds().toFloat(), 50.0f, 50.0f,
Colour::greyLevel (0.9f).withAlpha (0.4f),
Colour::greyLevel (0.8f).withAlpha (0.4f));
}
void resized() override
{
if (jucerComp != nullptr)
{
jucerComp->setBounds (getLocalBounds());
setOpaque (jucerComp->isOpaque());
}
}
void updateContent()
{
if (isUsingJucerComp)
{
if (jucerComp == nullptr
|| jucerComp->getOwnerDocument() == nullptr
|| jucerComp->getFilename() != jucerComponentFile)
{
jucerComp.reset();
jucerComp.reset (new TestComponent (ComponentTypeHandler::findParentDocument (this), nullptr, false));
jucerComp->setFilename (jucerComponentFile);
jucerComp->setToInitialSize();
addAndMakeVisible (jucerComp.get());
}
}
else
{
jucerComp.reset();
}
resized();
}
void parentHierarchyChanged() override
{
updateContent();
}
bool isUsingJucerComp;
String contentClassName, constructorParams;
String jucerComponentFile;
std::unique_ptr<TestComponent> jucerComp;
};
//==============================================================================
class TabOrientationProperty : public ComponentChoiceProperty<TabbedComponent>
{
public:
TabOrientationProperty (TabbedComponent* comp, JucerDocument& doc)
: ComponentChoiceProperty<TabbedComponent> ("tab position", comp, doc)
{
choices.add ("Tabs at top");
choices.add ("Tabs at bottom");
choices.add ("Tabs at left");
choices.add ("Tabs at right");
}
void setIndex (int newIndex)
{
const TabbedButtonBar::Orientation orientations[] = { TabbedButtonBar::TabsAtTop,
TabbedButtonBar::TabsAtBottom,
TabbedButtonBar::TabsAtLeft,
TabbedButtonBar::TabsAtRight };
document.perform (new TabOrienationChangeAction (component, *document.getComponentLayout(), orientations [newIndex]),
"Change TabComponent orientation");
}
int getIndex() const
{
switch (component->getOrientation())
{
case TabbedButtonBar::TabsAtTop: return 0;
case TabbedButtonBar::TabsAtBottom: return 1;
case TabbedButtonBar::TabsAtLeft: return 2;
case TabbedButtonBar::TabsAtRight: return 3;
default: jassertfalse; break;
}
return 0;
}
private:
class TabOrienationChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabOrienationChangeAction (TabbedComponent* const comp, ComponentLayout& l, const TabbedButtonBar::Orientation newState_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
newState (newState_)
{
oldState = comp->getOrientation();
}
bool perform()
{
showCorrectTab();
getComponent()->setOrientation (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setOrientation (oldState);
changed();
return true;
}
TabbedButtonBar::Orientation newState, oldState;
};
};
//==============================================================================
class TabInitialTabProperty : public ComponentChoiceProperty<TabbedComponent>
{
public:
TabInitialTabProperty (TabbedComponent* comp, JucerDocument& doc)
: ComponentChoiceProperty<TabbedComponent> ("initial tab", comp, doc)
{
for (int i = 0; i < comp->getNumTabs(); ++i)
choices.add ("Tab " + String (i) + ": \"" + comp->getTabNames() [i] + "\"");
}
void setIndex (int newIndex)
{
document.perform (new InitialTabChangeAction (component, *document.getComponentLayout(), newIndex),
"Change initial tab");
}
int getIndex() const
{
return component->getCurrentTabIndex();
}
private:
class InitialTabChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
InitialTabChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
newValue (newValue_)
{
oldValue = comp->getCurrentTabIndex();
}
bool perform()
{
showCorrectTab();
getComponent()->setCurrentTabIndex (newValue);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setCurrentTabIndex (oldValue);
changed();
return true;
}
private:
int newValue, oldValue;
};
};
//==============================================================================
class TabDepthProperty : public SliderPropertyComponent,
private ChangeListener
{
public:
TabDepthProperty (TabbedComponent* comp, JucerDocument& doc)
: SliderPropertyComponent ("tab depth", 10.0, 80.0, 1.0, 1.0),
component (comp),
document (doc)
{
document.addChangeListener (this);
}
~TabDepthProperty() override
{
document.removeChangeListener (this);
}
void setValue (double newValue) override
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new TabDepthChangeAction (component, *document.getComponentLayout(), roundToInt (newValue)),
"Change TabComponent tab depth");
}
double getValue() const override
{
return component->getTabBarDepth();
}
TabbedComponent* const component;
JucerDocument& document;
private:
void changeListenerCallback (ChangeBroadcaster*) override
{
refresh();
}
class TabDepthChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabDepthChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int newState_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
newState (newState_)
{
oldState = comp->getTabBarDepth();
}
bool perform()
{
showCorrectTab();
getComponent()->setTabBarDepth (newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTabBarDepth (oldState);
changed();
return true;
}
int newState, oldState;
};
};
//==============================================================================
class TabAddTabProperty : public ButtonPropertyComponent
{
public:
TabAddTabProperty (TabbedComponent* comp, JucerDocument& doc)
: ButtonPropertyComponent ("add tab", false),
component (comp),
document (doc)
{
}
void buttonClicked()
{
document.perform (new AddTabAction (component, *document.getComponentLayout()),
"Add a new tab");
}
String getButtonText() const
{
return "Create a new tab";
}
TabbedComponent* const component;
JucerDocument& document;
private:
class AddTabAction : public ComponentUndoableAction<TabbedComponent>
{
public:
AddTabAction (TabbedComponent* const comp, ComponentLayout& l)
: ComponentUndoableAction<TabbedComponent> (comp, l)
{
}
bool perform()
{
showCorrectTab();
addNewTab (getComponent());
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->removeTab (getComponent()->getNumTabs() - 1);
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
};
};
//==============================================================================
class TabRemoveTabProperty : public ButtonPropertyComponent
{
public:
TabRemoveTabProperty (TabbedComponent* comp, JucerDocument& doc)
: ButtonPropertyComponent ("remove tab", true),
component (comp),
document (doc)
{
}
void buttonClicked()
{
const StringArray names (component->getTabNames());
PopupMenu m;
for (int i = 0; i < component->getNumTabs(); ++i)
m.addItem (i + 1, "Delete tab " + String (i)
+ ": \"" + names[i] + "\"");
const int r = m.showAt (this);
if (r > 0)
{
document.perform (new RemoveTabAction (component, *document.getComponentLayout(), r - 1),
"Remove a tab");
}
}
String getButtonText() const
{
return "Delete a tab...";
}
TabbedComponent* const component;
JucerDocument& document;
private:
class RemoveTabAction : public ComponentUndoableAction<TabbedComponent>
{
public:
RemoveTabAction (TabbedComponent* const comp, ComponentLayout& l, int indexToRemove_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
indexToRemove (indexToRemove_)
{
previousState = getTabState (comp, indexToRemove);
}
bool perform()
{
showCorrectTab();
getComponent()->removeTab (indexToRemove);
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
bool undo()
{
showCorrectTab();
addNewTab (getComponent(), indexToRemove);
restoreTabState (getComponent(), indexToRemove, *previousState);
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
private:
int indexToRemove;
std::unique_ptr<XmlElement> previousState;
};
};
//==============================================================================
class TabNameProperty : public ComponentTextProperty<TabbedComponent>
{
public:
TabNameProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
: ComponentTextProperty<TabbedComponent> ("name", 200, false, comp, doc),
tabIndex (tabIndex_)
{
}
void setText (const String& newText) override
{
document.perform (new TabNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
"Change tab name");
}
String getText() const override
{
return component->getTabNames() [tabIndex];
}
private:
int tabIndex;
class TabNameChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newValue (newValue_)
{
oldValue = comp->getTabNames() [tabIndex];
}
bool perform()
{
showCorrectTab();
getComponent()->setTabName (tabIndex, newValue);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTabName (tabIndex, oldValue);
changed();
return true;
}
private:
const int tabIndex;
String newValue, oldValue;
};
};
//==============================================================================
class TabColourProperty : public JucerColourPropertyComponent,
private ChangeListener
{
public:
TabColourProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
: JucerColourPropertyComponent ("colour", false),
component (comp),
document (doc),
tabIndex (tabIndex_)
{
document.addChangeListener (this);
}
~TabColourProperty() override
{
document.removeChangeListener (this);
}
void setColour (Colour newColour) override
{
document.getUndoManager().undoCurrentTransactionOnly();
document.perform (new TabColourChangeAction (component, *document.getComponentLayout(), tabIndex, newColour),
"Change tab colour");
}
Colour getColour() const override
{
return component->getTabBackgroundColour (tabIndex);
}
void resetToDefault() override
{
jassertfalse; // shouldn't get called
}
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
private:
TabbedComponent* component;
JucerDocument& document;
int tabIndex;
class TabColourChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabColourChangeAction (TabbedComponent* comp, ComponentLayout& l,
int tabIndex_, Colour newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newValue (newValue_)
{
oldValue = comp->getTabBackgroundColour (tabIndex);
}
bool perform()
{
showCorrectTab();
getComponent()->setTabBackgroundColour (tabIndex, newValue);
changed();
return true;
}
bool undo()
{
showCorrectTab();
getComponent()->setTabBackgroundColour (tabIndex, oldValue);
changed();
return true;
}
private:
const int tabIndex;
Colour newValue, oldValue;
};
};
//==============================================================================
class TabContentTypeProperty : public ComponentChoiceProperty<TabbedComponent>
{
public:
TabContentTypeProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
: ComponentChoiceProperty<TabbedComponent> ("content type", comp, doc),
tabIndex (tabIndex_)
{
choices.add ("Jucer content component");
choices.add ("Named content component");
}
void setIndex (int newIndex)
{
document.perform (new TabContentTypeChangeAction (component, *document.getComponentLayout(), tabIndex, newIndex == 0),
"Change tab content type");
}
int getIndex() const
{
return isTabUsingJucerComp (component, tabIndex) ? 0 : 1;
}
private:
int tabIndex;
class TabContentTypeChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabContentTypeChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const bool newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newValue (newValue_)
{
oldValue = isTabUsingJucerComp (comp, tabIndex);
}
bool perform()
{
showCorrectTab();
setTabUsingJucerComp (getComponent(), tabIndex, newValue);
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
bool undo()
{
showCorrectTab();
setTabUsingJucerComp (getComponent(), tabIndex, oldValue);
layout.getDocument()->refreshAllPropertyComps();
changed();
return true;
}
private:
int tabIndex;
bool newValue, oldValue;
};
};
//==============================================================================
class TabJucerFileProperty : public FilePropertyComponent,
private ChangeListener
{
public:
TabJucerFileProperty (TabbedComponent* const comp, JucerDocument& doc, const int tabIndex_)
: FilePropertyComponent ("jucer file", false, true),
component (comp),
document (doc),
tabIndex (tabIndex_)
{
document.addChangeListener (this);
}
~TabJucerFileProperty() override
{
document.removeChangeListener (this);
}
//==============================================================================
void setFile (const File& newFile) override
{
document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(), tabIndex,
newFile.getRelativePathFrom (document.getCppFile().getParentDirectory())
.replaceCharacter ('\\', '/')),
"Change tab component file");
}
File getFile() const override
{
return document.getCppFile().getSiblingFile (getTabJucerFile (component, tabIndex));
}
private:
void changeListenerCallback (ChangeBroadcaster*) override { refresh(); }
TabbedComponent* const component;
JucerDocument& document;
int tabIndex;
class JucerCompFileChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
JucerCompFileChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newState_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newState (newState_)
{
oldState = getTabJucerFile (comp, tabIndex);
}
bool perform()
{
showCorrectTab();
setTabJucerFile (getComponent(), tabIndex, newState);
changed();
return true;
}
bool undo()
{
showCorrectTab();
setTabJucerFile (getComponent(), tabIndex, oldState);
changed();
return true;
}
int tabIndex;
String newState, oldState;
};
};
//==============================================================================
class TabContentClassProperty : public ComponentTextProperty<TabbedComponent>
{
public:
TabContentClassProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
: ComponentTextProperty<TabbedComponent> ("content class", 256, false, comp, doc),
tabIndex (tabIndex_)
{
}
void setText (const String& newText) override
{
document.perform (new TabClassNameChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
"Change TabbedComponent content class");
}
String getText() const override
{
return getTabClassName (component, tabIndex);
}
private:
int tabIndex;
class TabClassNameChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabClassNameChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newValue (newValue_)
{
oldValue = getTabClassName (comp, tabIndex);
}
bool perform()
{
showCorrectTab();
setTabClassName (getComponent(), tabIndex, newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
setTabClassName (getComponent(), tabIndex, oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
int tabIndex;
String newValue, oldValue;
};
};
//==============================================================================
class TabContentConstructorParamsProperty : public ComponentTextProperty<TabbedComponent>
{
public:
TabContentConstructorParamsProperty (TabbedComponent* comp, JucerDocument& doc, const int tabIndex_)
: ComponentTextProperty<TabbedComponent> ("constructor params", 512, false, comp, doc),
tabIndex (tabIndex_)
{
}
void setText (const String& newText) override
{
document.perform (new TabConstructorParamChangeAction (component, *document.getComponentLayout(), tabIndex, newText),
"Change TabbedComponent content constructor param");
}
String getText() const override
{
return getTabConstructorParams (component, tabIndex);
}
private:
int tabIndex;
class TabConstructorParamChangeAction : public ComponentUndoableAction<TabbedComponent>
{
public:
TabConstructorParamChangeAction (TabbedComponent* const comp, ComponentLayout& l, const int tabIndex_, const String& newValue_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
tabIndex (tabIndex_),
newValue (newValue_)
{
oldValue = getTabConstructorParams (comp, tabIndex);
}
bool perform()
{
showCorrectTab();
setTabConstructorParams (getComponent(), tabIndex, newValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
bool undo()
{
showCorrectTab();
setTabConstructorParams (getComponent(), tabIndex, oldValue);
changed();
layout.getDocument()->refreshAllPropertyComps();
return true;
}
int tabIndex;
String newValue, oldValue;
};
};
//==============================================================================
class TabMoveProperty : public ButtonPropertyComponent
{
public:
TabMoveProperty (TabbedComponent* comp, JucerDocument& doc,
const int tabIndex_, const int totalNumTabs_)
: ButtonPropertyComponent ("move tab", false),
component (comp),
document (doc),
tabIndex (tabIndex_),
totalNumTabs (totalNumTabs_)
{
}
void buttonClicked()
{
PopupMenu m;
m.addItem (1, "Move this tab up", tabIndex > 0);
m.addItem (2, "Move this tab down", tabIndex < totalNumTabs - 1);
const int r = m.showAt (this);
if (r != 0)
document.perform (new MoveTabAction (component, *document.getComponentLayout(), tabIndex, tabIndex + (r == 2 ? 1 : -1)),
"Move a tab");
}
String getButtonText() const
{
return "Move this tab...";
}
TabbedComponent* const component;
JucerDocument& document;
const int tabIndex, totalNumTabs;
private:
class MoveTabAction : public ComponentUndoableAction<TabbedComponent>
{
public:
MoveTabAction (TabbedComponent* const comp, ComponentLayout& l,
const int oldIndex_, const int newIndex_)
: ComponentUndoableAction<TabbedComponent> (comp, l),
oldIndex (oldIndex_),
newIndex (newIndex_)
{
}
void move (int from, int to)
{
showCorrectTab();
auto state = getTabState (getComponent(), from);
getComponent()->removeTab (from);
addNewTab (getComponent(), to);
restoreTabState (getComponent(), to, *state);
layout.getDocument()->refreshAllPropertyComps();
changed();
}
bool perform()
{
move (oldIndex, newIndex);
return true;
}
bool undo()
{
move (newIndex, oldIndex);
return true;
}
private:
const int oldIndex, newIndex;
};
};
};