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        let text = match mode {
 84            Mode::Normal => "-- NORMAL --",
 85            Mode::Insert => "-- INSERT --",
 86            Mode::Visual => "-- VISUAL --",
 87            Mode::VisualLine => "-- VISUAL LINE --",
 88            Mode::VisualBlock => "-- VISUAL BLOCK --",
 89        };
 90        Label::new(text, theme.vim_mode_indicator.text.clone())
 91            .contained()
 92            .with_style(theme.vim_mode_indicator.container)
 93            .into_any()
 94    }
 95}
 96
 97impl StatusItemView for ModeIndicator {
 98    fn set_active_pane_item(
 99        &mut self,
100        _active_pane_item: Option<&dyn ItemHandle>,
101        _cx: &mut ViewContext<Self>,
102    ) {
103        // nothing to do.
104    }
105}