Skip to content

Commit

Permalink
fix a couple quirks with <select>:
Browse files Browse the repository at this point in the history
- selectbox now scrolls when nav is being used
- selectbox ensures that the selected option is scrolled into view, anchored to the top
- "escape" can be used to cancel selection
  • Loading branch information
Paril committed Jan 9, 2024
1 parent 8c02595 commit 80fdc9f
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 10 deletions.
5 changes: 5 additions & 0 deletions Include/RmlUi/Core/Elements/ElementFormControlSelect.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions Source/Core/Elements/ElementFormControlSelect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
50 changes: 43 additions & 7 deletions Source/Core/Elements/WidgetDropDown.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -405,6 +416,8 @@ int WidgetDropDown::AddOption(ElementPtr element, int before)
option_index = before;
}

value_last_selected = -1;

return option_index;
}

Expand All @@ -414,13 +427,17 @@ void WidgetDropDown::RemoveOption(int index)
if (!element)
return;

value_last_selected = -1;

selection_element->RemoveChild(element);
}

void WidgetDropDown::ClearOptions()
{
while (Element* element = selection_element->GetLastChild())
selection_element->RemoveChild(element);

value_last_selected = -1;
}

Element* WidgetDropDown::GetOption(int index)
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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
Expand All @@ -636,4 +667,9 @@ void WidgetDropDown::ShowSelectBox(bool show)
box_visible = show;
}

bool WidgetDropDown::GetSelectBoxVisible()
{
return box_visible;
}

} // namespace Rml
16 changes: 13 additions & 3 deletions Source/Core/Elements/WidgetDropDown.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ class ElementFormControl;
@author Lloyd Weehuizen
*/

enum class DropDownBoxLayoutType {
None,
Open,
Switch
};

class WidgetDropDown : public EventListener {
public:
WidgetDropDown(ElementFormControl* element);
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand Down

0 comments on commit 80fdc9f

Please sign in to comment.