mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
1193 lines
41 KiB
C++
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;
|
|
};
|
|
};
|
|
};
|