From 563afde7a324aaecd7f57047cc8f4f7fda524775 Mon Sep 17 00:00:00 2001 From: twanvl Date: Sat, 25 Nov 2006 00:35:33 +0000 Subject: [PATCH] implemented choice editor with drop down list, todo: submenus git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@93 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/data/field/choice.cpp | 3 ++ src/gui/drop_down_list.cpp | 23 ++++++-- src/gui/drop_down_list.hpp | 8 +-- src/gui/value/choice.cpp | 105 +++++++++++++++++++++++++++++++++++- src/gui/value/choice.hpp | 52 ++++++++++++++++++ src/gui/value/color.cpp | 24 +++++---- src/render/card/viewer.cpp | 14 ++++- src/render/value/viewer.hpp | 2 + src/script/image.cpp | 2 + 9 files changed, 211 insertions(+), 22 deletions(-) diff --git a/src/data/field/choice.cpp b/src/data/field/choice.cpp index c401f8ee..be296e91 100644 --- a/src/data/field/choice.cpp +++ b/src/data/field/choice.cpp @@ -38,6 +38,9 @@ IMPLEMENT_REFLECTION(ChoiceField) { REFLECT_N("default", default_script); REFLECT(initial); REFLECT(default_name); + if (tag.reading()) { + choices->initIds(); + } } // ----------------------------------------------------------------------------- : ChoiceField::Choice diff --git a/src/gui/drop_down_list.cpp b/src/gui/drop_down_list.cpp index 48b11e82..86608f09 100644 --- a/src/gui/drop_down_list.cpp +++ b/src/gui/drop_down_list.cpp @@ -62,6 +62,8 @@ DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer) , hider(is_submenu ? nullptr : new DropDownHider(*this)) , viewer(viewer) , item_size(100,1) + , icon_size(0,0) + , text_offset(0) { // determine item height wxClientDC dc(this); @@ -79,10 +81,21 @@ void DropDownList::show(bool in_place, wxPoint pos) { if (IsShown()) return; // find selection selected_item = selection(); - // fix size & position - int line_count = 0; + // width size_t count = itemCount(); + if (item_size.width == 100) { // not initialized + wxClientDC dc(this); + dc.SetFont(*wxNORMAL_FONT); + for (size_t i = 0 ; i < count ; ++i) { + int text_width; + dc.GetTextExtent(capitalize(itemText(i)), &text_width, 0); + item_size.width = max(item_size.width, text_width + icon_size.width + 14); // 14 = room for popup arrow + padding + } + } + // height + int line_count = 0; for (size_t i = 0 ; i < count ; ++i) if (lineBelow(i)) line_count += 1; + // size RealSize size( item_size.width + marginW * 2, item_size.height * count + marginH * 2 + line_count @@ -165,7 +178,7 @@ bool DropDownList::showSubMenu() { } } bool DropDownList::showSubMenu(size_t item, int y) { - DropDownList* sub_menu = item == NO_SELECTION ? nullptr : popup(item); + DropDownList* sub_menu = item == NO_SELECTION ? nullptr : submenu(item); if (sub_menu == open_sub_menu) return sub_menu; // no change hideSubMenu(); open_sub_menu = sub_menu; @@ -243,7 +256,7 @@ void DropDownList::drawItem(DC& dc, int y, size_t item) { drawIcon(dc, marginW, y, item, item == selected_item); dc.DrawText(capitalize(itemText(item)), marginW + icon_size.width, y + text_offset); // draw popup icon - if (popup(item)) { + if (submenu(item)) { draw_menu_arrow(this, dc, wxRect(marginW, y, item_size.width, item_size.height), item == selected_item); } // draw line below @@ -262,7 +275,7 @@ void DropDownList::onLeftDown(wxMouseEvent&) { void DropDownList::onLeftUp(wxMouseEvent&) { if (mouse_down) { // don't hide if there is a child menu - if (selected_item != NO_SELECTION && popup(selected_item)) return; + if (selected_item != NO_SELECTION && submenu(selected_item)) return; hide(true); } } diff --git a/src/gui/drop_down_list.hpp b/src/gui/drop_down_list.hpp index fee013c2..abed445c 100644 --- a/src/gui/drop_down_list.hpp +++ b/src/gui/drop_down_list.hpp @@ -56,11 +56,11 @@ class DropDownList : public wxPopupWindow { /// Draw an icon at the specified location virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const = 0; /// Is there a line below an item? - virtual bool lineBelow(size_t item) const { return false; } + virtual bool lineBelow(size_t item) const { return false; } /// Should the item be highlighted? - virtual bool highlightItem(size_t item) const { return false; } - // An extra menu that pops up from an item, or null if there is no popup menu - virtual DropDownList* popup(size_t item) const { return nullptr; } + virtual bool highlightItem(size_t item) const { return false; } + // An extra submenu that pops up from an item, or null if there is no popup menu + virtual DropDownList* submenu(size_t item) const { return nullptr; } // --------------------------------------------------- : Layout diff --git a/src/gui/value/choice.cpp b/src/gui/value/choice.cpp index 7ac069fc..1c72a43f 100644 --- a/src/gui/value/choice.cpp +++ b/src/gui/value/choice.cpp @@ -7,7 +7,108 @@ // ----------------------------------------------------------------------------- : Includes #include +#include +#include -// ----------------------------------------------------------------------------- : +DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP); -IMPLEMENT_VALUE_EDITOR(Choice) {} +// ----------------------------------------------------------------------------- : DropDownChoiceList + +DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group) + : DropDownList(parent, is_submenu, is_submenu ? nullptr : &cve) + , group(group) + , cve(cve) +{} + +size_t DropDownChoiceList::itemCount() const { + return group->choices.size() + hasDefault(); +} + +ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const { + if (isGroupDefault(item)) { + return group; + } else { + return group->choices[item - hasDefault()]; + } +} + +String DropDownChoiceList::itemText(size_t item) const { + if (isFieldDefault(item)) { + return field().default_name; + } else { + ChoiceField::ChoiceP choice = getChoice(item); + return choice->name; + } +} +bool DropDownChoiceList::lineBelow(size_t item) const { + return isDefault(item); +} +DropDownList* DropDownChoiceList::submenu(size_t item) { + if (isDefault(item)) return nullptr; + item -= hasDefault(); + if (item < submenus.size()) submenus.resize(item + 1); + if (submenus[item]) return submenus[item].get(); + ChoiceField::ChoiceP choice = getChoice(item); + if (choice->isGroup()) { + // create submenu + submenus[item].reset(new DropDownChoiceList(GetParent(), true, cve, choice)); + } + return submenus[item].get(); +} + +void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { + // TODO +} + + +void DropDownChoiceList::select(size_t item) { + if (isFieldDefault(item)) { + cve.change( Defaultable() ); + } else { + ChoiceField::ChoiceP choice = getChoice(item); + cve.change( field().choices->choiceName(choice->first_id) ); + } +} +size_t DropDownChoiceList::selection() const { + if (hasFieldDefault() && cve.value().value.isDefault()) { + return 0; + } + size_t i = hasDefault(); + int id = field().choices->choiceId(cve.value().value()); + FOR_EACH(c, group->choices) { + if (id >= c->first_id && id < c->lastId()) { + return i; + } + i++; + } + return NO_SELECTION; +} + +// ----------------------------------------------------------------------------- : ChoiceValueEditor + +IMPLEMENT_VALUE_EDITOR(Choice) + , drop_down(new DropDownChoiceList(&editor(), false, *this, field().choices)) +{} + +void ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { + drop_down->onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook()); +} +void ChoiceValueEditor::onChar(wxKeyEvent& ev) { + drop_down->onCharInParent(ev); +} +void ChoiceValueEditor::onLoseFocus() { + drop_down->hide(false); +} + +void ChoiceValueEditor::drawSelection(RotatedDC& dc) { + if (nativeLook()) { + draw_drop_down_arrow(&editor(), dc.getDC(), style().getRect().grow(1), drop_down->IsShown()); + } +} +void ChoiceValueEditor::determineSize() { + style().height = max(style().height(), 16.); +} + +void ChoiceValueEditor::change(const Defaultable& c) { + getSet().actions.add(value_action(static_pointer_cast(valueP), c)); +} diff --git a/src/gui/value/choice.hpp b/src/gui/value/choice.hpp index a89f021d..a45770ad 100644 --- a/src/gui/value/choice.hpp +++ b/src/gui/value/choice.hpp @@ -11,14 +11,66 @@ #include #include +#include #include +DECLARE_POINTER_TYPE(DropDownList); + // ----------------------------------------------------------------------------- : ChoiceValueEditor /// An editor 'control' for editing ChoiceValues class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor { public: DECLARE_VALUE_EDITOR(Choice); + + // --------------------------------------------------- : Events + virtual void onLeftDown(const RealPoint& pos, wxMouseEvent& ev); + virtual void onChar(wxKeyEvent& ev); + virtual void onLoseFocus(); + + virtual void drawSelection(RotatedDC& dc); + virtual void determineSize(); + + private: + DropDownListP drop_down; + friend class DropDownChoiceList; + /// Change the choice + void change(const Defaultable& c); +}; + +// ----------------------------------------------------------------------------- : DropDownChoiceList + +// A drop down list of color choices +class DropDownChoiceList : public DropDownList { + public: + DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group); + + protected: + virtual size_t itemCount() const; + virtual bool lineBelow(size_t item) const; + virtual String itemText(size_t item) const; + virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; + virtual DropDownList* submenu(size_t item); + + virtual void select(size_t item); + virtual size_t selection() const; + + private: + ChoiceValueEditor& cve; + ChoiceField::ChoiceP group; ///< Group this menu shows + vector submenus; + + inline const ChoiceField& field() const { return cve.field(); } + + inline bool hasFieldDefault() const { return group == field().choices && field().default_script; } + inline bool hasGroupDefault() const { return group->hasDefault(); } + inline bool hasDefault() const { return hasFieldDefault() || hasGroupDefault(); } + inline bool isFieldDefault(size_t item) const { return item == 0 && hasFieldDefault(); } + inline bool isGroupDefault(size_t item) const { return item == 0 && hasGroupDefault(); } + inline bool isDefault (size_t item) const { return item == 0 && hasDefault(); } + + // Find an item in the group of choices + ChoiceField::ChoiceP getChoice(size_t item) const; }; // ----------------------------------------------------------------------------- : EOF diff --git a/src/gui/value/color.cpp b/src/gui/value/color.cpp index fa9bb876..f3321c43 100644 --- a/src/gui/value/color.cpp +++ b/src/gui/value/color.cpp @@ -35,9 +35,15 @@ class DropDownColorList : public DropDownList { mutable Color default_color; inline const ColorField& field() const { return cve.field(); } -// // default, custom item + // default, custom item bool hasDefault() const { return field().default_script; } bool hasCustom() const { return field().allow_custom; } + bool isDefault(size_t item) const { + return item == 0 && hasDefault(); + } + bool isCustom(size_t item) const { + return item == itemCount() - 1 && hasCustom(); + } }; @@ -56,13 +62,12 @@ size_t DropDownColorList::itemCount() const { return cve.field().choices.size() + hasDefault() + hasCustom(); } bool DropDownColorList::lineBelow(size_t item) const { - return (item == 0 && hasDefault()) // below default item - || (item == itemCount() - 2 && hasCustom()); // above custom item + return isDefault(item) || isCustom(item + 1); // below default item, above custom item } String DropDownColorList::itemText(size_t item) const { - if (item == 0 && hasDefault()) { + if (isDefault(item)) { return field().default_name; - } else if (item == itemCount()-1 && hasCustom()) { + } else if (isCustom(item)) { return _("Custom..."); } else { return field().choices[item - hasDefault()]->name; @@ -71,9 +76,9 @@ String DropDownColorList::itemText(size_t item) const { void DropDownColorList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { Color col; - if (item == 0 && hasDefault()) { // default + if (isDefault(item)) { col = default_color; - } else if (item == itemCount()-1 && hasCustom()) { // custom color + } else if (isCustom(item)) { col = cve.value().value(); } else { col = field().choices[item - hasDefault()]->color; @@ -84,6 +89,7 @@ void DropDownColorList::drawIcon(DC& dc, int x, int y, size_t item, bool selecte dc.DrawRectangle(x+1, y+1, icon_size.width-2, item_size.height-2); } + size_t DropDownColorList::selection() const { // find selected color size_t selection = hasCustom() ? itemCount() - 1 : NO_SELECTION; @@ -107,9 +113,9 @@ size_t DropDownColorList::selection() const { return selection; } void DropDownColorList::select(size_t item) { - if (item == 0 && hasDefault()) { + if (isDefault(item)) { cve.change( Defaultable()); - } else if (item == itemCount() - 1 && hasCustom()) { + } else if (isCustom(item)) { cve.changeCustom(); } else { cve.change(field().choices[item - hasDefault()]->color); diff --git a/src/render/card/viewer.cpp b/src/render/card/viewer.cpp index 02f18e62..e66905e2 100644 --- a/src/render/card/viewer.cpp +++ b/src/render/card/viewer.cpp @@ -13,6 +13,7 @@ #include #include #include +#include DECLARE_TYPEOF_COLLECTION(ValueViewerP); typedef IndexMap IndexMap_FieldP_StyleP; @@ -116,6 +117,15 @@ ValueViewerP DataViewer::makeViewer(const StyleP& style) { return style->makeViewer(*this, style); } -void DataViewer::onAction(const Action&, bool undone) { - // TODO +void DataViewer::onAction(const Action& action, bool undone) { + TYPE_CASE(action, ValueAction) { + FOR_EACH(v, viewers) { + if (v->getValue() == action.valueP) { + // refresh the viewer + v->onAction(action, undone); + onChange(); + return; + } + } + } } diff --git a/src/render/value/viewer.hpp b/src/render/value/viewer.hpp index 2c387a19..bff557fa 100644 --- a/src/render/value/viewer.hpp +++ b/src/render/value/viewer.hpp @@ -36,6 +36,8 @@ class ValueViewer { inline const FieldP& getField() const { return styleP->fieldP; } /// Return the associated style inline const StyleP& getStyle() const { return styleP; } + /// Return the associated value + inline const ValueP& getValue() const { return valueP; } // Draw this value virtual void draw(RotatedDC& dc) = 0; diff --git a/src/script/image.cpp b/src/script/image.cpp index 3046e52d..abc37ac8 100644 --- a/src/script/image.cpp +++ b/src/script/image.cpp @@ -143,6 +143,8 @@ template <> void Reader::handle(ScriptableImage& s) { if (starts_with(s.script.unparsed, _("script:"))) { s.script.unparsed = s.script.unparsed.substr(7); s.script.parse(*this); + } else if (s.script.unparsed.find_first_of('{') != String.npos) { + s.script.parse(*this, true); } else { // script is a constant function s.script.script = new_intrusive