git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit880edceb6b60b539106acd66889a1bb6d2a45659
parent2039d70111
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-01 13:26
feat: Dropdown::is_expanded says whether a dropdown is taking input

open alone stays true through the closing animation. A host that routes
input to a dropdown ahead of what is under it asks is_expanded. CLAUDE.md
also notes that the runner hands a press to a missed popover before the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 CLAUDE.md                    | 15 +++++++++++++--
 src/widget/input/dropdown.rs | 11 +++++++++++
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index f5b2280..173439d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -247,10 +247,21 @@ same flip / slide / shorten rules inside the window; so does any app run with
 (down for `dir > 0`), skipping header rows and `-` separators, stopping at either end
 rather than wrapping. Both scroll a shortened menu to the row, WITHOUT re-hovering the
 row under the pointer the way `scroll_by` does: the keyboard put the highlight there.
-The menu itself still reads no keys — a host that wants a walkable menu (the designer's
-dialog dropdowns) routes Up/Down to `step_hovered` and runs `hovered_item()` on Enter.
+The menu itself still reads no keys — a host that wants a walkable menu routes Up/Down
+to `step_hovered` and runs `hovered_item()` on Enter.
 `the_keyboard_steps_the_highlight_over_what_cannot_run` is the test.
 
+### A dropdown says whether it is taking input (since 2026-10-01)
+
+`Dropdown::is_expanded()` is `open && !closing`: `open` alone stays true through the
+closing animation, while the plate is still drawn but presses and keys are no longer
+the dropdown's. A host that routes input to a dropdown ahead of what is under it (the
+designer's dialog hosts one) asks this. Such a host should also know that the runner
+hands every left press to each registered popover whose hit test MISSES it, before the
+app sees the press (`close_popovers_missed_by_press`) — and a popover covered by an
+outer popover's claim always misses — so the dropdown may already have taken the press
+by the time the app is asked.
+
 ### A row can open a submenu (since 2026-09-29)
 
 `context_menu::set_row_submenu(idx, SubmenuSpec { options, header_count, sliders })`,
diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index eb4a62b..d1ea2e0 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -245,6 +245,15 @@ impl Dropdown {
         })
     }
 
+    /// Whether the menu is open and taking input: open and not shrinking
+    /// closed. `open` alone stays true through the closing animation, while
+    /// the plate is still drawn but presses and keys are no longer its — a
+    /// host that routes input to the dropdown ahead of what is under it asks
+    /// this.
+    pub fn is_expanded(&self) -> bool {
+        self.open && !self.closing
+    }
+
     /// Set (or clear) the app-owned concentric frame — see the `corner_frame` field docs.
     pub fn set_corner_frame(&mut self, frame: Option<((f32, f32, f32, f32), f32, (bool, bool, bool, bool))>) {
         self.corner_frame = frame;
@@ -1418,10 +1427,12 @@ mod tests {
         assert!(move_changed);
         assert_eq!(dd.hovered_item, Some(1));
 
+        assert!(dd.is_expanded(), "open and taking input");
         // 4. Click option B selects it and starts the animated close
         let select_changed = dd.mouse_input(MouseButton::Left, ElementState::Pressed, 50.0, 70.0, &mut dummy);
         assert!(select_changed);
         assert!(dd.closing, "selection starts the animated contraction");
+        assert!(dd.open && !dd.is_expanded(), "drawn while it shrinks, but no longer taking input");
         dd.land_anim_for_test();
         assert!(!dd.open);
         assert_eq!(dd.selected, 1);