Skip to repository content227 lines · 8.0 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T05:54:26.880Z 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
footer.rs
1use std::rc::Rc;
2
3use gpui::{Action, FocusHandle, Focusable};
4use ui::{ContextMenu, Divider, FluentBuilder, KeyBinding, PopoverMenu, Tooltip, prelude::*};
5
6use crate::Picker;
7use crate::PickerDelegate;
8use crate::SetPreviewBelow;
9use crate::SetPreviewRight;
10use crate::ToggleActionsMenu;
11use crate::TogglePreview;
12use crate::preview;
13
14/// Line in the Actions menu on the default footer.
15pub enum PickerAction {
16 Header(SharedString),
17 Separator,
18 Entry {
19 label: SharedString,
20 action: Box<dyn Action>,
21 toggled: Option<bool>,
22 },
23}
24
25impl PickerAction {
26 pub fn button(label: impl Into<SharedString>, action: Box<dyn Action>) -> Self {
27 Self::Entry {
28 label: label.into(),
29 action,
30 toggled: None,
31 }
32 }
33
34 /// A non-clickable section title.
35 pub fn header(label: impl Into<SharedString>) -> Self {
36 Self::Header(label.into())
37 }
38
39 /// A divider between groups of entries.
40 pub fn separator() -> Self {
41 Self::Separator
42 }
43
44 /// Make it possible to turn the previous item on and off
45 pub fn toggled(mut self, toggled: bool) -> Self {
46 if let Self::Entry { toggled: t, .. } = &mut self {
47 *t = Some(toggled);
48 }
49 self
50 }
51
52 pub(crate) fn add_to_menu(&self, menu: ContextMenu, focus_handle: &FocusHandle) -> ContextMenu {
53 match self {
54 Self::Header(label) => menu.header(label),
55 Self::Separator => menu.separator(),
56 Self::Entry {
57 label,
58 action,
59 toggled: Some(toggled),
60 } => {
61 let dispatched = action.boxed_clone();
62 let handler_focus = focus_handle.clone();
63 menu.toggleable_entry(
64 label,
65 *toggled,
66 ui::IconPosition::End,
67 Some(action.boxed_clone()),
68 move |window, cx| {
69 window.focus(&handler_focus, cx);
70 window.dispatch_action(dispatched.boxed_clone(), cx);
71 },
72 )
73 }
74 Self::Entry {
75 label,
76 action,
77 toggled: None,
78 } => menu.action(label, action.boxed_clone()),
79 }
80 }
81}
82
83impl<D: PickerDelegate> Picker<D> {
84 pub(crate) fn render_footer(
85 &self,
86 window: &mut Window,
87 cx: &mut Context<Self>,
88 ) -> Option<AnyElement> {
89 if let Some(footer) = self.delegate.render_footer(window, cx) {
90 return Some(footer);
91 }
92 self.render_default_footer(window, cx)
93 }
94
95 fn render_default_footer(
96 &self,
97 window: &mut Window,
98 cx: &mut Context<Self>,
99 ) -> Option<AnyElement> {
100 let actions = self.delegate.actions_menu(window, cx);
101
102 if self.preview.is_none() && actions.is_empty() {
103 return None;
104 }
105
106 let focus_handle = self.focus_handle(cx);
107
108 Some(
109 h_flex()
110 .w_full()
111 .p_1p5()
112 .justify_between()
113 .border_t_1()
114 .border_color(cx.theme().colors().border_variant)
115 .when(self.preview.is_some(), |this| {
116 this.child(self.render_preview_controls(window, cx))
117 })
118 .when(!actions.is_empty(), |this| {
119 this.child(self.render_actions_button(actions.into(), focus_handle, window, cx))
120 })
121 .into_any_element(),
122 )
123 }
124
125 fn render_preview_controls(
126 &self,
127 window: &mut Window,
128 cx: &mut Context<Self>,
129 ) -> impl IntoElement {
130 let focus_handle = self.focus_handle(cx);
131 let right_focus_handle = focus_handle.clone();
132 let below_focus_handle = focus_handle.clone();
133 let current = self.preview_layout().unwrap_or(preview::Layout::Hidden);
134 let preview_visible = current != preview::Layout::Hidden;
135
136 let diff_split = if self.is_auto_vertical(window) {
137 IconName::DiffSplitAuto
138 } else {
139 IconName::DiffSplit
140 };
141
142 h_flex()
143 .child(
144 Button::new("picker-preview-toggle", "Preview")
145 .when(preview_visible, |this| this.color(Color::Accent))
146 .key_binding(
147 KeyBinding::for_action_in(&TogglePreview, &focus_handle, cx)
148 .size(rems_from_px(12.)),
149 )
150 .on_click(
151 cx.listener(|this, _, window, cx| this.toggle_preview_visible(window, cx)),
152 ),
153 )
154 .when(preview_visible, |this| {
155 this.child(Divider::vertical().mx_1())
156 .child(
157 IconButton::new("picker-preview-below", IconName::DiffUnified)
158 .icon_size(IconSize::Small)
159 .toggle_state(current == preview::Layout::Below)
160 .tooltip(move |_window, cx| {
161 Tooltip::for_action_in(
162 "Preview Below",
163 &SetPreviewBelow,
164 &below_focus_handle,
165 cx,
166 )
167 })
168 .on_click(cx.listener(|this, _, window, cx| {
169 this.set_preview_layout(preview::Layout::Below, window, cx)
170 })),
171 )
172 .child(
173 IconButton::new("picker-preview-right", diff_split)
174 .icon_size(IconSize::Small)
175 .toggle_state(current == preview::Layout::Right)
176 .tooltip(move |_window, cx| {
177 Tooltip::for_action_in(
178 "Preview to the Right",
179 &SetPreviewRight,
180 &right_focus_handle,
181 cx,
182 )
183 })
184 .on_click(cx.listener(|this, _, window, cx| {
185 this.set_preview_layout(preview::Layout::Right, window, cx)
186 })),
187 )
188 })
189 }
190
191 fn render_actions_button(
192 &self,
193 actions: Rc<[crate::footer::PickerAction]>,
194 focus_handle: gpui::FocusHandle,
195 _window: &mut Window,
196 cx: &mut Context<Self>,
197 ) -> impl IntoElement {
198 PopoverMenu::new("picker-actions-menu")
199 .with_handle(self.actions_menu_handle.clone())
200 .trigger(
201 Button::new("picker-actions-trigger", "Actions…")
202 .key_binding(
203 KeyBinding::for_action_in(&ToggleActionsMenu, &focus_handle, cx)
204 .size(rems_from_px(12.)),
205 )
206 .selected_style(ui::ButtonStyle::Tinted(ui::TintColor::Accent)),
207 )
208 .menu(move |window, cx| {
209 let actions = Rc::clone(&actions);
210 let focus_handle = focus_handle.clone();
211 Some(ContextMenu::build(window, cx, move |mut menu, _, _| {
212 menu = menu.context(focus_handle.clone());
213 for item in actions.iter() {
214 menu = item.add_to_menu(menu, &focus_handle);
215 }
216 menu
217 }))
218 })
219 .attach(gpui::Anchor::TopRight)
220 .anchor(gpui::Anchor::BottomRight)
221 .offset(gpui::Point {
222 x: px(0.0),
223 y: px(-2.0),
224 })
225 }
226}
227