diff --git a/Include/RmlUi/Core/Elements/ElementFormControlSelect.h b/Include/RmlUi/Core/Elements/ElementFormControlSelect.h index 7488b317c..82f4b8040 100644 --- a/Include/RmlUi/Core/Elements/ElementFormControlSelect.h +++ b/Include/RmlUi/Core/Elements/ElementFormControlSelect.h @@ -94,6 +94,11 @@ class RMLUICORE_API ElementFormControlSelect : public ElementFormControl { /// Removes all options from the select control. void RemoveAll(); + /// Show or hide the selection box. + void ShowSelectBox(bool show); + /// Check whether the select box is opened or not. + bool GetSelectBoxVisible(); + protected: /// Moves all children to be under control of the widget. void OnUpdate() override; diff --git a/Source/Core/Elements/ElementFormControlSelect.cpp b/Source/Core/Elements/ElementFormControlSelect.cpp index d447614b5..53106cbde 100644 --- a/Source/Core/Elements/ElementFormControlSelect.cpp +++ b/Source/Core/Elements/ElementFormControlSelect.cpp @@ -110,6 +110,16 @@ void ElementFormControlSelect::RemoveAll() widget->ClearOptions(); } +void ElementFormControlSelect::ShowSelectBox(bool show) +{ + widget->ShowSelectBox(show); +} + +bool ElementFormControlSelect::GetSelectBoxVisible() +{ + return widget->GetSelectBoxVisible(); +} + void ElementFormControlSelect::OnUpdate() { ElementFormControl::OnUpdate(); diff --git a/Source/Core/Elements/WidgetDropDown.cpp b/Source/Core/Elements/WidgetDropDown.cpp index b4c0fc31e..32968ddf4 100644 --- a/Source/Core/Elements/WidgetDropDown.cpp +++ b/Source/Core/Elements/WidgetDropDown.cpp @@ -48,7 +48,7 @@ WidgetDropDown::WidgetDropDown(ElementFormControl* element) lock_selection = false; selection_dirty = false; - box_layout_dirty = false; + box_layout_dirty = DropDownBoxLayoutType::None; value_rml_dirty = false; value_layout_dirty = false; box_visible = false; @@ -151,7 +151,7 @@ void WidgetDropDown::OnUpdate() void WidgetDropDown::OnRender() { - if (box_visible && box_layout_dirty) + if (box_visible && box_layout_dirty != DropDownBoxLayoutType::None) { // Layout the selection box. // The following procedure should ensure that the selection box is never (partly) outside of the context's window. @@ -230,7 +230,16 @@ void WidgetDropDown::OnRender() selection_element->SetOffset(Vector2f(offset_x, offset_y), parent_element); } - box_layout_dirty = false; + // Scroll selected element into view, if we have one + if (int selection = GetSelection(); selection != -1) + { + Rml::ScrollIntoViewOptions scrollOptions { + box_layout_dirty == DropDownBoxLayoutType::Open ? Rml::ScrollAlignment::Start : Rml::ScrollAlignment::Nearest + }; + GetOption(selection)->ScrollIntoView(scrollOptions); + } + + box_layout_dirty = DropDownBoxLayoutType::None; } if (value_layout_dirty) @@ -267,7 +276,7 @@ void WidgetDropDown::OnLayout() value_element->SetOffset(parent_element->GetBox().GetPosition(BoxArea::Content), parent_element); value_element->SetBox(Box(size)); - box_layout_dirty = true; + box_layout_dirty = DropDownBoxLayoutType::Switch; value_layout_dirty = true; } @@ -380,6 +389,8 @@ int WidgetDropDown::AddOption(const String& rml, const String& option_value, int int result = AddOption(std::move(element), before); + value_last_selected = -1; + return result; } @@ -405,6 +416,8 @@ int WidgetDropDown::AddOption(ElementPtr element, int before) option_index = before; } + value_last_selected = -1; + return option_index; } @@ -414,6 +427,8 @@ void WidgetDropDown::RemoveOption(int index) if (!element) return; + value_last_selected = -1; + selection_element->RemoveChild(element); } @@ -421,6 +436,8 @@ void WidgetDropDown::ClearOptions() { while (Element* element = selection_element->GetLastChild()) selection_element->RemoveChild(element); + + value_last_selected = -1; } Element* WidgetDropDown::GetOption(int index) @@ -449,7 +466,8 @@ void WidgetDropDown::OnChildAdd(Element* element) SetSelection(element, true); selection_dirty = true; - box_layout_dirty = true; + value_last_selected = -1; + box_layout_dirty = DropDownBoxLayoutType::Switch; } void WidgetDropDown::OnChildRemove(Element* element) @@ -463,7 +481,8 @@ void WidgetDropDown::OnChildRemove(Element* element) SetSelection(nullptr); selection_dirty = true; - box_layout_dirty = true; + value_last_selected = -1; + box_layout_dirty = DropDownBoxLayoutType::Switch; } void WidgetDropDown::AttachScrollEvent() @@ -583,6 +602,17 @@ void WidgetDropDown::ProcessEvent(Event& event) parent_element->Click(); event.StopPropagation(); break; + case Input::KI_ESCAPE: + if (!box_visible) + break; + if (value_last_selected != -1) + { + SetSelection(GetOption(value_last_selected), true); + value_last_selected = -1; + } + ShowSelectBox(false); + event.StopPropagation(); + break; default: break; } } @@ -620,7 +650,8 @@ void WidgetDropDown::ShowSelectBox(bool show) selection_element->SetPseudoClass("checked", true); value_element->SetPseudoClass("checked", true); button_element->SetPseudoClass("checked", true); - box_layout_dirty = true; + box_layout_dirty = DropDownBoxLayoutType::Open; + value_last_selected = GetSelection(); AttachScrollEvent(); } else @@ -636,4 +667,9 @@ void WidgetDropDown::ShowSelectBox(bool show) box_visible = show; } +bool WidgetDropDown::GetSelectBoxVisible() +{ + return box_visible; +} + } // namespace Rml diff --git a/Source/Core/Elements/WidgetDropDown.h b/Source/Core/Elements/WidgetDropDown.h index 97564ec26..0c8857ba3 100644 --- a/Source/Core/Elements/WidgetDropDown.h +++ b/Source/Core/Elements/WidgetDropDown.h @@ -40,6 +40,12 @@ class ElementFormControl; @author Lloyd Weehuizen */ +enum class DropDownBoxLayoutType { + None, + Open, + Switch +}; + class WidgetDropDown : public EventListener { public: WidgetDropDown(ElementFormControl* element); @@ -100,9 +106,12 @@ class WidgetDropDown : public EventListener { /// Processes the incoming event. void ProcessEvent(Event& event) override; -private: - // Shows or hides the selection box. + /// Shows or hides the selection box. void ShowSelectBox(bool show); + /// Check whether the select box is visible or not. + bool GetSelectBoxVisible(); + +private: void AttachScrollEvent(); void DetachScrollEvent(); @@ -119,8 +128,9 @@ class WidgetDropDown : public EventListener { bool selection_dirty; bool value_rml_dirty; bool value_layout_dirty; - bool box_layout_dirty; + DropDownBoxLayoutType box_layout_dirty; bool box_visible; + int value_last_selected; }; } // namespace Rml