Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:47:53.998Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

footer.rs

227 lines · 8.0 KB · rust
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
Served at tenant.openagents/omega Member data and write actions are omitted.