model_selector_components.rs

  1use gpui::{Action, FocusHandle, prelude::*};
  2use ui::{ElevationIndex, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*};
  3
  4#[derive(IntoElement)]
  5pub struct ModelSelectorHeader {
  6    title: SharedString,
  7    has_border: bool,
  8}
  9
 10impl ModelSelectorHeader {
 11    pub fn new(title: impl Into<SharedString>, has_border: bool) -> Self {
 12        Self {
 13            title: title.into(),
 14            has_border,
 15        }
 16    }
 17}
 18
 19impl RenderOnce for ModelSelectorHeader {
 20    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
 21        div()
 22            .px_2()
 23            .pb_1()
 24            .when(self.has_border, |this| {
 25                this.mt_1()
 26                    .pt_2()
 27                    .border_t_1()
 28                    .border_color(cx.theme().colors().border_variant)
 29            })
 30            .child(
 31                Label::new(self.title)
 32                    .size(LabelSize::XSmall)
 33                    .color(Color::Muted),
 34            )
 35    }
 36}
 37
 38#[derive(IntoElement)]
 39pub struct ModelSelectorListItem {
 40    index: usize,
 41    title: SharedString,
 42    icon: Option<IconName>,
 43    is_selected: bool,
 44    is_focused: bool,
 45    is_favorite: bool,
 46    on_toggle_favorite: Option<Box<dyn Fn(&App) + 'static>>,
 47}
 48
 49impl ModelSelectorListItem {
 50    pub fn new(index: usize, title: impl Into<SharedString>) -> Self {
 51        Self {
 52            index,
 53            title: title.into(),
 54            icon: None,
 55            is_selected: false,
 56            is_focused: false,
 57            is_favorite: false,
 58            on_toggle_favorite: None,
 59        }
 60    }
 61
 62    pub fn icon(mut self, icon: IconName) -> Self {
 63        self.icon = Some(icon);
 64        self
 65    }
 66
 67    pub fn is_selected(mut self, is_selected: bool) -> Self {
 68        self.is_selected = is_selected;
 69        self
 70    }
 71
 72    pub fn is_focused(mut self, is_focused: bool) -> Self {
 73        self.is_focused = is_focused;
 74        self
 75    }
 76
 77    pub fn is_favorite(mut self, is_favorite: bool) -> Self {
 78        self.is_favorite = is_favorite;
 79        self
 80    }
 81
 82    pub fn on_toggle_favorite(mut self, handler: impl Fn(&App) + 'static) -> Self {
 83        self.on_toggle_favorite = Some(Box::new(handler));
 84        self
 85    }
 86}
 87
 88impl RenderOnce for ModelSelectorListItem {
 89    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
 90        let model_icon_color = if self.is_selected {
 91            Color::Accent
 92        } else {
 93            Color::Muted
 94        };
 95
 96        let is_favorite = self.is_favorite;
 97
 98        ListItem::new(self.index)
 99            .inset(true)
100            .spacing(ListItemSpacing::Sparse)
101            .toggle_state(self.is_focused)
102            .child(
103                h_flex()
104                    .w_full()
105                    .gap_1p5()
106                    .when_some(self.icon, |this, icon| {
107                        this.child(
108                            Icon::new(icon)
109                                .color(model_icon_color)
110                                .size(IconSize::Small),
111                        )
112                    })
113                    .child(Label::new(self.title).truncate()),
114            )
115            .end_slot(div().pr_2().when(self.is_selected, |this| {
116                this.child(
117                    Icon::new(IconName::Check)
118                        .color(Color::Accent)
119                        .size(IconSize::Small),
120                )
121            }))
122            .end_hover_slot(div().pr_2().when_some(self.on_toggle_favorite, {
123                |this, handle_click| {
124                    let (icon, color, tooltip) = if is_favorite {
125                        (IconName::StarFilled, Color::Accent, "Unfavorite Model")
126                    } else {
127                        (IconName::Star, Color::Default, "Favorite Model")
128                    };
129                    this.child(
130                        IconButton::new(("toggle-favorite", self.index), icon)
131                            .layer(ElevationIndex::ElevatedSurface)
132                            .icon_color(color)
133                            .icon_size(IconSize::Small)
134                            .tooltip(Tooltip::text(tooltip))
135                            .on_click(move |_, _, cx| (handle_click)(cx)),
136                    )
137                }
138            }))
139    }
140}
141
142#[derive(IntoElement)]
143pub struct ModelSelectorFooter {
144    action: Box<dyn Action>,
145    focus_handle: FocusHandle,
146}
147
148impl ModelSelectorFooter {
149    pub fn new(action: Box<dyn Action>, focus_handle: FocusHandle) -> Self {
150        Self {
151            action,
152            focus_handle,
153        }
154    }
155}
156
157impl RenderOnce for ModelSelectorFooter {
158    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
159        let action = self.action;
160        let focus_handle = self.focus_handle;
161
162        h_flex()
163            .w_full()
164            .p_1p5()
165            .border_t_1()
166            .border_color(cx.theme().colors().border_variant)
167            .child(
168                Button::new("configure", "Configure")
169                    .full_width()
170                    .style(ButtonStyle::Outlined)
171                    .key_binding(
172                        KeyBinding::for_action_in(action.as_ref(), &focus_handle, cx)
173                            .map(|kb| kb.size(rems_from_px(12.))),
174                    )
175                    .on_click(move |_, window, cx| {
176                        window.dispatch_action(action.boxed_clone(), cx);
177                    }),
178            )
179    }
180}