1- // SPDX-License-Identifier: MIT
1+ // SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
33
44package debugui
@@ -23,18 +23,15 @@ func (c *Context) Dropdown(selectedIndex *int, options []string) EventHandler {
2323
2424func (c * Context ) dropdown (selectedIndex * int , options []string , id widgetID ) (EventHandler , error ) {
2525 if selectedIndex == nil || len (options ) == 0 {
26- // If no options or selectedIndex is nil, return a null event handler
2726 return & nullEventHandler {}, nil
2827 }
29- // Clamp selectedIndex to valid range to prevent out-of-bounds access
3028 if * selectedIndex < 0 || * selectedIndex >= len (options ) {
3129 * selectedIndex = 0
3230 }
3331 last := * selectedIndex
3432
3533 dropdownID := c .idFromString ("dropdown:" + string (id ))
3634
37- // Ensure dropdown container always exists (create it if needed)
3835 dropdownContainer := c .container (dropdownID , 0 )
3936
4037 // Handle delayed closing of dropdown
@@ -45,7 +42,6 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
4542 }
4643 }
4744
48- // Start with the dropdown closed
4945 if dropdownContainer .layout .Bounds .Empty () {
5046 dropdownContainer .open = false
5147 }
@@ -54,30 +50,24 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
5450 windowOptions := optionDropdown | optionNoResize | optionNoTitle
5551
5652 if err := c .window ("" , image.Rectangle {}, windowOptions , dropdownID , func (layout ContainerLayout ) {
57- // Ensure dropdown container reference is fresh for each render
5853 if cnt := c .container (dropdownID , 0 ); cnt != nil {
5954 if cnt .open {
6055 c .bringToFront (cnt )
6156 }
6257 }
63-
64- // full width dropdown
6558 c .SetGridLayout ([]int {- 1 }, nil )
6659
67- // Render each dropdown option as a clickable button
6860 for i , option := range options {
6961 c .IDScope (option , func () {
7062 isSelected := i == * selectedIndex
7163
72- // Highlight the currently selected option
7364 var buttonColor int
7465 if isSelected {
7566 buttonColor = colorButtonFocus
7667 } else {
7768 buttonColor = colorButton
7869 }
7970
80- // Create clickable button widget for this option
8171 pc := caller ()
8272 buttonID := c .idFromCaller (pc )
8373 var wasPressed bool
@@ -87,14 +77,12 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
8777 var e EventHandler
8878
8979 if c .pointing .justPressed () && c .focus == buttonID {
90- // Handle option selection
9180 wasPressed = true
9281 e = & eventHandler {}
9382 }
9483
9584 return e
9685 }, func (bounds image.Rectangle ) {
97- // Draw the option button with appropriate styling
9886 c .drawWidgetFrame (buttonID , bounds , buttonColor , optionAlignCenter )
9987 if len (option ) > 0 {
10088 c .drawWidgetText (option , bounds , colorText , optionAlignCenter )
@@ -119,7 +107,6 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
119107 return nil , nil
120108 })
121109
122- // Create the main dropdown button that toggles the menu
123110 return c .widget (id , optionAlignCenter , nil , func (bounds image.Rectangle , wasFocused bool ) EventHandler {
124111 var e EventHandler
125112
@@ -138,18 +125,12 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
138125 }
139126 }
140127
141- // Toggle dropdown when button is clicked
142128 if c .pointing .justPressed () && c .focus == id {
143- // Check if dropdown container exists and its state
144-
145- isOpen := dropdownContainer .open
146-
147- if isOpen {
129+ if dropdownContainer .open {
148130 // Close the dropdown immediately and cancel any pending delay
149131 dropdownContainer .open = false
150132 dropdownContainer .dropdownCloseDelay = 0
151133 } else {
152- // Store the current state before opening, made in some desperate attempts to avoid feedback loops
153134 wasClosedBefore := ! dropdownContainer .open
154135
155136 // Open the dropdown and cancel any pending close delay
@@ -172,24 +153,19 @@ func (c *Context) dropdown(selectedIndex *int, options []string, id widgetID) (E
172153 }
173154 }
174155 }
175-
176- // Generate event if user changed selection
177156 if last != * selectedIndex {
178157 e = & eventHandler {}
179158 }
180159
181160 return e
182161 }, func (bounds image.Rectangle ) {
183- // Draw the dropdown button appearance
184162 c .drawWidgetFrame (id , bounds , colorButton , optionAlignCenter )
185163
186- // Show currently selected text (reserve space for arrow - use widget height for square arrow area)
187164 arrowWidth := bounds .Dy ()
188165 textBounds := bounds
189166 textBounds .Max .X -= arrowWidth
190167 c .drawWidgetText (options [* selectedIndex ], textBounds , colorText , optionAlignCenter )
191168
192- // Draw dropdown arrow indicator (up/down based on current state)
193169 arrowBounds := image .Rect (bounds .Max .X - arrowWidth , bounds .Min .Y , bounds .Max .X , bounds .Max .Y )
194170 icon := iconDown
195171 if c .container (dropdownID , 0 ).open {
0 commit comments