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 cx.update_window(mode_indicator.window_id(), |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(|| cx.global::<Vim>().state.mode);
48
49 Self {
50 mode,
51 _subscription,
52 }
53 }
54
55 pub fn set_mode(&mut self, mode: Mode, cx: &mut ViewContext<Self>) {
56 if self.mode != Some(mode) {
57 self.mode = Some(mode);
58 cx.notify();
59 }
60 }
61}
62
63impl Entity for ModeIndicator {
64 type Event = ();
65}
66
67impl View for ModeIndicator {
68 fn ui_name() -> &'static str {
69 "ModeIndicatorView"
70 }
71
72 fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
73 let Some(mode) = self.mode.as_ref() else {
74 return Empty::new().into_any();
75 };
76
77 let theme = &theme::current(cx).workspace.status_bar;
78
79 // we always choose text to be 12 monospace characters
80 // so that as the mode indicator changes, the rest of the
81 // UI stays still.
82 let text = match mode {
83 Mode::Normal => "-- NORMAL --",
84 Mode::Insert => "-- INSERT --",
85 Mode::Visual { line: false } => "-- VISUAL --",
86 Mode::Visual { line: true } => "VISUAL LINE ",
87 };
88 Label::new(text, theme.vim_mode_indicator.text.clone())
89 .contained()
90 .with_style(theme.vim_mode_indicator.container)
91 .into_any()
92 }
93}
94
95impl StatusItemView for ModeIndicator {
96 fn set_active_pane_item(
97 &mut self,
98 _active_pane_item: Option<&dyn ItemHandle>,
99 _cx: &mut ViewContext<Self>,
100 ) {
101 // nothing to do.
102 }
103}