diff --git a/src/gui/drop_down_list.cpp b/src/gui/drop_down_list.cpp index 68c7e799..b4148d91 100644 --- a/src/gui/drop_down_list.cpp +++ b/src/gui/drop_down_list.cpp @@ -40,6 +40,10 @@ class DropDownHider : public wxEvtHandler { list.hide(false); if (nh) nh->ProcessEvent(ev); return false; + } else if (t == wxEVT_MOTION) { + // send along all motion events + list.ProcessEvent(ev); + return wxEvtHandler::ProcessEvent(ev); } else { // if (t !=10093 && t !=10098 && t !=10097 && t !=10099 && t !=10004 && t !=10062 // && t !=10025 && t !=10035 && t !=10034 && t !=10036 && t !=10042 && t !=10119) @@ -64,7 +68,8 @@ DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer) , open_sub_menu(nullptr) , parent_menu(nullptr) , viewer(viewer) - , hider(is_submenu ? nullptr : new DropDownHider(*this)) + , hider (is_submenu ? nullptr : new DropDownHider(*this)) + , hider2(is_submenu ? nullptr : new DropDownHider(*this)) { if (is_submenu) { parent_menu = &dynamic_cast(*GetParent()); @@ -80,6 +85,7 @@ DropDownList::DropDownList(Window* parent, bool is_submenu, ValueViewer* viewer) DropDownList::~DropDownList() { realHide(); // restore event handler before deleting it delete hider; + delete hider2; } void DropDownList::show(bool in_place, wxPoint pos) { @@ -128,8 +134,9 @@ void DropDownList::show(bool in_place, wxPoint pos) { Position(pos, wxSize(0, parent_height)); // set event handler if (hider) { - Window* parent = wxGetTopLevelParent(GetParent()); - parent->PushEventHandler(hider); + assert(hider2); + wxGetTopLevelParent(GetParent())->PushEventHandler(hider); + GetParent() ->PushEventHandler(hider2); } // show if (selected_item == NO_SELECTION && itemCount() > 0) selected_item = 0; // select first item by default @@ -139,15 +146,21 @@ void DropDownList::show(bool in_place, wxPoint pos) { redrawArrowOnParent(); } -void DropDownList::hide(bool event) { +void DropDownList::hide(bool event, bool allow_veto) { + // send event + if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) { + bool close = select(selected_item); + if (allow_veto && !close) { + Refresh(false); + return; + } + } // hide root DropDownList* root = this; while (root->parent_menu) { root = root->parent_menu; } root->realHide(); - // send event - if (event && selected_item != NO_SELECTION && itemEnabled(selected_item)) select(selected_item); } void DropDownList::realHide() { @@ -160,8 +173,8 @@ void DropDownList::realHide() { } else { redrawArrowOnParent(); // disconnect event handler - Window* parent = wxGetTopLevelParent(GetParent()); - parent->RemoveEventHandler(hider); + GetParent() ->RemoveEventHandler(hider2); + wxGetTopLevelParent(GetParent())->RemoveEventHandler(hider); } } @@ -348,6 +361,11 @@ bool DropDownList::onCharInParent(wxKeyEvent& ev) { } break; case WXK_RETURN: + if (!showSubMenu() && (selected_item == NO_SELECTION || itemEnabled(selected_item))) { + hide(true, false); // don't veto; always close + } + break; + case WXK_SPACE: if (!showSubMenu() && (selected_item == NO_SELECTION || itemEnabled(selected_item))) { hide(true); } diff --git a/src/gui/drop_down_list.hpp b/src/gui/drop_down_list.hpp index 60a2ec7f..67fdba09 100644 --- a/src/gui/drop_down_list.hpp +++ b/src/gui/drop_down_list.hpp @@ -31,7 +31,7 @@ class DropDownList : public wxPopupWindow { /** if in_place, then shows the list at the position pos */ void show(bool in_place, wxPoint pos); /// Close the list, optionally send an onSelect event - void hide(bool event); + void hide(bool event, bool allow_veto = true); // --------------------------------------------------- : Parent control /// Takes all keyboard events from a FieldEditor @@ -48,7 +48,9 @@ class DropDownList : public wxPopupWindow { static const size_t NO_SELECTION = (size_t)-1; /// Signal that the list is closed and something is selected - virtual void select(size_t selection) = 0; + /** Returns true if the event was handled and the list should be hidden, + * false keeps the list open. */ + virtual bool select(size_t selection) = 0; /// When the list is being opened, what should be selected? virtual size_t selection() const = 0; @@ -86,7 +88,7 @@ class DropDownList : public wxPopupWindow { DropDownList* open_sub_menu; ///< The sub menu that is currently shown, if any DropDownList* parent_menu; ///< The parent menu, only applies to sub menus ValueViewer* viewer; ///< The parent viewer object (optional) - DropDownHider* hider; ///< Class to hide this window when we lose focus + DropDownHider* hider, *hider2; ///< Class to hide this window when we lose focus // --------------------------------------------------- : Events DECLARE_EVENT_TABLE(); @@ -94,7 +96,9 @@ class DropDownList : public wxPopupWindow { void onPaint(wxPaintEvent&); void onLeftDown(wxMouseEvent&); void onLeftUp (wxMouseEvent&); - void onMotion (wxMouseEvent&); + protected: + virtual void onMotion(wxMouseEvent&); // allow override + private: // --------------------------------------------------- : Privates diff --git a/src/gui/value/choice.cpp b/src/gui/value/choice.cpp index 0f03abc9..7c3f4601 100644 --- a/src/gui/value/choice.cpp +++ b/src/gui/value/choice.cpp @@ -227,13 +227,14 @@ void DropDownChoiceList::onShow() { generateThumbnailImages(); } -void DropDownChoiceList::select(size_t item) { +bool DropDownChoiceList::select(size_t item) { if (isFieldDefault(item)) { dynamic_cast(cve).change( Defaultable() ); } else { ChoiceField::ChoiceP choice = getChoice(item); dynamic_cast(cve).change( field().choices->choiceName(choice->first_id) ); } + return true; } size_t DropDownChoiceList::selection() const { @@ -278,8 +279,6 @@ ChoiceValueEditor::~ChoiceValueEditor() { } bool ChoiceValueEditor::onLeftDown(const RealPoint& pos, wxMouseEvent& ev) { - //HACK TODO REMOVEME - //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 29a00ca5..1818adee 100644 --- a/src/gui/value/choice.hpp +++ b/src/gui/value/choice.hpp @@ -95,7 +95,7 @@ class DropDownChoiceList : public DropDownChoiceListBase { protected: virtual void onShow(); - virtual void select(size_t item); + virtual bool select(size_t item); virtual size_t selection() const; virtual DropDownList* createSubMenu(ChoiceField::ChoiceP group) const; }; diff --git a/src/gui/value/color.cpp b/src/gui/value/color.cpp index 28d409cb..e95a1159 100644 --- a/src/gui/value/color.cpp +++ b/src/gui/value/color.cpp @@ -27,7 +27,7 @@ class DropDownColorList : public DropDownList { virtual String itemText(size_t item) const; virtual void drawIcon(DC& dc, int x, int y, size_t item, bool selected) const; - virtual void select(size_t item); + virtual bool select(size_t item); virtual size_t selection() const; private: @@ -112,7 +112,7 @@ size_t DropDownColorList::selection() const { } return selection; } -void DropDownColorList::select(size_t item) { +bool DropDownColorList::select(size_t item) { if (isDefault(item)) { cve.change( Defaultable()); } else if (isCustom(item)) { @@ -120,6 +120,7 @@ void DropDownColorList::select(size_t item) { } else { cve.change(field().choices[item - hasDefault()]->color); } + return true; } // ----------------------------------------------------------------------------- : ColorValueEditor diff --git a/src/gui/value/multiple_choice.cpp b/src/gui/value/multiple_choice.cpp index 4d893528..d1c02cbc 100644 --- a/src/gui/value/multiple_choice.cpp +++ b/src/gui/value/multiple_choice.cpp @@ -19,20 +19,26 @@ class DropDownMultipleChoiceList : public DropDownChoiceListBase { DropDownMultipleChoiceList(Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group); protected: - virtual void select(size_t item); + virtual void onShow(); + virtual bool 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; + + virtual void onMotion(wxMouseEvent&); + private: + bool kept_open; ///< Was the list kept open after selecting a choice, if so, be eager to close it }; DropDownMultipleChoiceList::DropDownMultipleChoiceList (Window* parent, bool is_submenu, ValueViewer& cve, ChoiceField::ChoiceP group) : DropDownChoiceListBase(parent, is_submenu, cve, group) + , kept_open(false) { icon_size.width += 16; } -void DropDownMultipleChoiceList::select(size_t item) { +bool DropDownMultipleChoiceList::select(size_t item) { MultipleChoiceValueEditor& mcve = dynamic_cast(cve); if (isFieldDefault(item)) { mcve.toggleDefault(); @@ -40,6 +46,10 @@ void DropDownMultipleChoiceList::select(size_t item) { ChoiceField::ChoiceP choice = getChoice(item); mcve.toggle(choice->first_id); } + // keep the box open + DropDownChoiceListBase::onShow(); // update 'enabled' + kept_open = true; + return false; } void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, bool selected) const { @@ -67,17 +77,31 @@ void DropDownMultipleChoiceList::drawIcon(DC& dc, int x, int y, size_t item, boo DropDownChoiceListBase::drawIcon(dc, x + 16, y, item, selected); } -size_t DropDownMultipleChoiceList::selection() const { +void DropDownMultipleChoiceList::onShow() { + DropDownChoiceListBase::onShow(); // we need thumbnail images soon const_cast(this)->generateThumbnailImages(); - // we don't know the selection - return NO_SELECTION; + kept_open = false; +} + +size_t DropDownMultipleChoiceList::selection() const { + return NO_SELECTION; // we don't know the selection } DropDownList* DropDownMultipleChoiceList::createSubMenu(ChoiceField::ChoiceP group) const { return new DropDownMultipleChoiceList(const_cast(this), true, cve, group); } +void DropDownMultipleChoiceList::onMotion(wxMouseEvent& ev) { + if (kept_open) { + wxSize cs = GetClientSize(); + if (ev.GetX() < 0 || ev.GetY() < 0 || ev.GetX() >= cs.x || ev.GetY() >= cs.y) { + hide(false); // outside box; hide it + } + } + DropDownChoiceListBase::onMotion(ev); +} + // ----------------------------------------------------------------------------- : MultipleChoiceValueEditor IMPLEMENT_VALUE_EDITOR(MultipleChoice) {}