model_selector_components.rs

  1use gpui::{Action, FocusHandle, prelude::*};
  2use ui::{KeyBinding, ListItem, ListItemSpacing, 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}
 46
 47impl ModelSelectorListItem {
 48    pub fn new(index: usize, title: impl Into<SharedString>) -> Self {
 49        Self {
 50            index,
 51            title: title.into(),
 52            icon: None,
 53            is_selected: false,
 54            is_focused: false,
 55        }
 56    }
 57
 58    pub fn icon(mut self, icon: IconName) -> Self {
 59        self.icon = Some(icon);
 60        self
 61    }
 62
 63    pub fn is_selected(mut self, is_selected: bool) -> Self {
 64        self.is_selected = is_selected;
 65        self
 66    }
 67
 68    pub fn is_focused(mut self, is_focused: bool) -> Self {
 69        self.is_focused = is_focused;
 70        self
 71    }
 72}
 73
 74impl RenderOnce for ModelSelectorListItem {
 75    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
 76        let model_icon_color = if self.is_selected {
 77            Color::Accent
 78        } else {
 79            Color::Muted
 80        };
 81
 82        ListItem::new(self.index)
 83            .inset(true)
 84            .spacing(ListItemSpacing::Sparse)
 85            .toggle_state(self.is_focused)
 86            .child(
 87                h_flex()
 88                    .w_full()
 89                    .gap_1p5()
 90                    .when_some(self.icon, |this, icon| {
 91                        this.child(
 92                            Icon::new(icon)
 93                                .color(model_icon_color)
 94                                .size(IconSize::Small),
 95                        )
 96                    })
 97                    .child(Label::new(self.title).truncate()),
 98            )
 99            .end_slot(div().pr_2().when(self.is_selected, |this| {
100                this.child(
101                    Icon::new(IconName::Check)
102                        .color(Color::Accent)
103                        .size(IconSize::Small),
104                )
105            }))
106    }
107}
108
109#[derive(IntoElement)]
110pub struct ModelSelectorFooter {
111    action: Box<dyn Action>,
112    focus_handle: FocusHandle,
113}
114
115impl ModelSelectorFooter {
116    pub fn new(action: Box<dyn Action>, focus_handle: FocusHandle) -> Self {
117        Self {
118            action,
119            focus_handle,
120        }
121    }
122}
123
124impl RenderOnce for ModelSelectorFooter {
125    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
126        let action = self.action;
127        let focus_handle = self.focus_handle;
128
129        h_flex()
130            .w_full()
131            .p_1p5()
132            .border_t_1()
133            .border_color(cx.theme().colors().border_variant)
134            .child(
135                Button::new("configure", "Configure")
136                    .full_width()
137                    .style(ButtonStyle::Outlined)
138                    .key_binding(
139                        KeyBinding::for_action_in(action.as_ref(), &focus_handle, cx)
140                            .map(|kb| kb.size(rems_from_px(12.))),
141                    )
142                    .on_click(move |_, window, cx| {
143                        window.dispatch_action(action.boxed_clone(), cx);
144                    }),
145            )
146    }
147}