Browse Source

Scroll the selection box when navigating `<select>` options with controller/keyboard (#566)

And scroll the selected option into view when opening the selection box.

Further, add ability to programmatically show or hide the selection box.

Co-authored-by: Michael Ragazzon <[email protected]>
Jonathan 1 year ago
parent
commit
ad62159bb5

+ 7 - 0
Include/RmlUi/Core/Elements/ElementFormControlSelect.h

@@ -93,6 +93,13 @@ public:
 
 	/// Removes all options from the select control.
 	void RemoveAll();
+	
+	/// Show the selection box.
+	void ShowSelectBox();
+	/// Hide the selection box.
+	void HideSelectBox();
+	/// Check whether the select box is opened or not.
+	bool IsSelectBoxVisible();
 
 protected:
 	/// Moves all children to be under control of the widget.

+ 15 - 0
Source/Core/Elements/ElementFormControlSelect.cpp

@@ -110,6 +110,21 @@ void ElementFormControlSelect::RemoveAll()
 	widget->ClearOptions();
 }
 
+void ElementFormControlSelect::ShowSelectBox()
+{
+	widget->ShowSelectBox();
+}
+
+void ElementFormControlSelect::HideSelectBox()
+{
+	widget->HideSelectBox();
+}
+
+bool ElementFormControlSelect::IsSelectBoxVisible()
+{
+	return widget->IsSelectBoxVisible();
+}
+
 void ElementFormControlSelect::OnUpdate()
 {
 	ElementFormControl::OnUpdate();

+ 45 - 25
Source/Core/Elements/WidgetDropDown.cpp

@@ -49,6 +49,7 @@ WidgetDropDown::WidgetDropDown(ElementFormControl* element)
 	lock_selection = false;
 	selection_dirty = false;
 	box_layout_dirty = false;
+	box_opened_since_last_format = false;
 	value_rml_dirty = false;
 	value_layout_dirty = false;
 	box_visible = false;
@@ -230,6 +231,19 @@ void WidgetDropDown::OnRender()
 			selection_element->SetOffset(Vector2f(offset_x, offset_y), parent_element);
 		}
 
+		int selection = GetSelection();
+
+		// Scroll selected element into view, if we have one
+		if (selection != -1)
+		{
+			ScrollIntoViewOptions scroll_options = {
+				box_opened_since_last_format ? ScrollAlignment::Center : ScrollAlignment::Nearest,
+				ScrollAlignment::Nearest,
+				ScrollBehavior::Instant,
+			};
+			GetOption(selection)->ScrollIntoView(scroll_options);
+		}
+		box_opened_since_last_format = false;
 		box_layout_dirty = false;
 	}
 
@@ -502,7 +516,7 @@ void WidgetDropDown::ProcessEvent(Event& event)
 						SetSelection(current_element);
 						event.StopPropagation();
 
-						ShowSelectBox(false);
+						HideSelectBox();
 						parent_element->Focus();
 					}
 				}
@@ -523,9 +537,9 @@ void WidgetDropDown::ProcessEvent(Event& event)
 			}
 
 			if (selection_element->GetComputedValues().visibility() == Style::Visibility::Hidden)
-				ShowSelectBox(true);
+				ShowSelectBox();
 			else
-				ShowSelectBox(false);
+				HideSelectBox();
 		}
 	}
 	break;
@@ -542,7 +556,7 @@ void WidgetDropDown::ProcessEvent(Event& event)
 	{
 		if (event.GetTargetElement() == parent_element)
 		{
-			ShowSelectBox(false);
+			HideSelectBox();
 			value_element->SetPseudoClass("focus", false);
 			button_element->SetPseudoClass("focus", false);
 		}
@@ -604,7 +618,7 @@ void WidgetDropDown::ProcessEvent(Event& event)
 			}
 
 			if (!scrolls_selection_box)
-				ShowSelectBox(false);
+				HideSelectBox();
 		}
 	}
 	break;
@@ -612,28 +626,34 @@ void WidgetDropDown::ProcessEvent(Event& event)
 	}
 }
 
-void WidgetDropDown::ShowSelectBox(bool show)
+void WidgetDropDown::ShowSelectBox()
 {
-	if (show)
-	{
-		selection_element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Visible));
-		selection_element->SetPseudoClass("checked", true);
-		value_element->SetPseudoClass("checked", true);
-		button_element->SetPseudoClass("checked", true);
-		box_layout_dirty = true;
-		AttachScrollEvent();
-	}
-	else
-	{
-		selection_element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Hidden));
-		selection_element->RemoveProperty(PropertyId::Height);
-		selection_element->SetPseudoClass("checked", false);
-		value_element->SetPseudoClass("checked", false);
-		button_element->SetPseudoClass("checked", false);
-		DetachScrollEvent();
-	}
+	selection_element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Visible));
+	selection_element->SetPseudoClass("checked", true);
+	value_element->SetPseudoClass("checked", true);
+	button_element->SetPseudoClass("checked", true);
+	box_layout_dirty = true;
+	box_opened_since_last_format = true;
+	AttachScrollEvent();
 
-	box_visible = show;
+	box_visible = true;
+}
+
+void WidgetDropDown::HideSelectBox()
+{
+	selection_element->SetProperty(PropertyId::Visibility, Property(Style::Visibility::Hidden));
+	selection_element->RemoveProperty(PropertyId::Height);
+	selection_element->SetPseudoClass("checked", false);
+	value_element->SetPseudoClass("checked", false);
+	button_element->SetPseudoClass("checked", false);
+	DetachScrollEvent();
+
+	box_visible = false;
+}
+
+bool WidgetDropDown::IsSelectBoxVisible()
+{
+	return box_visible;
 }
 
 } // namespace Rml

+ 8 - 3
Source/Core/Elements/WidgetDropDown.h

@@ -100,10 +100,14 @@ public:
 	/// Processes the incoming event.
 	void ProcessEvent(Event& event) override;
 
-private:
-	// Shows or hides the selection box.
-	void ShowSelectBox(bool show);
+	/// Shows the selection box.
+	void ShowSelectBox();
+	/// Hides the selection box.
+	void HideSelectBox();
+	/// Check whether the select box is visible or not.
+	bool IsSelectBoxVisible();
 
+private:
 	void AttachScrollEvent();
 	void DetachScrollEvent();
 
@@ -120,6 +124,7 @@ private:
 	bool value_rml_dirty;
 	bool value_layout_dirty;
 	bool box_layout_dirty;
+	bool box_opened_since_last_format;
 	bool box_visible;
 };