mode_indicator.rs

  1use gpui::{
  2    elements::{Empty, Label},
  3    AnyElement, Element, Entity, Subscription, View, ViewContext,
  4};
  5use settings::SettingsStore;
  6use workspace::{item::ItemHandle, StatusItemView};
  7
  8use crate::{state::Mode, Vim, VimEvent, VimModeSetting};
  9
 10pub struct ModeIndicator {
 11    pub mode: Option<Mode>,
 12    _subscription: Subscription,
 13}
 14
 15impl ModeIndicator {
 16    pub fn new(cx: &mut ViewContext<Self>) -> Self {
 17        let handle = cx.handle().downgrade();
 18
 19        let _subscription = cx.subscribe_global::<VimEvent, _>(move |&event, cx| {
 20            if let Some(mode_indicator) = handle.upgrade(cx) {
 21                match event {
 22                    VimEvent::ModeChanged { mode } => {
 23                        mode_indicator.window().update(cx, |cx| {
 24                            mode_indicator.update(cx, move |mode_indicator, cx| {
 25                                mode_indicator.set_mode(mode, cx);
 26                            })
 27                        });
 28                    }
 29                }
 30            }
 31        });
 32
 33        cx.observe_global::<SettingsStore, _>(move |mode_indicator, cx| {
 34            if settings::get::<VimModeSetting>(cx).0 {
 35                mode_indicator.mode = cx
 36                    .has_global::<Vim>()
 37                    .then(|| cx.global::<Vim>().state.mode);
 38            } else {
 39                mode_indicator.mode.take();
 40            }
 41        })
 42        .detach();
 43
 44        // Vim doesn't exist in some tests
 45        let mode = cx
 46            .has_global::<Vim>()
 47            .then(|| {
 48                let vim = cx.global::<Vim>();
 49                vim.enabled.then(|| vim.state.mode)
 50            })
 51            .flatten();
 52
 53        Self {
 54            mode,
 55            _subscription,
 56        }
 57    }
 58
 59    pub fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
 60        if self.mode != Some(mode) {
 61            self.mode = Some(mode);
 62            cx.notify();
 63        }
 64    }
 65}
 66
 67impl Entity for ModeIndicator {
 68    type Event = ();
 69}
 70
 71impl View for ModeIndicator {
 72    fn ui_name() -> &'static str {
 73        "ModeIndicatorView"
 74    }
 75
 76    fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
 77        let Some(mode) = self.mode.as_ref() else {
 78            return Empty::new().into_any();
 79        };
 80
 81        let theme = &theme::current(cx).workspace.status_bar;
 82
 83        // we always choose text to be 12 monospace characters
 84        // so that as the mode indicator changes, the rest of the
 85        // UI stays still.
 86        let text = match mode {
 87            Mode::Normal => "-- NORMAL --",
 88            Mode::Insert => "-- INSERT --",
 89            Mode::Visual { line: false } => "-- VISUAL --",
 90            Mode::Visual { line: true } => "VISUAL LINE ",
 91        };
 92        Label::new(text, theme.vim_mode_indicator.text.clone())
 93            .contained()
 94            .with_style(theme.vim_mode_indicator.container)
 95            .into_any()
 96    }
 97}
 98
 99impl StatusItemView for ModeIndicator {
100    fn set_active_pane_item(
101        &mut self,
102        _active_pane_item: Option<&dyn ItemHandle>,
103        _cx: &mut ViewContext<Self>,
104    ) {
105        // nothing to do.
106    }
107}