// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "uiattributescontroller.h" #if VSTGUI_LIVE_EDITING #include "uiactions.h" #include "uicolor.h" #include "uieditcontroller.h" #include "uidialogcontroller.h" #include "../uiattributes.h" #include "../detail/uiviewcreatorattributes.h" #include "../../lib/controls/coptionmenu.h" #include "../../lib/controls/csearchtextedit.h" #include "../../lib/controls/cslider.h" #include "../../lib/controls/cbuttons.h" #include "../../lib/coffscreencontext.h" #include "../../lib/crowcolumnview.h" #include "../../lib/iviewlistener.h" #include "../../lib/cstring.h" #include "../../lib/cgraphicspath.h" #include "../../lib/ctexteditor.h" #include "../../lib/cvstguitimer.h" #include #include #include namespace VSTGUI { namespace UIAttributeControllers { //---------------------------------------------------------------------------------------------------- class Controller : public NonAtomicReferenceCounted, public DelegationController { public: Controller (IController* baseController, const std::string& attrName) : DelegationController (baseController), attrName (attrName), differentValues (false) {} const std::string& getAttributeName () const { return attrName; } virtual void setValue (const std::string& value) = 0; virtual void hasDifferentValues (bool state) { differentValues = state; } bool hasDifferentValues () const { return differentValues; } protected: IControlListener* getControlListener (UTF8StringPtr controlTagName) override { return this; } UIAttributesController* getAttributesController () const { return dynamic_cast (controller); } void performValueChange (UTF8StringPtr value) { hasDifferentValues (false); std::string valueStr = value ? value : ""; UIAttributesController* attrController = getAttributesController (); if (attrController) attrController->performAttributeChange (attrName, valueStr); } std::string attrName; bool differentValues; }; //---------------------------------------------------------------------------------------------------- class TextAlignmentController : public Controller { public: TextAlignmentController (IController* baseController, const std::string& attrName) : Controller (baseController, attrName) {} CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { auto* control = dynamic_cast(view); if (control) { int32_t tag = control->getTag (); if (tag >= kLeftTag && tag <= kRightTag) controls[tag] = control; } return controller->verifyView (view, attributes, description); } void setValue (const std::string& value) override { if (hasDifferentValues ()) { for (int32_t i = kLeftTag; i <= kRightTag; i++) { controls[i]->setValue (0.f); controls[i]->invalid (); } } else { int32_t alignment = 0; if (value == "center") alignment = 1; else if (value == "right") alignment = 2; for (int32_t i = kLeftTag; i <= kRightTag; i++) { controls[i]->setValue (i == alignment ? 1.f : 0.f); controls[i]->invalid (); } } } void valueChanged (CControl* control) override { if (control->getValue () == control->getMax ()) { switch (control->getTag ()) { case kLeftTag: { performValueChange ("left"); break; } case kCenterTag: { performValueChange ("center"); break; } case kRightTag: { performValueChange ("right"); break; } } } else { control->setValue (control->getMax ()); control->invalid (); } } protected: enum { kLeftTag = 0, kCenterTag, kRightTag }; CControl* controls[3]; }; //---------------------------------------------------------------------------------------------------- class AutosizeController : public Controller { public: AutosizeController (IController* baseController, UISelection* selection, const std::string& attrName) : Controller (baseController, attrName), selection (selection) {} CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { auto* control = dynamic_cast(view); if (control) { int32_t tag = control->getTag (); if (tag >= kLeftTag && tag <= kColTag) controls[tag] = control; if (tag >= kRowTag && tag <= kColTag) { for (const auto& selView : *selection) { if (selView->asViewContainer () == nullptr) { controls[tag]->setVisible (false); break; } } } } return controller->verifyView (view, attributes, description); } void setValue (const std::string& value) override { if (hasDifferentValues ()) { for (int32_t i = kLeftTag; i <= kColTag; i++) { controls[i]->setValue (0.f); } } else { controls[kLeftTag]->setValue (value.find ("left") == std::string::npos ? controls[kLeftTag]->getMin () : controls[kLeftTag]->getMax ()); controls[kRightTag]->setValue (value.find ("right") == std::string::npos ? controls[kRightTag]->getMin () : controls[kRightTag]->getMax ()); controls[kTopTag]->setValue (value.find ("top") == std::string::npos ? controls[kTopTag]->getMin () : controls[kTopTag]->getMax ()); controls[kBottomTag]->setValue (value.find ("bottom") == std::string::npos ? controls[kBottomTag]->getMin () : controls[kBottomTag]->getMax ()); controls[kRowTag]->setValue (value.find ("row") == std::string::npos ? controls[kRowTag]->getMin () : controls[kRowTag]->getMax ()); controls[kColTag]->setValue (value.find ("column") == std::string::npos ? controls[kColTag]->getMin () : controls[kColTag]->getMax ()); } for (int32_t i = kLeftTag; i <= kColTag; i++) { controls[i]->invalid (); } } void valueChanged (CControl* control) override { if (control == controls[kRowTag]) { if (control->getValue () == control->getMax ()) { controls[kColTag]->setValue (control->getMin ()); } } else if (control == controls[kColTag]) { if (control->getValue () == control->getMax ()) { controls[kRowTag]->setValue (control->getMin ()); } } std::string str; if (controls[kLeftTag]->getValue () == controls[kLeftTag]->getMax ()) str = "left"; if (controls[kRightTag]->getValue () == controls[kRightTag]->getMax ()) { if (str.empty () == false) str += " "; str += "right"; } if (controls[kTopTag]->getValue () == controls[kTopTag]->getMax ()) { if (str.empty () == false) str += " "; str += "top"; } if (controls[kBottomTag]->getValue () == controls[kBottomTag]->getMax ()) { if (str.empty () == false) str += " "; str += "bottom"; } if (controls[kRowTag]->getValue () == controls[kRowTag]->getMax ()) { if (str.empty () == false) str += " "; str += "row"; } if (controls[kColTag]->getValue () == controls[kColTag]->getMax ()) { if (str.empty () == false) str += " "; str += "column"; } performValueChange (str.c_str ()); } protected: enum { kLeftTag = 0, kTopTag, kRightTag, kBottomTag, kRowTag, kColTag }; CControl* controls[6]; SharedPointer selection; }; //---------------------------------------------------------------------------------------------------- class BooleanController : public Controller { public: BooleanController (IController* baseController, const std::string& attrName) : Controller (baseController, attrName), control (nullptr) {} CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { if (control == nullptr) { control = dynamic_cast(view); } return controller->verifyView (view, attributes, description); } void valueChanged (CControl* pControl) override { if (pControl->getValue () == control->getMax ()) performValueChange ("true"); else performValueChange ("false"); } void setValue (const std::string& value) override { if (hasDifferentValues()) { control->setValue (control->getMin () + (control->getMax () - control->getMin ()) / 2.f); } else { if (value == "true") control->setValue (control->getMax ()); else control->setValue (control->getMin ()); } control->invalid (); } protected: CControl* control; }; //---------------------------------------------------------------------------------------------------- class TextController : public Controller, public ViewListenerAdapter, public ITextLabelListener { public: TextController (IController* baseController, const std::string& attrName) : Controller (baseController, attrName) {} ~TextController () override { if (textLabel) { textLabel->unregisterViewListener (this); textLabel->unregisterTextLabelListener (this); } } CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { if (textLabel == nullptr) { auto* edit = dynamic_cast(view); if (edit) { textLabel = edit; originalTextColor = textLabel->getFontColor (); textLabel->registerTextLabelListener (this); textLabel->registerViewListener (this); } } return controller->verifyView (view, attributes, description); } void controlBeginEdit (VSTGUI::CControl* pControl) override { Controller::controlBeginEdit (pControl); } void controlEndEdit (VSTGUI::CControl* pControl) override { Controller::controlEndEdit (pControl); } void valueChanged (CControl* pControl) override { if (textLabel == pControl) { textLabel->setFontColor (originalTextColor); performValueChange (textLabel->getText ()); } } void setValue (const std::string& value) override { if (textLabel) { if (hasDifferentValues ()) { CColor newColor (originalTextColor); newColor.alpha /= 2; textLabel->setFontColor (newColor); textLabel->setText ("Multiple Values"); } else { textLabel->setText (value.c_str ()); } } } virtual void valueDisplayTruncated (UTF8StringPtr txt) { if (textLabel) { if (txt && *txt != 0) textLabel->setAttribute (kCViewTooltipAttribute, static_cast (textLabel->getText ().length () + 1), textLabel->getText ().data ()); else textLabel->removeAttribute (kCViewTooltipAttribute); } } void viewLostFocus (CView* view) override { if (view == textLabel) { SharedPointer textEdit = textLabel.cast (); if (textEdit && textEdit->bWasReturnPressed) { textEdit->getFrame ()->doAfterEventProcessing ([=] () { textEdit->takeFocus (); }); } } } void onTextLabelTruncatedTextChanged (CTextLabel* label) override { UTF8StringPtr txt = label->getTruncatedText (); valueDisplayTruncated (txt); } protected: SharedPointer textLabel; CColor originalTextColor; }; //------------------------------------------------------------------------ class ScriptController : public TextController, public IDialogController, public TextEditorControllerAdapter { public: using TextController::TextController; CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { if (editButton == nullptr) { auto button = dynamic_cast (view); if (button) editButton = button; } return TextController::verifyView (view, attributes, description); } void valueChanged (CControl* pControl) override { TextController::valueChanged (pControl); if (editButton == pControl && pControl->getValue () == pControl->getMax ()) { auto dc = new UIDialogController (this, editButton->getFrame ()); dc->run ("scripteditor", "Script Editor", "Close", nullptr, this, UIEditController::getEditorDescription (), true); } } CView* createView (const UIAttributes& attributes, const IUIDescription* description) override { if (auto name = attributes.getAttributeValue (IUIDescription::kCustomViewName)) { if (*name == "TextEditor") { CPoint size; attributes.getPointAttribute (UIViewCreator::kAttrSize, size); auto view = createNewTextEditor ({0, 0, size.x, size.y}, this); if (textEditor) { ITextEditor::Style style {}; description->getColor ("control.back", style.backColor); description->getColor ("control.font", style.textColor); description->getColor ("control.frame", style.selectionBackColor); description->getColor ("control.frame", style.lineNumberTextColor); description->getColor ("control.frame", style.frameColor); if (auto font = description->getFont ("scripteditor.font")) { style.font = font; style.lineNumbersFont = makeOwned (*font); style.lineNumbersFont->setSize (style.lineNumbersFont->getSize () - 2); } textEditor->setStyle (style); textEditor->setPlainText (textLabel->getText ().getString ()); } return view; } } return nullptr; } void onDialogButton1Clicked (UIDialogController*) override {} void onDialogButton2Clicked (UIDialogController*) override {} void onDialogShow (UIDialogController*) override {} void onTextEditorCreated (const ITextEditor& te) override { textEditor = &te; Call::later ([This = shared (this)] () { if (This->textEditor) This->textEditor->handleCommand (ITextEditor::Command::TakeFocus); }); } void onTextEditorDestroyed (const ITextEditor& te) override { textEditor = nullptr; auto text = te.getPlainText (); if (text != textLabel->getText ().getString ()) performValueChange (text.data ()); te.resetController (); } protected: SharedPointer editButton; const ITextEditor* textEditor {nullptr}; }; //---------------------------------------------------------------------------------------------------- class NumberController : public TextController { public: NumberController (IController* baseController, const std::string& attrName) : TextController (baseController, attrName) { } CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) override { if (slider == nullptr) { auto* sliderView = dynamic_cast (view); if (sliderView) slider = sliderView; } return TextController::verifyView (view, attributes, description); } void controlBeginEdit (VSTGUI::CControl* pControl) override { if (pControl == slider) { getAttributesController ()->beginLiveAttributeChange ( attrName, UIAttributes::doubleToString (slider->getValue ())); } TextController::controlBeginEdit (pControl); } void controlEndEdit (VSTGUI::CControl* pControl) override { if (pControl == slider) { getAttributesController ()->endLiveAttributeChange (); } TextController::controlEndEdit (pControl); } void valueChanged (CControl* pControl) override { TextController::valueChanged (pControl); if (slider == pControl) { performValueChange (UIAttributes::doubleToString (slider->getValue ()).data ()); } } void setValue (const std::string& value) override { TextController::setValue (value); if (slider) { float floatValue; std::istringstream sstream (value); sstream.imbue (std::locale::classic ()); sstream.precision (40); sstream >> floatValue; slider->setValue (floatValue); slider->invalid (); } } protected: SharedPointer slider; }; //---------------------------------------------------------------------------------------------------- class MenuController : public TextController, public OptionMenuListenerAdapter, public CommandMenuItemTargetAdapter { public: MenuController (IController* baseController, const std::string& attrName, UIDescription* description, bool addNoneItem = true, bool sortItems = true) : TextController (baseController, attrName), description (description), addNoneItem (addNoneItem), sortItems (sortItems) {} ~MenuController () override { if (menu) menu->unregisterOptionMenuListener (this); } CView* verifyView (CView* view, const UIAttributes& attributes, const IUIDescription*) override { if (menu == nullptr) { menu = dynamic_cast(view); if (menu) menu->registerOptionMenuListener (this); } return TextController::verifyView (view, attributes, description); } using StringPtrList =std::list; virtual void collectMenuItemNames (StringPtrList& names) = 0; virtual void validateMenuEntry (CCommandMenuItem* item) {} virtual void addMenuEntry (const std::string* entryName) { CCommandMenuItem* item = new CCommandMenuItem (CCommandMenuItem::Desc{entryName->data (), this}); validateMenuEntry (item); menu->addEntry (item); if (textLabel->getText () == *entryName) { int32_t index = menu->getNbEntries () - 1; menu->setValue ((float)index); menu->setCurrent (index); } } void setValue (const std::string& value) override { TextController::setValue (value); } void onOptionMenuPrePopup (COptionMenu* optMenu) override { optMenu->removeAllEntry (); if (addNoneItem) optMenu->addEntry (new CCommandMenuItem (CCommandMenuItem::Desc{"None", 100, this})); StringPtrList names; collectMenuItemNames (names); if (sortItems) names.sort (UIEditController::std__stringCompare); if (addNoneItem && !names.empty ()) optMenu->addSeparator (); for (const auto& name : names) addMenuEntry (name); } bool onCommandMenuItemSelected (CCommandMenuItem* item) override { performValueChange (item->getTag () == 100 ? UTF8String ("") : item->getTitle ()); return true; } void valueDisplayTruncated (UTF8StringPtr txt) override { if (textLabel && menu) { if (txt && *txt != 0) menu->setAttribute (kCViewTooltipAttribute, static_cast (textLabel->getText ().length () + 1), textLabel->getText ().data ()); else menu->removeAttribute (kCViewTooltipAttribute); } } protected: SharedPointer description; SharedPointer menu; bool addNoneItem; bool sortItems; }; //---------------------------------------------------------------------------------------------------- class ColorController : public MenuController { public: ColorController (IController* baseController, const std::string& attrName, UIDescription* description) : MenuController (baseController, attrName, description) {} void collectMenuItemNames (StringPtrList& names) override { description->collectColorNames (names); } void validateMenuEntry (CCommandMenuItem* item) override { CColor color; if (description->getColor (item->getTitle (), color)) { item->setIcon (createColorIcon (color)); } } void setValue (const std::string& value) override { MenuController::setValue (value); if (colorView) { if (hasDifferentValues ()) { colorView->color = kTransparentCColor; } else { CColor color; if (description->getColor (value.c_str (), color)) { colorView->color = color; } else { colorView->color = kTransparentCColor; } } colorView->invalid (); } } CView* createView (const UIAttributes& attributes, const IUIDescription* description) override { const std::string* attr = attributes.getAttributeValue (IUIDescription::kCustomViewName); if (attr && *attr == "ColorView") { colorView = new ColorView (); return colorView; } return nullptr; } protected: class ColorView : public CView { public: ColorView () : CView (CRect (0, 0, 0, 0)) ,color (kTransparentCColor) {} void draw (CDrawContext* context) override { context->setFillColor (color); context->setDrawMode (kAliasing); context->drawRect (getViewSize (), kDrawFilled); } CColor color; }; SharedPointer colorView; }; //---------------------------------------------------------------------------------------------------- class GradientController : public MenuController { public: GradientController (IController* baseController, const std::string& attrName, UIDescription* description) : MenuController (baseController, attrName, description) {} void collectMenuItemNames (StringPtrList& names) override { description->collectGradientNames (names); } void validateMenuEntry (CCommandMenuItem* item) override { const CCoord size = 15; if (CGradient* gradient = description->getGradient (item->getTitle ())) { if (auto context = COffscreenContext::create ({size, size})) { context->beginDraw (); SharedPointer path = owned (context->createGraphicsPath ()); path->addRect (CRect (0, 0, size, size)); context->fillLinearGradient(path, *gradient, CPoint (0, 0), CPoint (size, 0)); context->endDraw (); item->setIcon (context->getBitmap ()); } } } void setValue (const std::string& value) override { MenuController::setValue (value); if (gradientView) { if (hasDifferentValues ()) { gradientView->gradient = nullptr; } else { gradientView->gradient = description->getGradient (value.c_str ()); } gradientView->invalid (); } } CView* createView (const UIAttributes& attributes, const IUIDescription* description) override { const std::string* attr = attributes.getAttributeValue (IUIDescription::kCustomViewName); if (attr && *attr == "GradientView") { gradientView = new GradientView (); return gradientView; } return nullptr; } protected: class GradientView : public CView { public: GradientView () : CView (CRect (0, 0, 0, 0)) {} void draw (CDrawContext* context) override { if (gradient == nullptr) return; CRect r = getViewSize (); SharedPointer path = owned (context->createGraphicsPath ()); path->addRect (r); context->fillLinearGradient (path, *gradient, r.getTopLeft (), r.getTopRight ()); } SharedPointer gradient; }; SharedPointer gradientView; }; //---------------------------------------------------------------------------------------------------- class TagController : public MenuController { public: TagController (IController* baseController, const std::string& attrName, UIDescription* description) : MenuController (baseController, attrName, description, true, false) {} void collectMenuItemNames (StringPtrList& names) override { description->collectControlTagNames (names); } }; //---------------------------------------------------------------------------------------------------- class BitmapController : public MenuController { public: BitmapController (IController* baseController, const std::string& attrName, UIDescription* description) : MenuController (baseController, attrName, description) {} void collectMenuItemNames (StringPtrList& names) override { description->collectBitmapNames (names); } }; //---------------------------------------------------------------------------------------------------- class FontController : public MenuController { public: FontController (IController* baseController, const std::string& attrName, UIDescription* description) : MenuController (baseController, attrName, description) {} void collectMenuItemNames (StringPtrList& names) override { description->collectFontNames (names); } }; //---------------------------------------------------------------------------------------------------- class ListController : public MenuController { public: ListController (IController* baseController, const std::string& attrName, UIDescription* description, UISelection* selection) : MenuController (baseController, attrName, description, false, false), selection (selection) {} void collectMenuItemNames (StringPtrList& names) override { const auto* viewFactory = dynamic_cast (description->getViewFactory ()); if (viewFactory) { viewFactory->getPossibleAttributeListValues (selection->first (), attrName, names); } } protected: SharedPointer selection; }; } // VSTGUI //---------------------------------------------------------------------------------------------------- UIAttributesController::UIAttributesController (IController* baseController, UISelection* selection, UIUndoManager* undoManager, UIDescription* description) : DelegationController (baseController) , selection (selection) , undoManager (undoManager) , editDescription (description) , liveAction (nullptr) , viewNameLabel (nullptr) , attributeView (nullptr) , currentAttributeName (nullptr) { selection->registerListener (this); undoManager->registerListener (this); description->registerListener (this); UIEditController::getEditorDescription ()->getColor ("control.font", attributeNameColor); } //---------------------------------------------------------------------------------------------------- UIAttributesController::~UIAttributesController () { if (viewNameLabel) viewNameLabel->unregisterViewListener (this); if (attributeView) attributeView->unregisterViewListener (this); selection->unregisterListener (this); undoManager->unregisterListener (this); editDescription->unregisterListener (this); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::beginLiveAttributeChange (const std::string& name, const std::string& currentValue) { liveAction = new AttributeChangeAction (editDescription, selection, name, currentValue); undoManager->startGroupAction (liveAction->getName ()); undoManager->pushAndPerform (new AttributeChangeAction (editDescription, selection, name, currentValue)); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::endLiveAttributeChange () { if (liveAction) { liveAction->undo (); undoManager->pushAndPerform (liveAction); liveAction = nullptr; undoManager->endGroupAction (); } } //---------------------------------------------------------------------------------------------------- void UIAttributesController::performAttributeChange (const std::string& name, const std::string& value) { IAction* action = new AttributeChangeAction (editDescription, selection, name, value); if (liveAction) { delete liveAction; liveAction = action; action->perform (); } else undoManager->pushAndPerform (action); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::valueChanged (CControl* control) { switch (control->getTag ()) { case kSearchFieldTag: { if (auto sf = dynamic_cast (control)) { filterString = sf->getText (); rebuildAttributesView (); auto attributes = editDescription->getCustomAttributes ("UIAttributesController", true); if (attributes) { attributes->setAttribute("UIAttributesController", filterString); } } break; } } } //---------------------------------------------------------------------------------------------------- CView* UIAttributesController::verifyView (CView* view, const UIAttributes& attributes, const IUIDescription* description) { if (attributeView == nullptr) { auto* rcv = dynamic_cast(view); if (rcv) { attributeView = rcv; attributeView->registerViewListener (this); } } if (searchField == nullptr) { auto* textEdit = dynamic_cast(view); if (textEdit && textEdit->getTag () == kSearchFieldTag) { searchField = textEdit; auto attrs = editDescription->getCustomAttributes ("UIAttributesController", true); if (attrs) { const std::string* searchText = attrs->getAttributeValue ("SearchString"); if (searchText) { searchField->setText (searchText->c_str ()); } } } } if (viewNameLabel == nullptr) { auto* textLabel = dynamic_cast(view); if (textLabel && textLabel->getTag () == kViewNameTag) { viewNameLabel = textLabel; viewNameLabel->setText ("No Selection"); viewNameLabel->registerViewListener (this); } } return DelegationController::verifyView (view, attributes, description); } //---------------------------------------------------------------------------------------------------- IControlListener* UIAttributesController::getControlListener (UTF8StringPtr name) { return this; } //---------------------------------------------------------------------------------------------------- IController* UIAttributesController::createSubController (IdStringPtr _name, const IUIDescription* description) { UTF8StringView name (_name); if (currentAttributeName) { if (name == "TextController") { return new UIAttributeControllers::TextController (this, *currentAttributeName); } else if (name == "NumberController") { return new UIAttributeControllers::NumberController (this, *currentAttributeName); } else if (name == "BooleanController") { return new UIAttributeControllers::BooleanController (this, *currentAttributeName); } else if (name == "ColorController") { return new UIAttributeControllers::ColorController (this, *currentAttributeName, editDescription); } else if (name == "GradientController") { return new UIAttributeControllers::GradientController (this, *currentAttributeName, editDescription); } else if (name == "TagController") { return new UIAttributeControllers::TagController (this, *currentAttributeName, editDescription); } else if (name == "BitmapController") { return new UIAttributeControllers::BitmapController (this, *currentAttributeName, editDescription); } else if (name == "FontController") { return new UIAttributeControllers::FontController (this, *currentAttributeName, editDescription); } else if (name == "ListController") { return new UIAttributeControllers::ListController (this, *currentAttributeName, editDescription, selection); } else if (name == "TextAlignmentController") { return new UIAttributeControllers::TextAlignmentController (this, *currentAttributeName); } else if (name == "AutosizeController") { return new UIAttributeControllers::AutosizeController (this, selection, *currentAttributeName); } else if (name == "ScriptController") { return new UIAttributeControllers::ScriptController (this, *currentAttributeName); } } return controller->createSubController (name, description); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescTagChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescColorChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescFontChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescBitmapChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescTemplateChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUIDescGradientChanged (UIDescription* desc) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::selectionDidChange (UISelection*) { if (!rebuildRequested && attributeView) { if (auto frame = attributeView->getFrame ()) { if (frame->inEventProcessing ()) { rebuildRequested = true; frame->doAfterEventProcessing ([Self = shared (this)] () { Self->rebuildAttributesView (); Self->rebuildRequested = false; }); } } if (!rebuildRequested) rebuildAttributesView (); } } //---------------------------------------------------------------------------------------------------- void UIAttributesController::selectionViewsDidChange (UISelection*) { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::onUndoManagerChange () { validateAttributeViews (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::validateAttributeViews () { const auto* viewFactory = static_cast (editDescription->getViewFactory ()); for (auto& controller : attributeControllers) { std::string attrValue; bool first = true; bool hasDifferentValues = false; for (const auto& view : *selection) { std::string temp; viewFactory->getAttributeValue (view, controller->getAttributeName (), temp, editDescription); if (temp != attrValue && !first) hasDifferentValues = true; attrValue = temp; first = false; } controller->hasDifferentValues (hasDifferentValues); controller->setValue (attrValue); } } //---------------------------------------------------------------------------------------------------- CView* UIAttributesController::createValueViewForAttributeType (const IViewFactory* viewFactory, CView* view, const std::string& attrName, IViewCreator::AttrType attrType) { auto editorDescription = UIEditController::getEditorDescription (); if (!editDescription) return nullptr; switch (attrType) { case IViewCreator::kFontType: return editorDescription->createView ("attributes.font", this); case IViewCreator::kBitmapType: return editorDescription->createView ("attributes.bitmap", this); case IViewCreator::kTagType: return editorDescription->createView ("attributes.tag", this); case IViewCreator::kColorType: return editorDescription->createView ("attributes.color", this); case IViewCreator::kGradientType: return editorDescription->createView ("attributes.gradient", this); case IViewCreator::kBooleanType: return editorDescription->createView ("attributes.boolean", this); case IViewCreator::kListType: return editorDescription->createView ("attributes.list", this); case IViewCreator::kScriptType: return editorDescription->createView ("attributes.script", this); case IViewCreator::kFloatType: case IViewCreator::kIntegerType: { double minValue, maxValue; const auto* viewFactoryEditing = dynamic_cast (viewFactory); if (viewFactoryEditing->getAttributeValueRange (view, attrName, minValue, maxValue)) { CView* valueView = editorDescription->createView ("attributes.number", this); if (valueView) { if (auto container = valueView->asViewContainer ()) { std::vector sliders; if (container->getChildViewsOfType (sliders) == 1) { sliders[0]->setMin (static_cast (minValue)); sliders[0]->setMax (static_cast (maxValue)); } } return valueView; } } } default: break; } return editorDescription->createView ("attributes.text", this); } //---------------------------------------------------------------------------------------------------- CView* UIAttributesController::createViewForAttribute (const std::string& attrName) { const CCoord height = 18; const CCoord width = 160; const CCoord margin = 2; CViewContainer* result = new CViewContainer (CRect (0, 0, width, height+2)); result->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeColumn); result->setTransparency (true); CCoord middle = width/2; CTextLabel* label = new CTextLabel (CRect (5, 1, middle - margin, height+1), attrName.c_str ()); label->setTextTruncateMode (CTextLabel::kTruncateHead); label->setTransparency (true); label->setHoriAlign (kRightText); label->setFontColor (attributeNameColor); label->setFont (kNormalFontSmall); label->setAutosizeFlags (kAutosizeAll); result->addView (label); bool hasDifferentValues = false; const auto* viewFactory = editDescription->getViewFactory (); const auto* viewFactoryEditing = dynamic_cast (viewFactory); std::string attrValue; bool first = true; for (const auto& view : *selection) { std::string temp; viewFactory->getAttributeValue (view, attrName, temp, editDescription); if (temp != attrValue && !first) hasDifferentValues = true; attrValue = temp; first = false; } CRect r (middle+margin, 1, width-5, height+1); CView* valueView = nullptr; if (attrName == "text-alignment") { valueView = UIEditController::getEditorDescription ()->createView ("attributes.text.alignment", this); } else if (attrName == "autosize") { valueView = UIEditController::getEditorDescription ()->createView ("attributes.view.autosize", this); } if (valueView == nullptr) { CView* firstView = selection->first (); auto attrType = viewFactoryEditing->getAttributeType (firstView, attrName); valueView = createValueViewForAttributeType (viewFactory, firstView, attrName, attrType); } if (valueView == nullptr) // fallcack if attributes.text template not defined { IController* controller = new UIAttributeControllers::TextController (this, *currentAttributeName); auto* textEdit = new CTextEdit (r, this, -1); textEdit->setText (attrValue.c_str ()); textEdit->setTransparency (true); textEdit->setFontColor (kBlackCColor); textEdit->setFont (kNormalFontSmall); textEdit->setListener (controller); valueView = textEdit; valueView->setAttribute (kCViewControllerAttribute, controller); } if (valueView) { IController* controller = getViewController (valueView, true); if (controller) { auto* c = dynamic_cast(controller); if (c) { c->hasDifferentValues (hasDifferentValues); c->setValue (attrValue); attributeControllers.emplace_back (c); } } r.setHeight (valueView->getHeight ()); valueView->setViewSize (r); valueView->setMouseableArea (r); result->addView (valueView); r = result->getViewSize (); r.setHeight (valueView->getHeight()+2); result->setViewSize (r); result->setMouseableArea (r); } return result; } //---------------------------------------------------------------------------------------------------- void UIAttributesController::getConsolidatedAttributeNames (StringList& attrNames, const std::string& filter) { const auto* viewFactory = dynamic_cast (editDescription->getViewFactory ()); vstgui_assert (viewFactory); if (!viewFactory) return; for (const auto& view : *selection) { StringList temp; if (viewFactory->getAttributeNamesForView (view, temp)) { StringList toRemove; if (attrNames.empty ()) attrNames = temp; else { for (auto& attrName : attrNames) { bool found = std::find (temp.begin (), temp.end (), attrName) != temp.end (); if (!found) { toRemove.emplace_back (attrName); temp.remove (attrName); } } } if (!filter.empty ()) { for (auto rit = temp.rbegin (); rit != temp.rend (); ++rit) { std::string lowerCaseName (*rit); std::transform (lowerCaseName.begin (), lowerCaseName.end (), lowerCaseName.begin (), ::tolower); if (lowerCaseName.find (filter) == std::string::npos) toRemove.emplace_back (*rit); } } for (auto& attrName : toRemove) { attrNames.remove (attrName); } } } } //---------------------------------------------------------------------------------------------------- void UIAttributesController::rebuildAttributesView () { auto viewFactory = dynamic_cast (editDescription->getViewFactory ()); if (attributeView == nullptr || viewFactory == nullptr) return; attributeView->invalid (); attributeView->removeAll (); attributeControllers.clear (); std::string filter (filterString); std::transform (filter.begin (), filter.end (), filter.begin (), ::tolower); if (viewNameLabel) { int32_t selectedViews = selection->total (); if (selectedViews > 0) { UTF8StringPtr viewname = nullptr; for (const auto& view : *selection) { UTF8StringPtr name = viewFactory->getViewDisplayName (view); if (viewname != nullptr && UTF8StringView (name) != viewname) { viewname = nullptr; break; } viewname = name; } if (viewname != nullptr) { if (selectedViews == 1) viewNameLabel->setText (viewname); else { std::stringstream str; str << selectedViews << "x " << viewname; viewNameLabel->setText (str.str ().c_str ()); } } else { std::stringstream str; str << selectedViews << "x different views"; viewNameLabel->setText (str.str ().c_str ()); } } else { viewNameLabel->setText ("No Selection"); } } StringList attrNames; getConsolidatedAttributeNames (attrNames, filter); if (attrNames.empty ()) { CRect r (attributeView->getViewSize ()); r.setHeight (0); attributeView->setViewSize (r); attributeView->setMouseableArea (r); } else { CCoord width = attributeView->getWidth () - (attributeView->getMargin ().left + attributeView->getMargin ().right); for (const auto& name : attrNames) { currentAttributeName = &name; CView* view = createViewForAttribute (name); if (view) { CRect r = view->getViewSize (); r.setWidth (width); view->setViewSize (r); view->setMouseableArea (r); attributeView->addView (view); } } currentAttributeName = nullptr; attributeView->sizeToFit (); attributeView->setMouseableArea (attributeView->getViewSize ()); } attributeView->invalid (); } //---------------------------------------------------------------------------------------------------- void UIAttributesController::viewWillDelete (CView* view) { if (view == attributeView) attributeView = nullptr; else if (view == viewNameLabel) viewNameLabel = nullptr; view->unregisterViewListener (this); } } // VSTGUI #endif // VSTGUI_LIVE_EDITING