search_bar.rs

  1use editor::{Editor, EditorElement, EditorStyle, MultiBufferOffset, ToOffset};
  2use gpui::{Action, App, Entity, FocusHandle, Hsla, IntoElement, TextStyle};
  3use settings::Settings;
  4use theme_settings::ThemeSettings;
  5use ui::{IconButton, IconButtonShape};
  6use ui::{Tooltip, prelude::*};
  7
  8pub(super) enum HistoryNavigationDirection {
  9    Previous,
 10    Next,
 11}
 12
 13pub(super) fn should_navigate_history(
 14    editor: &Entity<Editor>,
 15    direction: HistoryNavigationDirection,
 16    cx: &App,
 17) -> bool {
 18    let editor_ref = editor.read(cx);
 19    let snapshot = editor_ref.buffer().read(cx).snapshot(cx);
 20    if snapshot.max_point().row == 0 {
 21        return true;
 22    }
 23    let selections = editor_ref.selections.disjoint_anchors();
 24    if let [selection] = selections {
 25        let offset = selection.end.to_offset(&snapshot);
 26        match direction {
 27            HistoryNavigationDirection::Previous => offset == MultiBufferOffset(0),
 28            HistoryNavigationDirection::Next => offset == snapshot.len(),
 29        }
 30    } else {
 31        true
 32    }
 33}
 34
 35pub(super) enum ActionButtonState {
 36    Disabled,
 37    Toggled,
 38}
 39
 40pub(super) fn render_action_button(
 41    id_prefix: &'static str,
 42    icon: ui::IconName,
 43    button_state: Option<ActionButtonState>,
 44    tooltip: &'static str,
 45    action: &'static dyn Action,
 46    focus_handle: FocusHandle,
 47) -> impl IntoElement {
 48    IconButton::new(
 49        SharedString::from(format!("{id_prefix}-{}", action.name())),
 50        icon,
 51    )
 52    .shape(IconButtonShape::Square)
 53    .on_click({
 54        let focus_handle = focus_handle.clone();
 55        move |_, window, cx| {
 56            if !focus_handle.is_focused(window) {
 57                window.focus(&focus_handle, cx);
 58            }
 59            window.dispatch_action(action.boxed_clone(), cx);
 60        }
 61    })
 62    .tooltip(move |_window, cx| Tooltip::for_action_in(tooltip, action, &focus_handle, cx))
 63    .when_some(button_state, |this, state| match state {
 64        ActionButtonState::Toggled => this.toggle_state(true),
 65        ActionButtonState::Disabled => this.disabled(true),
 66    })
 67}
 68
 69pub(crate) fn input_base_styles(border_color: Hsla, map: impl FnOnce(Div) -> Div) -> Div {
 70    h_flex()
 71        .map(map)
 72        .min_w_32()
 73        .min_h_8()
 74        .pl_2()
 75        .pr_1()
 76        .border_1()
 77        .border_color(border_color)
 78        .rounded_md()
 79}
 80pub(crate) fn filter_search_results_input(
 81    border_color: Hsla,
 82    map: impl FnOnce(Div) -> Div,
 83    cx: &App,
 84) -> Div {
 85    input_base_styles(border_color, map).pl_0().child(
 86        h_flex()
 87            .mr_2()
 88            .px_2()
 89            .h_full()
 90            .border_r_1()
 91            .border_color(cx.theme().colors().border)
 92            .bg(cx.theme().colors().text_accent.opacity(0.05))
 93            .child(Label::new("Find in Results").color(Color::Muted)),
 94    )
 95}
 96
 97pub(crate) fn render_text_input(
 98    editor: &Entity<Editor>,
 99    color_override: Option<Color>,
100    app: &App,
101) -> impl IntoElement {
102    let (color, use_syntax) = if editor.read(app).read_only(app) {
103        (app.theme().colors().text_disabled, false)
104    } else {
105        match color_override {
106            Some(color_override) => (color_override.color(app), false),
107            None => (app.theme().colors().text, true),
108        }
109    };
110
111    let settings = ThemeSettings::get_global(app);
112    let text_style = TextStyle {
113        color,
114        font_family: settings.buffer_font.family.clone(),
115        font_features: settings.buffer_font.features.clone(),
116        font_fallbacks: settings.buffer_font.fallbacks.clone(),
117        font_size: rems(0.875).into(),
118        font_weight: settings.buffer_font.weight,
119        line_height: relative(1.3),
120        ..TextStyle::default()
121    };
122
123    let mut editor_style = EditorStyle {
124        background: app.theme().colors().toolbar_background,
125        local_player: app.theme().players().local(),
126        text: text_style,
127        ..EditorStyle::default()
128    };
129    if use_syntax {
130        editor_style.syntax = app.theme().syntax().clone();
131    }
132
133    EditorElement::new(editor, editor_style)
134}
135
136/// This element makes all search inputs align as if they were in the same column
137pub(crate) fn alignment_element() -> Div {
138    div().size_5().flex_none().ml_0p5()
139}