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}