Skip to repository content140 lines · 4.5 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T04:43:46.739Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
dropdown.rs
1use std::rc::Rc;
2
3use gpui::{App, ElementId, IntoElement, RenderOnce, SharedString};
4use heck::ToTitleCase as _;
5use ui::{
6 ButtonSize, ContextMenu, Disableable as _, DropdownMenu, DropdownStyle, FluentBuilder as _,
7 IconPosition, px,
8};
9
10#[derive(IntoElement)]
11pub struct EnumVariantDropdown<T>
12where
13 T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static,
14{
15 id: ElementId,
16 current_value: T,
17 variants: &'static [T],
18 labels: &'static [&'static str],
19 should_do_title_case: bool,
20 tab_index: Option<isize>,
21 disabled: bool,
22 aria_label: Option<SharedString>,
23 aria_description: Option<SharedString>,
24 on_change: Rc<dyn Fn(T, &mut ui::Window, &mut App) + 'static>,
25}
26
27impl<T> EnumVariantDropdown<T>
28where
29 T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static,
30{
31 pub fn new(
32 id: impl Into<ElementId>,
33 current_value: T,
34 variants: &'static [T],
35 labels: &'static [&'static str],
36 on_change: impl Fn(T, &mut ui::Window, &mut App) + 'static,
37 ) -> Self {
38 Self {
39 id: id.into(),
40 current_value,
41 variants,
42 labels,
43 should_do_title_case: true,
44 tab_index: None,
45 disabled: false,
46 aria_label: None,
47 aria_description: None,
48 on_change: Rc::new(on_change),
49 }
50 }
51
52 pub fn title_case(mut self, title_case: bool) -> Self {
53 self.should_do_title_case = title_case;
54 self
55 }
56
57 pub fn tab_index(mut self, tab_index: isize) -> Self {
58 self.tab_index = Some(tab_index);
59 self
60 }
61
62 pub fn disabled(mut self, disabled: bool) -> Self {
63 self.disabled = disabled;
64 self
65 }
66
67 /// Sets the label announced by assistive technology.
68 /// Defaults to the currently selected value's label.
69 pub fn aria_label(mut self, label: impl Into<SharedString>) -> Self {
70 self.aria_label = Some(label.into());
71 self
72 }
73
74 /// Sets the supplementary description announced by assistive technology
75 /// after the combobox's name, role, and value (e.g. a setting subtitle).
76 pub fn aria_description(mut self, description: impl Into<SharedString>) -> Self {
77 self.aria_description = Some(description.into());
78 self
79 }
80}
81
82impl<T> RenderOnce for EnumVariantDropdown<T>
83where
84 T: strum::VariantArray + strum::VariantNames + Copy + PartialEq + Send + Sync + 'static,
85{
86 fn render(self, window: &mut ui::Window, cx: &mut ui::App) -> impl gpui::IntoElement {
87 let current_value_label = self.labels[self
88 .variants
89 .iter()
90 .position(|v| *v == self.current_value)
91 .unwrap()];
92
93 let context_menu = window.use_keyed_state(current_value_label, cx, |window, cx| {
94 ContextMenu::new(window, cx, move |mut menu, _, _| {
95 for (&value, &label) in std::iter::zip(self.variants, self.labels) {
96 let on_change = self.on_change.clone();
97 let current_value = self.current_value;
98 menu = menu.toggleable_entry(
99 if self.should_do_title_case {
100 label.to_title_case()
101 } else {
102 label.to_string()
103 },
104 value == current_value,
105 IconPosition::End,
106 None,
107 move |window, cx| {
108 on_change(value, window, cx);
109 },
110 );
111 }
112 menu
113 })
114 });
115
116 DropdownMenu::new(
117 self.id,
118 if self.should_do_title_case {
119 current_value_label.to_title_case()
120 } else {
121 current_value_label.to_string()
122 },
123 context_menu,
124 )
125 .when_some(self.aria_label, |this, label| this.aria_label(label))
126 .when_some(self.aria_description, |this, description| {
127 this.aria_description(description)
128 })
129 .disabled(self.disabled)
130 .when_some(self.tab_index, |elem, tab_index| elem.tab_index(tab_index))
131 .trigger_size(ButtonSize::Medium)
132 .style(DropdownStyle::Outlined)
133 .offset(gpui::Point {
134 x: px(0.0),
135 y: px(2.0),
136 })
137 .into_any_element()
138 }
139}
140