From 3dd0521c40c79385e3bda40db9cd11302dd3a898 Mon Sep 17 00:00:00 2001 From: twanvl Date: Thu, 12 Apr 2007 19:18:17 +0000 Subject: [PATCH] MultipleChoiceValueEditor: implemented drop down list git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@252 0fc631ac-6414-0410-93d0-97cfa31319b6 --- src/gui/value/choice.cpp | 110 +++++++++++++++++------------- src/gui/value/choice.hpp | 31 ++++++--- src/gui/value/multiple_choice.cpp | 104 +++++++++++++++++++++++++++- src/gui/value/multiple_choice.hpp | 13 ++++ 4 files changed, 199 insertions(+), 59 deletions(-) diff --git a/src/gui/value/choice.cpp b/src/gui/value/choice.cpp index 82e670a4..2ad40076 100644 --- a/src/gui/value/choice.cpp +++ b/src/gui/value/choice.cpp @@ -19,7 +19,7 @@ DECLARE_TYPEOF_COLLECTION(ChoiceField::ChoiceP); class ChoiceThumbnailRequest : public ThumbnailRequest { public: - ChoiceThumbnailRequest(ChoiceValueEditor* cve, int id, bool from_disk); + ChoiceThumbnailRequest(ValueViewer* cve, int id, bool from_disk); virtual Image generate(); virtual void store(const Image&); private: @@ -27,10 +27,10 @@ class ChoiceThumbnailRequest : public ThumbnailRequest { int id; }; -ChoiceThumbnailRequest::ChoiceThumbnailRequest(ChoiceValueEditor* cve, int id, bool from_disk) +ChoiceThumbnailRequest::ChoiceThumbnailRequest(ValueViewer* cve, int id, bool from_disk) : ThumbnailRequest( cve, - cve->viewer.stylesheet->name() + _("/") + cve->field().name + _("/") << id, + cve->viewer.stylesheet->name() + _("/") + cve->getField()->name + _("/") << id, from_disk ? cve->viewer.stylesheet->lastModified() : wxDateTime::Now() ) @@ -82,9 +82,10 @@ void ChoiceThumbnailRequest::store(const Image& img) { } } -// ----------------------------------------------------------------------------- : DropDownChoiceList +// ----------------------------------------------------------------------------- : DropDownChoiceListBase -DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group) +DropDownChoiceListBase::DropDownChoiceListBase + (Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group) : DropDownList(parent, is_submenu, is_submenu ? nullptr : &cve) , cve(cve) , group(group) @@ -94,11 +95,11 @@ DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ChoiceVa item_size.height = max(16., item_size.height); } -size_t DropDownChoiceList::itemCount() const { +size_t DropDownChoiceListBase::itemCount() const { return group->choices.size() + hasDefault(); } -ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const { +ChoiceField::ChoiceP DropDownChoiceListBase::getChoice(size_t item) const { if (isGroupDefault(item)) { return group; } else { @@ -106,7 +107,7 @@ ChoiceField::ChoiceP DropDownChoiceList::getChoice(size_t item) const { } } -String DropDownChoiceList::itemText(size_t item) const { +String DropDownChoiceListBase::itemText(size_t item) const { if (isFieldDefault(item)) { return field().default_name; } else if (isGroupDefault(item)) { @@ -116,10 +117,10 @@ String DropDownChoiceList::itemText(size_t item) const { return choice->name; } } -bool DropDownChoiceList::lineBelow(size_t item) const { +bool DropDownChoiceListBase::lineBelow(size_t item) const { return isDefault(item); } -DropDownList* DropDownChoiceList::submenu(size_t item) const { +DropDownList* DropDownChoiceListBase::submenu(size_t item) const { if (isDefault(item)) return nullptr; item -= hasDefault(); if (item >= submenus.size()) submenus.resize(item + 1); @@ -127,14 +128,14 @@ DropDownList* DropDownChoiceList::submenu(size_t item) const { ChoiceField::ChoiceP choice = group->choices[item]; if (choice->isGroup()) { // create submenu - submenus[item].reset(new DropDownChoiceList(const_cast(this), true, cve, choice)); + submenus[item].reset(createSubMenu(choice)); } return submenus[item].get(); } -void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { +void DropDownChoiceListBase::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { // imagelist to use - wxImageList* il = cve.style().thumbnails; + wxImageList* il = style().thumbnails; assert(il); // find the image for the item int image_id; @@ -149,29 +150,68 @@ void DropDownChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool select } } +void DropDownChoiceListBase::generateThumbnailImages() { + if (!isRoot()) return; + if (!style().thumbnails) { + style().thumbnails = new wxImageList(16,16); + } + int image_count = style().thumbnails->GetImageCount(); + int end = group->lastId(); + Context& ctx = cve.viewer.getContext(); + for (int i = 0 ; i < end ; ++i) { + String name = cannocial_name_form(group->choiceName(i)); + ScriptableImage& img = style().choice_images[name]; + bool up_to_date = img.upToDate(ctx, style().thumbnail_age); + if (i >= image_count || !up_to_date) { + // TODO : handle the case where image i was previously skipped + // request this thumbnail + thumbnail_thread.request( new_shared3(&cve, i, up_to_date && !style().invalidated_images) ); + } + } + style().thumbnail_age.update(); +} + +void DropDownChoiceListBase::onIdle(wxIdleEvent& ev) { + if (!isRoot()) return; + if (thumbnail_thread.done(&cve)) { + Refresh(false); + } +} + +BEGIN_EVENT_TABLE(DropDownChoiceListBase, DropDownList) + EVT_IDLE(DropDownChoiceListBase::onIdle) +END_EVENT_TABLE() + +// ----------------------------------------------------------------------------- : DropDownChoiceList + +DropDownChoiceList::DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group) + : DropDownChoiceListBase(parent, is_submenu, cve, group) +{} void DropDownChoiceList::select(size_t item) { if (isFieldDefault(item)) { - cve.change( Defaultable() ); + dynamic_cast(cve).change( Defaultable() ); } else { ChoiceField::ChoiceP choice = getChoice(item); - cve.change( field().choices->choiceName(choice->first_id) ); + dynamic_cast(cve).change( field().choices->choiceName(choice->first_id) ); } } + size_t DropDownChoiceList::selection() const { // we need thumbnail images soon const_cast(this)->generateThumbnailImages(); // selected item - int id = field().choices->choiceId(cve.value().value()); + const Defaultable& value = dynamic_cast(cve).value().value(); + int id = field().choices->choiceId(value); // id of default item if (hasFieldDefault()) { - if (cve.value().value.isDefault()) { + if (value.isDefault()) { // default is selected default_id = id; return 0; } else { // run default script to find out what the default choice would be - String default_choice = *cve.field().default_script.invoke( cve.viewer.getContext() ); + String default_choice = *field().default_script.invoke( cve.viewer.getContext() ); default_id = group->choiceId(default_choice); } } @@ -186,38 +226,10 @@ size_t DropDownChoiceList::selection() const { return NO_SELECTION; } -void DropDownChoiceList::generateThumbnailImages() { - if (!isRoot()) return; - if (!cve.style().thumbnails) { - cve.style().thumbnails = new wxImageList(16,16); - } - int image_count = cve.style().thumbnails->GetImageCount(); - int end = group->lastId(); - Context& ctx = cve.viewer.getContext(); - for (int i = 0 ; i < end ; ++i) { - String name = cannocial_name_form(group->choiceName(i)); - ScriptableImage& img = cve.style().choice_images[name]; - bool up_to_date = img.upToDate(ctx, cve.style().thumbnail_age); - if (i >= image_count || !up_to_date) { - // TODO : handle the case where image i was previously skipped - // request this thumbnail - thumbnail_thread.request( new_shared3(&cve, i, up_to_date && !cve.style().invalidated_images) ); - } - } - cve.style().thumbnail_age.update(); +DropDownList* DropDownChoiceList::createSubMenu(ChoiceField::ChoiceP group) const { + return new DropDownChoiceList(const_cast(this), true, cve, group); } -void DropDownChoiceList::onIdle(wxIdleEvent& ev) { - if (!isRoot()) return; - if (thumbnail_thread.done(&cve)) { - Refresh(false); - } -} - -BEGIN_EVENT_TABLE(DropDownChoiceList, DropDownList) - EVT_IDLE(DropDownChoiceList::onIdle) -END_EVENT_TABLE() - // ----------------------------------------------------------------------------- : ChoiceValueEditor IMPLEMENT_VALUE_EDITOR(Choice) @@ -230,7 +242,7 @@ ChoiceValueEditor::~ChoiceValueEditor() { bool ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { //HACK TODO REMOVEME - thumbnail_thread.abortAll(); + //thumbnail_thread.abortAll(); return drop_down->onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook()); } bool ChoiceValueEditor::onChar(wxKeyEvent& ev) { diff --git a/src/gui/value/choice.hpp b/src/gui/value/choice.hpp index b736f1ea..05ec79a0 100644 --- a/src/gui/value/choice.hpp +++ b/src/gui/value/choice.hpp @@ -42,10 +42,11 @@ class ChoiceValueEditor : public ChoiceValueViewer, public ValueEditor { // ----------------------------------------------------------------------------- : DropDownChoiceList -// A drop down list of color choices -class DropDownChoiceList : public DropDownList { +/// A drop down list of choices +/** This is a base class, used for single and multiple choice fields */ +class DropDownChoiceListBase : public DropDownList { public: - DropDownChoiceList(Window* parent, bool is_submenu, ChoiceValueEditor& cve, ChoiceField::ChoiceP group); + DropDownChoiceListBase(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group); protected: virtual size_t itemCount() const; @@ -54,23 +55,24 @@ class DropDownChoiceList : public DropDownList { virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; virtual DropDownList* submenu(size_t item) const; - virtual void select(size_t item); - virtual size_t selection() const; + protected: + virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const = 0; private: DECLARE_EVENT_TABLE(); - ChoiceValueEditor& cve; + ValueViewer& cve; ///< Editor this list belongs to ChoiceField::ChoiceP group; ///< Group this menu shows mutable vector submenus; mutable int default_id; ///< Item id for the default item (if !hasFieldDefault()) this is undefined) - inline const ChoiceField& field() const { return cve.field(); } + inline ChoiceField& field() const { return static_cast(*cve.getField()); } + inline ChoiceStyle& style() const { return static_cast(*cve.getStyle()); } inline bool isRoot() const { return group == field().choices; } inline bool hasFieldDefault() const { return isRoot() && field().default_script; } inline bool hasGroupDefault() const { return group->hasDefault(); } - inline bool hasDefault() const { return hasFieldDefault() || hasGroupDefault(); } + virtual 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(); } @@ -82,5 +84,18 @@ class DropDownChoiceList : public DropDownList { void onIdle(wxIdleEvent&); }; +// ----------------------------------------------------------------------------- : DropDownChoiceList + +/// A drop down list of color choices +class DropDownChoiceList : public DropDownChoiceListBase { + public: + DropDownChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group); + + protected: + virtual void select(size_t item); + virtual size_t selection() const; + virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const; +}; + // ----------------------------------------------------------------------------- : EOF #endif diff --git a/src/gui/value/multiple_choice.cpp b/src/gui/value/multiple_choice.cpp index e10c100b..0b7f48d0 100644 --- a/src/gui/value/multiple_choice.cpp +++ b/src/gui/value/multiple_choice.cpp @@ -7,12 +7,83 @@ // ----------------------------------------------------------------------------- : Includes #include +#include +#include #include +// ----------------------------------------------------------------------------- : DropDownMultipleChoiceList + +/// A drop down list of color choices +class DropDownMultipleChoiceList : public DropDownChoiceListBase { + public: + DropDownMultipleChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group); + + protected: + virtual void select(size_t item); + virtual size_t selection() const; + virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const; + virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; +}; + +DropDownMultipleChoiceList::DropDownMultipleChoiceList + (Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group) + : DropDownChoiceListBase(parent, is_submenu, cve, group) +{ + icon_size.width += 16; +} + +void DropDownMultipleChoiceList::select(size_t item) { + if (isFieldDefault(item)) { + // should not happen + } else { + ChoiceField::ChoiceP choice = getChoice(item); + dynamic_cast(cve).toggle(choice->first_id); + } +} + +void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { + // is this item active? + bool active = false; + if (!isFieldDefault(item)) { + ChoiceField::ChoiceP choice = getChoice(item); + active = dynamic_cast(cve).active[choice->first_id]; + } + // draw checkbox + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + dc.DrawRectangle(x,y,16,16); + wxRect rect = RealRect(x+2,y+2,12,12); + draw_checkbox(nullptr, dc, rect, active); + // draw icon + DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected); +} + +size_t DropDownMultipleChoiceList::selection() const { + // we need thumbnail images soon + const_cast(this)->generateThumbnailImages(); + // we don't know the selection + return NO_SELECTION; +} + +DropDownList* DropDownMultipleChoiceList::createSubMenu(ChoiceField::ChoiceP group) const { + return new DropDownMultipleChoiceList(const_cast(this), true, cve, group); +} + // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor IMPLEMENT_VALUE_EDITOR(MultipleChoice) {} +MultipleChoiceValueEditor::~MultipleChoiceValueEditor() { + thumbnail_thread.abort(this); +} + +DropDownList& MultipleChoiceValueEditor::initDropDown() { + if (!drop_down) { + drop_down.reset(new DropDownMultipleChoiceList(&editor(), false, *this, field().choices)); + } + return *drop_down; +} + void MultipleChoiceValueEditor::determineSize(bool force_fit) { if (!nativeLook()) return; // item height @@ -24,7 +95,7 @@ void MultipleChoiceValueEditor::determineSize(bool force_fit) { bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { // find item under cursor - if (style().render_style && RENDER_CHECKLIST) { + if (style().render_style & RENDER_CHECKLIST) { int id = (pos.y - style().top) / item_height; int end = field().choices->lastId(); if (id >= 0 && id < end) { @@ -32,10 +103,39 @@ bool MultipleChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& e return true; } } else { - // TODO + // open a drop down menu + return initDropDown().onMouseInParent(ev, style().popup_style == POPUP_DROPDOWN_IN_PLACE && !nativeLook()); } return false; } +bool MultipleChoiceValueEditor::onChar(wxKeyEvent& ev) { + if (style().render_style & RENDER_CHECKLIST) { + // todo; + return false; + } else { + return initDropDown().onCharInParent(ev); + } +} +void MultipleChoiceValueEditor::onLoseFocus() { + if (drop_down) drop_down->hide(false); +} + +void MultipleChoiceValueEditor::onValueChange() { + MultipleChoiceValueViewer::onValueChange(); + // determine active values + active.clear(); + vector selected; + value().get(selected); + vector::iterator select_it = selected.begin(); + // for each choice... + int end = field().choices->lastId(); + for (int i = 0 ; i < end ; ++i) { + String choice = field().choices->choiceName(i); + bool is_active = select_it != selected.end() && *select_it == choice; + if (is_active) select_it++; + active.push_back(is_active); + } +} void MultipleChoiceValueEditor::toggle(int id) { String new_value; diff --git a/src/gui/value/multiple_choice.hpp b/src/gui/value/multiple_choice.hpp index eec111fe..bb72f91b 100644 --- a/src/gui/value/multiple_choice.hpp +++ b/src/gui/value/multiple_choice.hpp @@ -11,6 +11,7 @@ #include #include +#include #include // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor @@ -19,10 +20,22 @@ class MultipleChoiceValueEditor : public MultipleChoiceValueViewer, public ValueEditor { public: DECLARE_VALUE_EDITOR(MultipleChoice); + ~MultipleChoiceValueEditor(); + + virtual void onValueChange(); virtual void determineSize(bool force_fit); + virtual bool onLeftDown (const RealPoint& pos, wxMouseEvent& ev); + virtual bool onChar(wxKeyEvent& ev); + virtual void onLoseFocus(); + private: + DropDownListP drop_down; + vector active; ///< Which choices are active? (note: vector is evil) + friend class DropDownMultipleChoiceList; + /// Initialize the drop down list + DropDownList& initDropDown(); /// Toggle a choice or on or off void toggle(int id); };