nvim_gtk/
shell.rs

1use std::cell::{Cell, RefCell};
2use std::collections::HashMap;
3use std::ops::Deref;
4use std::rc::Rc;
5use std::sync::{Arc, Condvar, Mutex};
6use std::thread;
7use std::time::Duration;
8
9use clap::{self, value_t};
10
11use cairo;
12use gdk;
13use gdk::{EventButton, EventMotion, EventScroll, EventType, ModifierType, WindowExt};
14use glib;
15use gtk;
16use gtk::prelude::*;
17use pango;
18use pango::FontDescription;
19use pangocairo;
20
21use neovim_lib::neovim_api::Tabpage;
22use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
23
24use crate::color::{Color, COLOR_BLACK, COLOR_WHITE};
25use crate::grid::GridMap;
26use crate::highlight::HighlightMap;
27use crate::misc::{decode_uri, escape_filename, split_at_comma};
28use crate::nvim::{
29    self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef, NvimHandler,
30    RepaintMode,
31};
32use crate::settings::{FontSource, Settings};
33use crate::ui_model::ModelRect;
34
35use crate::cmd_line::{CmdLine, CmdLineContext};
36use crate::cursor::{BlinkCursor, Cursor, CursorRedrawCb};
37use crate::error;
38use crate::input;
39use crate::input::keyval_to_input_string;
40use crate::mode;
41use crate::popup_menu::{self, PopupMenu};
42use crate::render;
43use crate::render::CellMetrics;
44use crate::subscriptions::{SubscriptionHandle, SubscriptionKey, Subscriptions};
45use crate::tabline::Tabline;
46use crate::ui::UiMutex;
47
48const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12";
49pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.3.2";
50
51macro_rules! idle_cb_call {
52    ($state:ident.$cb:ident($( $x:expr ),*)) => (
53            glib::idle_add(move || {
54                               if let Some(ref cb) = $state.borrow().$cb {
55                                   (&mut *cb.borrow_mut())($($x),*);
56                               }
57
58                               glib::Continue(false)
59                           });
60    )
61}
62
63pub struct RenderState {
64    pub font_ctx: render::Context,
65    pub hl: HighlightMap,
66    pub mode: mode::Mode,
67}
68
69impl RenderState {
70    pub fn new(pango_context: pango::Context) -> Self {
71        RenderState {
72            font_ctx: render::Context::new(pango_context),
73            hl: HighlightMap::new(),
74            mode: mode::Mode::new(),
75        }
76    }
77}
78
79pub struct TransparencySettigns {
80    background_alpha: f64,
81    filled_alpha: f64,
82    enabled: bool,
83}
84
85impl TransparencySettigns {
86    pub fn new() -> Self {
87        TransparencySettigns {
88            background_alpha: 1.0,
89            filled_alpha: 1.0,
90            enabled: false,
91        }
92    }
93
94    fn filled_alpha(&self) -> Option<f64> {
95        if self.enabled {
96            Some(self.filled_alpha)
97        } else {
98            None
99        }
100    }
101
102    fn background_alpha(&self) -> Option<f64> {
103        if self.enabled {
104            Some(self.background_alpha)
105        } else {
106            None
107        }
108    }
109}
110
111pub struct State {
112    pub grids: GridMap,
113
114    mouse_enabled: bool,
115    nvim: Rc<NeovimClient>,
116    cursor: Option<BlinkCursor<State>>,
117    popup_menu: PopupMenu,
118    cmd_line: CmdLine,
119    settings: Rc<RefCell<Settings>>,
120    render_state: Rc<RefCell<RenderState>>,
121
122    resize_request: (i64, i64),
123    resize_timer: Rc<Cell<Option<glib::SourceId>>>,
124
125    pub clipboard_clipboard: gtk::Clipboard,
126    pub clipboard_primary: gtk::Clipboard,
127
128    stack: gtk::Stack,
129    pub drawing_area: gtk::DrawingArea,
130    tabs: Tabline,
131    im_context: gtk::IMMulticontext,
132    error_area: error::ErrorArea,
133
134    options: ShellOptions,
135    transparency_settings: TransparencySettigns,
136
137    detach_cb: Option<Box<RefCell<dyn FnMut() + Send + 'static>>>,
138    nvim_started_cb: Option<Box<RefCell<dyn FnMut() + Send + 'static>>>,
139    command_cb: Option<Box<dyn FnMut(&mut State, nvim::NvimCommand) + Send + 'static>>,
140
141    subscriptions: RefCell<Subscriptions>,
142}
143
144impl State {
145    pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> State {
146        let drawing_area = gtk::DrawingArea::new();
147
148        let pango_context = drawing_area.create_pango_context().unwrap();
149        pango_context.set_font_description(&FontDescription::from_string(DEFAULT_FONT_NAME));
150
151        let mut render_state = RenderState::new(pango_context);
152        render_state.hl.set_use_cterm(options.cterm_colors);
153        let render_state = Rc::new(RefCell::new(render_state));
154
155        let popup_menu = PopupMenu::new(&drawing_area);
156        let cmd_line = CmdLine::new(&drawing_area, render_state.clone());
157
158        State {
159            grids: GridMap::new(),
160            nvim: Rc::new(NeovimClient::new()),
161            mouse_enabled: true,
162            cursor: None,
163            popup_menu,
164            cmd_line,
165            settings,
166            render_state,
167
168            resize_request: (-1, -1),
169            resize_timer: Rc::new(Cell::new(None)),
170
171            clipboard_clipboard: gtk::Clipboard::get(&gdk::Atom::intern("CLIPBOARD")),
172            clipboard_primary: gtk::Clipboard::get(&gdk::Atom::intern("PRIMARY")),
173
174            // UI
175            stack: gtk::Stack::new(),
176            drawing_area,
177            tabs: Tabline::new(),
178            im_context: gtk::IMMulticontext::new(),
179            error_area: error::ErrorArea::new(),
180
181            options,
182            transparency_settings: TransparencySettigns::new(),
183
184            detach_cb: None,
185            nvim_started_cb: None,
186            command_cb: None,
187
188            subscriptions: RefCell::new(Subscriptions::new()),
189        }
190    }
191
192    /// Return NeovimRef only if vim in non blocking state
193    ///
194    /// Note that this call also do neovim api call get_mode
195    #[allow(dead_code)]
196    pub fn nvim_non_blocked(&self) -> Option<NeovimRef> {
197        self.nvim().and_then(NeovimRef::non_blocked)
198    }
199
200    pub fn nvim(&self) -> Option<NeovimRef> {
201        self.nvim.nvim()
202    }
203
204    pub fn try_nvim(&self) -> Option<NeovimRef> {
205        self.nvim.try_nvim()
206    }
207
208    pub fn nvim_clone(&self) -> Rc<NeovimClient> {
209        self.nvim.clone()
210    }
211
212    pub fn start_nvim_initialization(&self) -> bool {
213        if self.nvim.is_uninitialized() {
214            self.nvim.set_in_progress();
215            true
216        } else {
217            false
218        }
219    }
220
221    pub fn set_detach_cb<F>(&mut self, cb: Option<F>)
222    where
223        F: FnMut() + Send + 'static,
224    {
225        if let Some(c) = cb {
226            self.detach_cb = Some(Box::new(RefCell::new(c)));
227        } else {
228            self.detach_cb = None;
229        }
230    }
231
232    pub fn set_nvim_started_cb<F>(&mut self, cb: Option<F>)
233    where
234        F: FnMut() + Send + 'static,
235    {
236        if let Some(c) = cb {
237            self.nvim_started_cb = Some(Box::new(RefCell::new(c)));
238        } else {
239            self.nvim_started_cb = None;
240        }
241    }
242
243    pub fn set_nvim_command_cb<F>(&mut self, cb: Option<F>)
244    where
245        F: FnMut(&mut State, nvim::NvimCommand) + Send + 'static,
246    {
247        if let Some(c) = cb {
248            self.command_cb = Some(Box::new(c));
249        } else {
250            self.command_cb = None;
251        }
252    }
253
254    pub fn set_font_desc(&mut self, desc: &str) {
255        let font_description = FontDescription::from_string(desc);
256
257        if font_description.get_size() <= 0 {
258            error!("Font size must be > 0");
259            return;
260        }
261
262        let pango_context = self.drawing_area.create_pango_context().unwrap();
263        pango_context.set_font_description(&font_description);
264
265        self.render_state
266            .borrow_mut()
267            .font_ctx
268            .update(pango_context);
269        self.grids.clear_glyphs();
270        self.try_nvim_resize();
271        self.on_redraw(&RepaintMode::All);
272    }
273
274    pub fn set_font_features(&mut self, font_features: String) {
275        let font_features = render::FontFeatures::from(font_features);
276
277        self.render_state
278            .borrow_mut()
279            .font_ctx
280            .update_font_features(font_features);
281        self.grids.clear_glyphs();
282        self.on_redraw(&RepaintMode::All);
283    }
284
285    pub fn set_line_space(&mut self, line_space: String) {
286        let line_space: i32 = match line_space.parse() {
287            Ok(line_space) => line_space,
288            Err(e) => {
289                error!("Can't convert argument to integer: {}", e.to_string());
290                return;
291            }
292        };
293
294        self.render_state
295            .borrow_mut()
296            .font_ctx
297            .update_line_space(line_space);
298        self.grids.clear_glyphs();
299        self.try_nvim_resize();
300        self.on_redraw(&RepaintMode::All);
301    }
302
303    /// return true if transparency enabled
304    pub fn set_transparency(&mut self, background_alpha: f64, filled_alpha: f64) -> bool {
305        if background_alpha < 1.0 || filled_alpha < 1.0 {
306            self.transparency_settings.background_alpha = background_alpha;
307            self.transparency_settings.filled_alpha = filled_alpha;
308            self.transparency_settings.enabled = true;
309        } else {
310            self.transparency_settings.background_alpha = 1.0;
311            self.transparency_settings.filled_alpha = 1.0;
312            self.transparency_settings.enabled = false;
313        }
314
315        self.on_redraw(&RepaintMode::All);
316
317        self.transparency_settings.enabled
318    }
319
320    pub fn set_cursor_blink(&mut self, val: i32) {
321        if let Some(cursor) = &mut self.cursor {
322            cursor.set_cursor_blink(val);
323        }
324    }
325
326    pub fn open_file(&self, path: &str) {
327        if let Some(mut nvim) = self.nvim() {
328            nvim.command_async(&format!("e {}", path))
329                .cb(|r| r.report_err())
330                .call();
331        }
332    }
333
334    pub fn cd(&self, path: &str) {
335        if let Some(mut nvim) = self.nvim() {
336            nvim.command_async(&format!("cd {}", path))
337                .cb(|r| r.report_err())
338                .call();
339        }
340    }
341
342    pub fn clipboard_clipboard_set(&self, text: &str) {
343        self.clipboard_clipboard.set_text(text);
344    }
345
346    pub fn clipboard_primary_set(&self, text: &str) {
347        self.clipboard_primary.set_text(text);
348    }
349
350    fn close_popup_menu(&self) {
351        if self.popup_menu.is_open() {
352            if let Some(mut nvim) = self.nvim() {
353                nvim.input("<Esc>").report_err();
354            }
355        }
356    }
357
358    fn queue_draw_area<M: AsRef<ModelRect>>(&mut self, rect_list: &[M]) {
359        // extends by items before, then after changes
360
361        let rects: Vec<_> = rect_list
362            .iter()
363            .map(|rect| rect.as_ref().clone())
364            .map(|mut rect| {
365                rect.extend_by_items(self.grids.current_model());
366                rect
367            })
368            .collect();
369
370        self.update_dirty_glyphs();
371
372        let render_state = self.render_state.borrow();
373        let cell_metrics = render_state.font_ctx.cell_metrics();
374
375        for mut rect in rects {
376            rect.extend_by_items(self.grids.current_model());
377
378            let (x, y, width, height) =
379                rect.to_area_extend_ink(self.grids.current_model(), cell_metrics);
380            self.drawing_area.queue_draw_area(x, y, width, height);
381        }
382    }
383
384    fn update_dirty_glyphs(&mut self) {
385        let render_state = self.render_state.borrow();
386        if let Some(model) = self.grids.current_model_mut() {
387            render::shape_dirty(&render_state.font_ctx, model, &render_state.hl);
388        }
389    }
390
391    fn im_commit(&self, ch: &str) {
392        if let Some(mut nvim) = self.nvim() {
393            input::im_input(&mut nvim, ch);
394        }
395    }
396
397    fn calc_nvim_size(&self) -> (usize, usize) {
398        let &CellMetrics {
399            line_height,
400            char_width,
401            ..
402        } = self.render_state.borrow().font_ctx.cell_metrics();
403        let alloc = self.drawing_area.get_allocation();
404        (
405            (alloc.width as f64 / char_width).trunc() as usize,
406            (alloc.height as f64 / line_height).trunc() as usize,
407        )
408    }
409
410    fn show_error_area(&self) {
411        let stack = self.stack.clone();
412        gtk::idle_add(move || {
413            stack.set_visible_child_name("Error");
414            Continue(false)
415        });
416    }
417
418    fn set_im_location(&self) {
419        if let Some((row, col)) = self.grids.current().map(|g| g.get_cursor()) {
420            let (x, y, width, height) = ModelRect::point(col, row)
421                .to_area(self.render_state.borrow().font_ctx.cell_metrics());
422
423            self.im_context.set_cursor_location(&gdk::Rectangle {
424                x,
425                y,
426                width,
427                height,
428            });
429
430            self.im_context.reset();
431        }
432    }
433
434    fn try_nvim_resize(&mut self) {
435        let (columns, rows) = self.calc_nvim_size();
436
437        let (requested_rows, requested_cols) = self.resize_request;
438
439        if requested_rows == rows as i64 && requested_cols == columns as i64 {
440            return;
441        }
442
443        let resize_timer = self.resize_timer.take();
444        if let Some(resize_timer) = resize_timer {
445            glib::source_remove(resize_timer);
446        }
447
448        self.resize_request = (rows as i64, columns as i64);
449
450        let nvim = self.nvim.clone();
451        let resize_timer = self.resize_timer.clone();
452
453        let resize_id = gtk::timeout_add(200, move || {
454            if let Some(mut nvim) = nvim.try_nvim() {
455                debug!("ui_try_resize {}/{}", columns, rows);
456                resize_timer.set(None);
457
458                nvim.ui_try_resize_async(columns as i64, rows as i64)
459                    .cb(|r| r.report_err())
460                    .call();
461
462                return Continue(false);
463            }
464
465            Continue(true)
466        });
467
468        self.resize_timer.set(Some(resize_id));
469    }
470
471    fn edit_paste(&self, clipboard: &str) {
472        let nvim = self.nvim();
473        if let Some(mut nvim) = nvim {
474            let render_state = self.render_state.borrow();
475            if render_state.mode.is(&mode::NvimMode::Insert)
476                || render_state.mode.is(&mode::NvimMode::Normal)
477            {
478                let paste_code = format!("normal! \"{}P", clipboard);
479                nvim.command_async(&paste_code)
480                    .cb(|r| r.report_err())
481                    .call();
482            } else {
483                let paste_code = format!("<C-r>{}", clipboard);
484                nvim.input_async(&paste_code).cb(|r| r.report_err()).call();
485            };
486        }
487    }
488
489    fn edit_copy(&self, clipboard: &str) {
490        let nvim = self.nvim();
491        if let Some(mut nvim) = nvim {
492            let paste_code = format!("normal! \"{}y", clipboard);
493            nvim.command_async(&paste_code)
494                .cb(|r| r.report_err())
495                .call();
496        }
497    }
498
499    fn max_popup_width(&self) -> i32 {
500        self.drawing_area.get_allocated_width() - 20
501    }
502
503    pub fn subscribe<F>(&self, key: SubscriptionKey, args: &[&str], cb: F) -> SubscriptionHandle
504    where
505        F: Fn(Vec<String>) + 'static,
506    {
507        self.subscriptions.borrow_mut().subscribe(key, args, cb)
508    }
509
510    pub fn set_autocmds(&self) {
511        self.subscriptions
512            .borrow()
513            .set_autocmds(&mut self.nvim().unwrap());
514    }
515
516    pub fn notify(&self, params: Vec<Value>) -> Result<(), String> {
517        self.subscriptions.borrow().notify(params)
518    }
519
520    pub fn run_now(&self, handle: &SubscriptionHandle) {
521        self.subscriptions
522            .borrow()
523            .run_now(handle, &mut self.nvim().unwrap());
524    }
525
526    pub fn set_font(&mut self, font_desc: String) {
527        self.set_font_rpc(&font_desc);
528    }
529
530    pub fn set_font_rpc(&mut self, font_desc: &str) {
531        {
532            let mut settings = self.settings.borrow_mut();
533            settings.set_font_source(FontSource::Rpc);
534        }
535
536        self.set_font_desc(font_desc);
537    }
538
539    pub fn on_command(&mut self, command: nvim::NvimCommand) {
540        let mut cb = self.command_cb.take();
541
542        if let Some(ref mut cb) = cb {
543            cb(self, command);
544        }
545
546        self.command_cb = cb;
547    }
548}
549
550#[derive(PartialEq)]
551enum MouseCursor {
552    None,
553    Text,
554    Default,
555}
556
557pub struct UiState {
558    mouse_pressed: bool,
559    scroll_delta: (f64, f64),
560
561    // previous editor position (col, row)
562    prev_pos: (u64, u64),
563
564    mouse_cursor: MouseCursor,
565}
566
567impl UiState {
568    pub fn new() -> UiState {
569        UiState {
570            mouse_pressed: false,
571            scroll_delta: (0.0, 0.0),
572            prev_pos: (0, 0),
573
574            mouse_cursor: MouseCursor::None,
575        }
576    }
577
578    fn apply_mouse_cursor(&mut self, cursor: MouseCursor, window: Option<gdk::Window>) {
579        if self.mouse_cursor == cursor {
580            return;
581        }
582
583        self.mouse_cursor = cursor;
584
585        if let Some(window) = window {
586            let cursor = match self.mouse_cursor {
587                MouseCursor::Default => "default",
588                MouseCursor::None => "none",
589                MouseCursor::Text => "text",
590            };
591
592            window.set_cursor(gdk::Cursor::new_from_name(&window.get_display(), cursor).as_ref());
593        }
594    }
595}
596
597#[derive(Clone)]
598pub struct ShellOptions {
599    nvim_bin_path: Option<String>,
600    timeout: Option<Duration>,
601    args_for_neovim: Vec<String>,
602    input_data: Option<String>,
603    cterm_colors: bool,
604}
605
606impl ShellOptions {
607    pub fn new(matches: &clap::ArgMatches, input_data: Option<String>) -> Self {
608        ShellOptions {
609            input_data,
610            cterm_colors: matches.is_present("cterm-colors"),
611            nvim_bin_path: matches.value_of("nvim-bin-path").map(str::to_owned),
612            timeout: value_t!(matches.value_of("timeout"), u64)
613                .map(Duration::from_secs)
614                .ok(),
615            args_for_neovim: matches
616                .values_of("nvim-args")
617                .map(|args| args.map(str::to_owned).collect())
618                .unwrap_or_else(|| vec![]),
619        }
620    }
621
622    // remove input data from original
623    // shell option, as it need to be used only once
624    pub fn take(&mut self) -> Self {
625        let input_data = self.input_data.take();
626        let mut clone = self.clone();
627        clone.input_data = input_data;
628
629        clone
630    }
631}
632
633pub struct Shell {
634    pub state: Arc<UiMutex<State>>,
635    ui_state: Rc<RefCell<UiState>>,
636
637    widget: gtk::Box,
638}
639
640impl Shell {
641    pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> Shell {
642        let shell = Shell {
643            state: Arc::new(UiMutex::new(State::new(settings, options))),
644            ui_state: Rc::new(RefCell::new(UiState::new())),
645
646            widget: gtk::Box::new(gtk::Orientation::Vertical, 0),
647        };
648
649        let shell_ref = Arc::downgrade(&shell.state);
650        shell.state.borrow_mut().cursor = Some(BlinkCursor::new(shell_ref));
651
652        shell
653    }
654
655    pub fn is_nvim_initialized(&self) -> bool {
656        let state = self.state.borrow();
657        state.nvim.is_initialized()
658    }
659
660    pub fn init(&mut self) {
661        let state = self.state.borrow();
662
663        state.drawing_area.set_hexpand(true);
664        state.drawing_area.set_vexpand(true);
665        state.drawing_area.set_can_focus(true);
666
667        state.im_context.set_use_preedit(false);
668
669        let nvim_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
670
671        nvim_box.pack_start(&*state.tabs, false, true, 0);
672        nvim_box.pack_start(&state.drawing_area, true, true, 0);
673
674        state.stack.add_named(&nvim_box, "Nvim");
675        state.stack.add_named(&*state.error_area, "Error");
676
677        self.widget.pack_start(&state.stack, true, true, 0);
678
679        state.drawing_area.add_events(
680            gdk::EventMask::BUTTON_RELEASE_MASK
681                | gdk::EventMask::BUTTON_PRESS_MASK
682                | gdk::EventMask::BUTTON_MOTION_MASK
683                | gdk::EventMask::SCROLL_MASK
684                | gdk::EventMask::SMOOTH_SCROLL_MASK
685                | gdk::EventMask::ENTER_NOTIFY_MASK
686                | gdk::EventMask::LEAVE_NOTIFY_MASK
687                | gdk::EventMask::POINTER_MOTION_MASK,
688        );
689
690        let menu = self.create_context_menu();
691        let ref_state = self.state.clone();
692        let ref_ui_state = self.ui_state.clone();
693        state.drawing_area.connect_button_press_event(move |_, ev| {
694            gtk_button_press(&mut *ref_state.borrow_mut(), &ref_ui_state, ev, &menu)
695        });
696
697        let ref_state = self.state.clone();
698        let ref_ui_state = self.ui_state.clone();
699        state
700            .drawing_area
701            .connect_button_release_event(move |_, ev| {
702                gtk_button_release(
703                    &mut *ref_state.borrow_mut(),
704                    &mut *ref_ui_state.borrow_mut(),
705                    ev,
706                )
707            });
708
709        let ref_state = self.state.clone();
710        let ref_ui_state = self.ui_state.clone();
711        state
712            .drawing_area
713            .connect_motion_notify_event(move |_, ev| {
714                gtk_motion_notify(
715                    &mut *ref_state.borrow_mut(),
716                    &mut *ref_ui_state.borrow_mut(),
717                    ev,
718                )
719            });
720
721        let ref_state = self.state.clone();
722        state
723            .drawing_area
724            .connect_draw(move |_, ctx| gtk_draw(&ref_state, ctx));
725
726        let ref_ui_state = self.ui_state.clone();
727        let ref_state = self.state.clone();
728        state.drawing_area.connect_key_press_event(move |_, ev| {
729            ref_state
730                .borrow_mut()
731                .cursor
732                .as_mut()
733                .unwrap()
734                .reset_state();
735
736            if ref_state.borrow().im_context.filter_keypress(ev) {
737                Inhibit(true)
738            } else {
739                let state = ref_state.borrow();
740                let nvim = state.try_nvim();
741                if let Some(mut nvim) = nvim {
742                    input::gtk_key_press(&mut nvim, ev)
743                } else {
744                    Inhibit(false)
745                }
746            }
747        });
748        let ref_state = self.state.clone();
749        state.drawing_area.connect_key_release_event(move |da, ev| {
750            ref_state.borrow().im_context.filter_keypress(ev);
751            ref_ui_state
752                .borrow_mut()
753                .apply_mouse_cursor(MouseCursor::None, da.get_window());
754            Inhibit(false)
755        });
756
757        let ref_state = self.state.clone();
758        let ref_ui_state = self.ui_state.clone();
759        state.drawing_area.connect_scroll_event(move |_, ev| {
760            gtk_scroll_event(
761                &mut *ref_state.borrow_mut(),
762                &mut *ref_ui_state.borrow_mut(),
763                ev,
764            )
765        });
766
767        let ref_state = self.state.clone();
768        state
769            .drawing_area
770            .connect_focus_in_event(move |_, _| gtk_focus_in(&mut *ref_state.borrow_mut()));
771
772        let ref_state = self.state.clone();
773        state
774            .drawing_area
775            .connect_focus_out_event(move |_, _| gtk_focus_out(&mut *ref_state.borrow_mut()));
776
777        let ref_state = self.state.clone();
778        state.drawing_area.connect_realize(move |w| {
779            // sometime set_client_window does not work without idle_add
780            // and looks like not enabled im_context
781            gtk::idle_add(clone!(ref_state, w => move || {
782                ref_state.borrow().im_context.set_client_window(
783                    w.get_window().as_ref(),
784                );
785                Continue(false)
786            }));
787        });
788
789        let ref_state = self.state.clone();
790        state
791            .im_context
792            .connect_commit(move |_, ch| ref_state.borrow().im_commit(ch));
793
794        let ref_state = self.state.clone();
795        state.drawing_area.connect_configure_event(move |_, ev| {
796            debug!("configure_event {:?}", ev.get_size());
797
798            let mut state = ref_state.borrow_mut();
799            state.try_nvim_resize();
800
801            false
802        });
803
804        let ref_state = self.state.clone();
805        state.drawing_area.connect_size_allocate(move |_, _| {
806            init_nvim(&ref_state);
807        });
808
809        let ref_state = self.state.clone();
810        let targets = vec![gtk::TargetEntry::new(
811            "text/uri-list",
812            gtk::TargetFlags::OTHER_APP,
813            0,
814        )];
815        state
816            .drawing_area
817            .drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY);
818        state
819            .drawing_area
820            .connect_drag_data_received(move |_, _, _, _, s, _, _| {
821                let uris = s.get_uris();
822                let command = uris.iter().filter_map(|uri| decode_uri(uri)).fold(
823                    ":ar".to_owned(),
824                    |command, filename| {
825                        let filename = escape_filename(&filename);
826                        command + " " + &filename
827                    },
828                );
829                let state = ref_state.borrow_mut();
830                let mut nvim = state.nvim().unwrap();
831                nvim.command_async(&command).cb(|r| r.report_err()).call()
832            });
833
834        let ui_state_ref = self.ui_state.clone();
835        state.drawing_area.connect_enter_notify_event(move |_, ev| {
836            ui_state_ref
837                .borrow_mut()
838                .apply_mouse_cursor(MouseCursor::Text, ev.get_window());
839            gtk::Inhibit(false)
840        });
841
842        let ui_state_ref = self.ui_state.clone();
843        state.drawing_area.connect_leave_notify_event(move |_, ev| {
844            ui_state_ref
845                .borrow_mut()
846                .apply_mouse_cursor(MouseCursor::Default, ev.get_window());
847            gtk::Inhibit(false)
848        });
849    }
850
851    fn create_context_menu(&self) -> gtk::Menu {
852        let menu = gtk::Menu::new();
853        let copy = gtk::MenuItem::new_with_label("Copy");
854        let ref_state = self.state.clone();
855        copy.connect_activate(move |_| ref_state.borrow().edit_copy("+"));
856        copy.show_all();
857
858        let paste = gtk::MenuItem::new_with_label("Paste");
859        let ref_state = self.state.clone();
860        paste.connect_activate(move |_| ref_state.borrow().edit_paste("+"));
861        paste.show_all();
862
863        menu.append(&copy);
864        menu.append(&paste);
865
866        menu
867    }
868
869    #[cfg(unix)]
870    pub fn set_font_desc(&self, font_name: &str) {
871        self.state.borrow_mut().set_font_desc(font_name);
872    }
873
874    pub fn grab_focus(&self) {
875        self.state.borrow().drawing_area.grab_focus();
876    }
877
878    pub fn open_file(&self, path: &str) {
879        self.state.borrow().open_file(path);
880    }
881
882    pub fn cd(&self, path: &str) {
883        self.state.borrow().cd(path);
884    }
885
886    pub fn detach_ui(&mut self) {
887        let state = self.state.borrow();
888
889        let nvim = state.nvim();
890        if let Some(mut nvim) = nvim {
891            nvim.ui_detach().expect("Error in ui_detach");
892        }
893    }
894
895    pub fn edit_paste(&self) {
896        self.state.borrow().edit_paste("+");
897    }
898
899    pub fn edit_save_all(&self) {
900        let state = self.state.borrow();
901
902        let nvim = state.nvim();
903        if let Some(mut nvim) = nvim {
904            nvim.command_async(":wa").cb(|r| r.report_err()).call();
905        }
906    }
907
908    pub fn new_tab(&self) {
909        let state = self.state.borrow();
910
911        let nvim = state.nvim();
912        if let Some(mut nvim) = nvim {
913            nvim.command_async(":tabe").cb(|r| r.report_err()).call();
914        }
915    }
916
917    pub fn set_detach_cb<F>(&self, cb: Option<F>)
918    where
919        F: FnMut() + Send + 'static,
920    {
921        let mut state = self.state.borrow_mut();
922        state.set_detach_cb(cb);
923    }
924
925    pub fn set_nvim_started_cb<F>(&self, cb: Option<F>)
926    where
927        F: FnMut() + Send + 'static,
928    {
929        let mut state = self.state.borrow_mut();
930        state.set_nvim_started_cb(cb);
931    }
932
933    pub fn set_nvim_command_cb<F>(&self, cb: Option<F>)
934    where
935        F: FnMut(&mut State, nvim::NvimCommand) + Send + 'static,
936    {
937        let mut state = self.state.borrow_mut();
938        state.set_nvim_command_cb(cb);
939    }
940
941    pub fn set_completeopts(&self, options: &str) {
942        self.state
943            .borrow()
944            .popup_menu
945            .set_preview(options.contains("preview"));
946    }
947}
948
949impl Deref for Shell {
950    type Target = gtk::Box;
951
952    fn deref(&self) -> &gtk::Box {
953        &self.widget
954    }
955}
956
957fn gtk_focus_in(state: &mut State) -> Inhibit {
958    if let Some(mut nvim) = state.try_nvim() {
959        nvim.command_async("if exists('#FocusGained') | doautocmd FocusGained | endif")
960            .cb(|r| r.report_err())
961            .call();
962    }
963
964    state.im_context.focus_in();
965    state.cursor.as_mut().unwrap().enter_focus();
966    state.queue_redraw_cursor();
967
968    Inhibit(false)
969}
970
971fn gtk_focus_out(state: &mut State) -> Inhibit {
972    if let Some(mut nvim) = state.try_nvim() {
973        nvim.command_async("if exists('#FocusLost') | doautocmd FocusLost | endif")
974            .cb(|r| r.report_err())
975            .call();
976    }
977
978    state.im_context.focus_out();
979    state.cursor.as_mut().unwrap().leave_focus();
980    state.queue_redraw_cursor();
981
982    Inhibit(false)
983}
984
985fn gtk_scroll_event(state: &mut State, ui_state: &mut UiState, ev: &EventScroll) -> Inhibit {
986    if !state.mouse_enabled && !state.nvim.is_initializing() {
987        return Inhibit(false);
988    }
989
990    state.close_popup_menu();
991
992    match ev.get_direction() {
993        gdk::ScrollDirection::Right => {
994            mouse_input(state, "ScrollWheelRight", ev.get_state(), ev.get_position())
995        }
996        gdk::ScrollDirection::Left => {
997            mouse_input(state, "ScrollWheelLeft", ev.get_state(), ev.get_position())
998        }
999        gdk::ScrollDirection::Up => {
1000            mouse_input(state, "ScrollWheelUp", ev.get_state(), ev.get_position())
1001        }
1002        gdk::ScrollDirection::Down => {
1003            mouse_input(state, "ScrollWheelDown", ev.get_state(), ev.get_position())
1004        }
1005        gdk::ScrollDirection::Smooth => {
1006            // Remember and accumulate scroll deltas, so slow scrolling still
1007            // works.
1008            ui_state.scroll_delta.0 += ev.as_ref().delta_x;
1009            ui_state.scroll_delta.1 += ev.as_ref().delta_y;
1010            // Perform scroll action for deltas with abs(delta) >= 1.
1011            let x = ui_state.scroll_delta.0 as isize;
1012            let y = ui_state.scroll_delta.1 as isize;
1013            for _ in 0..x {
1014                mouse_input(state, "ScrollWheelRight", ev.get_state(), ev.get_position())
1015            }
1016            for _ in 0..-x {
1017                mouse_input(state, "ScrollWheelLeft", ev.get_state(), ev.get_position())
1018            }
1019            for _ in 0..y {
1020                mouse_input(state, "ScrollWheelDown", ev.get_state(), ev.get_position())
1021            }
1022            for _ in 0..-y {
1023                mouse_input(state, "ScrollWheelUp", ev.get_state(), ev.get_position())
1024            }
1025            // Subtract performed scroll deltas.
1026            ui_state.scroll_delta.0 -= x as f64;
1027            ui_state.scroll_delta.1 -= y as f64;
1028        }
1029        _ => (),
1030    }
1031    Inhibit(false)
1032}
1033
1034fn gtk_button_press(
1035    shell: &mut State,
1036    ui_state: &Rc<RefCell<UiState>>,
1037    ev: &EventButton,
1038    menu: &gtk::Menu,
1039) -> Inhibit {
1040    if ev.get_event_type() != EventType::ButtonPress {
1041        return Inhibit(false);
1042    }
1043
1044    if shell.mouse_enabled {
1045        ui_state.borrow_mut().mouse_pressed = true;
1046
1047        match ev.get_button() {
1048            1 => mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position()),
1049            2 => mouse_input(shell, "MiddleMouse", ev.get_state(), ev.get_position()),
1050            3 => menu.popup_at_pointer(None),
1051
1052            _ => (),
1053        }
1054    }
1055    Inhibit(false)
1056}
1057
1058fn mouse_input(shell: &mut State, input: &str, state: ModifierType, position: (f64, f64)) {
1059    if let Some(mut nvim) = shell.try_nvim() {
1060        let (col, row) = mouse_coordinates_to_nvim(shell, position);
1061        let input_str = format!("{}<{},{}>", keyval_to_input_string(input, state), col, row);
1062
1063        nvim.input(&input_str)
1064            .expect("Can't send mouse input event");
1065    }
1066}
1067
1068/**
1069 * Translate gtk mouse event coordinates to nvim (col, row).
1070 */
1071fn mouse_coordinates_to_nvim(shell: &State, position: (f64, f64)) -> (u64, u64) {
1072    let &CellMetrics {
1073        line_height,
1074        char_width,
1075        ..
1076    } = shell.render_state.borrow().font_ctx.cell_metrics();
1077    let (x, y) = position;
1078    let col = (x / char_width).trunc() as u64;
1079    let row = (y / line_height).trunc() as u64;
1080    (col, row)
1081}
1082
1083fn gtk_button_release(shell: &mut State, ui_state: &mut UiState, ev: &EventButton) -> Inhibit {
1084    ui_state.mouse_pressed = false;
1085
1086    if shell.mouse_enabled && !shell.nvim.is_initializing() {
1087        match ev.get_button() {
1088            1 => mouse_input(shell, "LeftRelease", ev.get_state(), ev.get_position()),
1089            2 => mouse_input(shell, "MiddleRelease", ev.get_state(), ev.get_position()),
1090            3 => mouse_input(shell, "RightRelease", ev.get_state(), ev.get_position()),
1091            _ => (),
1092        }
1093    }
1094
1095    Inhibit(false)
1096}
1097
1098fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion) -> Inhibit {
1099    if shell.mouse_enabled && ui_state.mouse_pressed {
1100        let ev_pos = ev.get_position();
1101        let pos = mouse_coordinates_to_nvim(shell, ev_pos);
1102
1103        // if we fire LeftDrag on the same coordinates multiple times, then
1104        // we get: https://github.com/daa84/neovim-gtk/issues/185
1105        if pos != ui_state.prev_pos {
1106            mouse_input(shell, "LeftDrag", ev.get_state(), ev_pos);
1107            ui_state.prev_pos = pos;
1108        }
1109    }
1110
1111    ui_state.apply_mouse_cursor(MouseCursor::Text, shell.drawing_area.get_window());
1112    Inhibit(false)
1113}
1114
1115fn draw_content(state: &State, ctx: &cairo::Context) {
1116    ctx.push_group();
1117
1118    let render_state = state.render_state.borrow();
1119    render::fill_background(
1120        ctx,
1121        &render_state.hl,
1122        state.transparency_settings.background_alpha(),
1123    );
1124    render::render(
1125        ctx,
1126        state.cursor.as_ref().unwrap(),
1127        &render_state.font_ctx,
1128        state.grids.current_model().unwrap(),
1129        &render_state.hl,
1130        state.transparency_settings.filled_alpha(),
1131    );
1132
1133    ctx.pop_group_to_source();
1134    ctx.paint();
1135}
1136
1137fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
1138    let state = state_arc.borrow();
1139    if state.nvim.is_initialized() {
1140        draw_content(&*state, ctx);
1141    } else if state.nvim.is_initializing() {
1142        draw_initializing(&*state, ctx);
1143    }
1144
1145    Inhibit(false)
1146}
1147
1148fn show_nvim_start_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
1149    let source = err.source();
1150    let cmd = err.cmd().unwrap().to_owned();
1151
1152    glib::idle_add(move || {
1153        let state = state_arc.borrow();
1154        state.nvim.set_error();
1155        state.error_area.show_nvim_start_error(&source, &cmd);
1156        state.show_error_area();
1157
1158        Continue(false)
1159    });
1160}
1161
1162fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
1163    let source = err.source();
1164
1165    glib::idle_add(move || {
1166        let state = state_arc.borrow();
1167        state.nvim.set_error();
1168        state.error_area.show_nvim_init_error(&source);
1169        state.show_error_area();
1170
1171        Continue(false)
1172    });
1173}
1174
1175fn init_nvim_async(
1176    state_arc: Arc<UiMutex<State>>,
1177    nvim_handler: NvimHandler,
1178    options: ShellOptions,
1179    cols: usize,
1180    rows: usize,
1181) {
1182    // execute nvim
1183    let nvim = match nvim::start(
1184        nvim_handler,
1185        options.nvim_bin_path.as_ref(),
1186        options.timeout,
1187        options.args_for_neovim,
1188    ) {
1189        Ok(nvim) => nvim,
1190        Err(err) => {
1191            show_nvim_start_error(&err, state_arc);
1192            return;
1193        }
1194    };
1195
1196    let nvim = set_nvim_to_state(state_arc.clone(), nvim);
1197
1198    // add callback on session end
1199    let guard = nvim.borrow().unwrap().session.take_dispatch_guard();
1200    let state_ref = state_arc.clone();
1201    thread::spawn(move || {
1202        guard.join().expect("Can't join dispatch thread");
1203
1204        glib::idle_add(move || {
1205            state_ref.borrow().nvim.clear();
1206            if let Some(ref cb) = state_ref.borrow().detach_cb {
1207                (&mut *cb.borrow_mut())();
1208            }
1209
1210            glib::Continue(false)
1211        });
1212    });
1213
1214    // attach ui
1215    if let Err(err) = nvim::post_start_init(nvim, cols as i64, rows as i64, options.input_data) {
1216        show_nvim_init_error(&err, state_arc.clone());
1217    } else {
1218        set_nvim_initialized(state_arc);
1219    }
1220}
1221
1222fn set_nvim_to_state(state_arc: Arc<UiMutex<State>>, nvim: Neovim) -> NeovimClientAsync {
1223    let pair = Arc::new((Mutex::new(None), Condvar::new()));
1224    let pair2 = pair.clone();
1225    let mut nvim = Some(nvim);
1226
1227    glib::idle_add(move || {
1228        let nvim_aync = state_arc.borrow().nvim.set_nvim_async(nvim.take().unwrap());
1229
1230        let &(ref lock, ref cvar) = &*pair2;
1231        let mut started = lock.lock().unwrap();
1232        *started = Some(nvim_aync);
1233        cvar.notify_one();
1234
1235        Continue(false)
1236    });
1237
1238    // Wait idle set nvim properly
1239    let &(ref lock, ref cvar) = &*pair;
1240    let mut started = lock.lock().unwrap();
1241    while started.is_none() {
1242        started = cvar.wait(started).unwrap();
1243    }
1244
1245    started.take().unwrap()
1246}
1247
1248fn set_nvim_initialized(state_arc: Arc<UiMutex<State>>) {
1249    glib::idle_add(clone!(state_arc => move || {
1250        let mut state = state_arc.borrow_mut();
1251        state.nvim.async_to_sync();
1252        state.nvim.set_initialized();
1253        // in some case resize can happens while initilization in progress
1254        // so force resize here
1255        state.try_nvim_resize();
1256        state.cursor.as_mut().unwrap().start();
1257
1258        Continue(false)
1259    }));
1260
1261    idle_cb_call!(state_arc.nvim_started_cb());
1262}
1263
1264fn draw_initializing(state: &State, ctx: &cairo::Context) {
1265    let render_state = state.render_state.borrow();
1266    let hl = &render_state.hl;
1267    let layout = pangocairo::functions::create_layout(ctx).unwrap();
1268    let alloc = state.drawing_area.get_allocation();
1269
1270    let bg_color = hl.bg();
1271    let fg_color = hl.fg();
1272    ctx.set_source_rgb(bg_color.0, bg_color.1, bg_color.2);
1273    ctx.paint();
1274
1275    layout.set_text("Loading->");
1276    let (width, height) = layout.get_pixel_size();
1277
1278    let x = alloc.width as f64 / 2.0 - width as f64 / 2.0;
1279    let y = alloc.height as f64 / 2.0 - height as f64 / 2.0;
1280
1281    ctx.move_to(x, y);
1282    ctx.set_source_rgb(fg_color.0, fg_color.1, fg_color.2);
1283    pangocairo::functions::update_layout(ctx, &layout);
1284    pangocairo::functions::show_layout(ctx, &layout);
1285
1286    ctx.move_to(x + width as f64, y);
1287    state
1288        .cursor
1289        .as_ref()
1290        .unwrap()
1291        .draw(ctx, &render_state.font_ctx, y, false, &hl);
1292}
1293
1294fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
1295    let mut state = state_ref.borrow_mut();
1296    if state.start_nvim_initialization() {
1297        let (cols, rows) = state.calc_nvim_size();
1298
1299        debug!("Init nvim {}/{}", cols, rows);
1300
1301        let state_arc = state_ref.clone();
1302        let nvim_handler = NvimHandler::new(state_ref.clone());
1303        let options = state.options.take();
1304        thread::spawn(move || init_nvim_async(state_arc, nvim_handler, options, cols, rows));
1305    }
1306}
1307
1308// Neovim redraw events
1309impl State {
1310    pub fn grid_line(
1311        &mut self,
1312        grid: u64,
1313        row: u64,
1314        col_start: u64,
1315        cells: Vec<Vec<Value>>,
1316    ) -> RepaintMode {
1317        let hl = &self.render_state.borrow().hl;
1318        let repaint_area = self.grids[grid].line(row as usize, col_start as usize, cells, hl);
1319        RepaintMode::Area(repaint_area)
1320    }
1321
1322    pub fn grid_clear(&mut self, grid: u64) -> RepaintMode {
1323        let hl = &self.render_state.borrow().hl;
1324        self.grids[grid].clear(&hl.default_hl());
1325        RepaintMode::All
1326    }
1327
1328    pub fn grid_destroy(&mut self, grid: u64) -> RepaintMode {
1329        self.grids.destroy(grid);
1330        RepaintMode::All
1331    }
1332
1333    pub fn grid_cursor_goto(&mut self, grid: u64, row: u64, column: u64) -> RepaintMode {
1334        let repaint_area = self.grids[grid].cursor_goto(row as usize, column as usize);
1335        self.set_im_location();
1336        RepaintMode::AreaList(repaint_area)
1337    }
1338
1339    pub fn grid_resize(&mut self, grid: u64, columns: u64, rows: u64) -> RepaintMode {
1340        debug!("on_resize {}/{}", columns, rows);
1341
1342        self.grids.get_or_create(grid).resize(columns, rows);
1343        RepaintMode::Nothing
1344    }
1345
1346    pub fn on_redraw(&mut self, mode: &RepaintMode) {
1347        match *mode {
1348            RepaintMode::All => {
1349                self.update_dirty_glyphs();
1350                self.drawing_area.queue_draw();
1351            }
1352            RepaintMode::Area(ref rect) => self.queue_draw_area(&[rect]),
1353            RepaintMode::AreaList(ref list) => self.queue_draw_area(&list.list),
1354            RepaintMode::Nothing => (),
1355        }
1356    }
1357
1358    pub fn grid_scroll(
1359        &mut self,
1360        grid: u64,
1361        top: u64,
1362        bot: u64,
1363        left: u64,
1364        right: u64,
1365        rows: i64,
1366        cols: i64,
1367    ) -> RepaintMode {
1368        let hl = &self.render_state.borrow().hl;
1369        RepaintMode::Area(self.grids[grid].scroll(
1370            top,
1371            bot,
1372            left,
1373            right,
1374            rows,
1375            cols,
1376            &hl.default_hl(),
1377        ))
1378    }
1379
1380    pub fn hl_attr_define(
1381        &mut self,
1382        id: u64,
1383        rgb_attr: HashMap<String, Value>,
1384        _: &Value,
1385        info: Vec<HashMap<String, Value>>,
1386    ) -> RepaintMode {
1387        self.render_state.borrow_mut().hl.set(id, &rgb_attr, &info);
1388        RepaintMode::Nothing
1389    }
1390
1391    pub fn default_colors_set(
1392        &mut self,
1393        fg: i64,
1394        bg: i64,
1395        sp: i64,
1396        cterm_fg: i64,
1397        cterm_bg: i64,
1398    ) -> RepaintMode {
1399        self.render_state.borrow_mut().hl.set_defaults(
1400            Color::from_indexed_color(fg as u64),
1401            Color::from_indexed_color(bg as u64),
1402            Color::from_indexed_color(sp as u64),
1403            if cterm_fg > 0 {
1404                Color::from_cterm((cterm_fg - 1) as u8)
1405            } else {
1406                COLOR_WHITE
1407            },
1408            if cterm_bg > 0 {
1409                Color::from_cterm((cterm_bg - 1) as u8)
1410            } else {
1411                COLOR_BLACK
1412            },
1413        );
1414        RepaintMode::All
1415    }
1416
1417    fn cur_point_area(&self) -> RepaintMode {
1418        if let Some(cur_point) = self.grids.current().map(|g| g.cur_point()) {
1419            RepaintMode::Area(cur_point)
1420        } else {
1421            RepaintMode::Nothing
1422        }
1423    }
1424
1425    pub fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode {
1426        let mut render_state = self.render_state.borrow_mut();
1427        render_state.mode.update(&mode, idx as usize);
1428        self.cursor
1429            .as_mut()
1430            .unwrap()
1431            .set_mode_info(render_state.mode.mode_info().cloned());
1432        self.cmd_line
1433            .set_mode_info(render_state.mode.mode_info().cloned());
1434
1435        self.cur_point_area()
1436    }
1437
1438    pub fn on_mouse(&mut self, on: bool) -> RepaintMode {
1439        self.mouse_enabled = on;
1440        RepaintMode::Nothing
1441    }
1442
1443    pub fn on_busy(&mut self, busy: bool) -> RepaintMode {
1444        if busy {
1445            self.cursor.as_mut().unwrap().busy_on();
1446        } else {
1447            self.cursor.as_mut().unwrap().busy_off();
1448        }
1449
1450        self.cur_point_area()
1451    }
1452
1453    pub fn popupmenu_show(
1454        &mut self,
1455        menu: &[CompleteItem],
1456        selected: i64,
1457        row: u64,
1458        col: u64,
1459    ) -> RepaintMode {
1460        let point = ModelRect::point(col as usize, row as usize);
1461        let render_state = self.render_state.borrow();
1462        let (x, y, width, height) = point.to_area(render_state.font_ctx.cell_metrics());
1463
1464        let context = popup_menu::PopupMenuContext {
1465            nvim: &self.nvim,
1466            hl: &render_state.hl,
1467            font_ctx: &render_state.font_ctx,
1468            menu_items: &menu,
1469            selected,
1470            x,
1471            y,
1472            width,
1473            height,
1474            max_width: self.max_popup_width(),
1475        };
1476
1477        self.popup_menu.show(context);
1478
1479        RepaintMode::Nothing
1480    }
1481
1482    pub fn popupmenu_hide(&mut self) -> RepaintMode {
1483        self.popup_menu.hide();
1484        RepaintMode::Nothing
1485    }
1486
1487    pub fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
1488        self.popup_menu.select(selected);
1489        RepaintMode::Nothing
1490    }
1491
1492    pub fn tabline_update(
1493        &mut self,
1494        selected: Tabpage,
1495        tabs: Vec<(Tabpage, Option<String>)>,
1496    ) -> RepaintMode {
1497        self.tabs.update_tabs(&self.nvim, &selected, &tabs);
1498
1499        RepaintMode::Nothing
1500    }
1501
1502    pub fn option_set(&mut self, name: String, val: Value) -> RepaintMode {
1503        if let "guifont" = name.as_str() { self.set_font_from_value(val) };
1504        RepaintMode::Nothing
1505    }
1506
1507    fn set_font_from_value(&mut self, val: Value) {
1508        if let Value::String(val) = val {
1509            if let Some(val) = val.into_str() {
1510                if !val.is_empty() {
1511                    let exists_fonts = self.render_state.borrow().font_ctx.font_families();
1512                    let fonts = split_at_comma(&val);
1513                    for font in &fonts {
1514                        let desc = FontDescription::from_string(&font);
1515                        if desc.get_size() > 0
1516                            && exists_fonts.contains(&desc.get_family().unwrap_or_else(|| "".into()))
1517                        {
1518                            self.set_font_rpc(font);
1519                            return;
1520                        }
1521                    }
1522
1523                    // font does not exists? set first one
1524                    if !fonts.is_empty() {
1525                        self.set_font_rpc(&fonts[0]);
1526                    }
1527                }
1528            }
1529        }
1530    }
1531
1532    pub fn mode_info_set(
1533        &mut self,
1534        cursor_style_enabled: bool,
1535        mode_infos: Vec<HashMap<String, Value>>,
1536    ) -> RepaintMode {
1537        let mode_info_arr = mode_infos
1538            .iter()
1539            .map(|mode_info_map| mode::ModeInfo::new(mode_info_map))
1540            .collect();
1541
1542        match mode_info_arr {
1543            Ok(mode_info_arr) => {
1544                let mut render_state = self.render_state.borrow_mut();
1545                render_state
1546                    .mode
1547                    .set_info(cursor_style_enabled, mode_info_arr);
1548            }
1549            Err(err) => {
1550                error!("Error load mode info: {}", err);
1551            }
1552        }
1553
1554        RepaintMode::Nothing
1555    }
1556
1557    pub fn cmdline_show(
1558        &mut self,
1559        content: Vec<(u64, String)>,
1560        pos: u64,
1561        firstc: String,
1562        prompt: String,
1563        indent: u64,
1564        level: u64,
1565    ) -> RepaintMode {
1566        {
1567            let cursor = self.grids.current().unwrap().cur_point();
1568            let render_state = self.render_state.borrow();
1569            let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics());
1570            let ctx = CmdLineContext {
1571                nvim: &self.nvim,
1572                content,
1573                pos,
1574                firstc,
1575                prompt,
1576                indent,
1577                level_idx: level,
1578                x,
1579                y,
1580                width,
1581                height,
1582                max_width: self.max_popup_width(),
1583            };
1584
1585            self.cmd_line.show_level(&ctx);
1586        }
1587
1588        self.on_busy(true)
1589    }
1590
1591    pub fn cmdline_hide(&mut self, level: u64) -> RepaintMode {
1592        self.cmd_line.hide_level(level);
1593        self.on_busy(false)
1594    }
1595
1596    pub fn cmdline_block_show(&mut self, content: Vec<Vec<(u64, String)>>) -> RepaintMode {
1597        let max_width = self.max_popup_width();
1598        self.cmd_line.show_block(&content, max_width);
1599        self.on_busy(true)
1600    }
1601
1602    pub fn cmdline_block_append(&mut self, content: Vec<(u64, String)>) -> RepaintMode {
1603        self.cmd_line.block_append(&content);
1604        RepaintMode::Nothing
1605    }
1606
1607    pub fn cmdline_block_hide(&mut self) -> RepaintMode {
1608        self.cmd_line.block_hide();
1609        self.on_busy(false)
1610    }
1611
1612    pub fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode {
1613        let render_state = self.render_state.borrow();
1614        self.cmd_line.pos(&*render_state, pos, level);
1615        RepaintMode::Nothing
1616    }
1617
1618    pub fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode {
1619        let render_state = self.render_state.borrow();
1620        self.cmd_line.special_char(&*render_state, c, shift, level);
1621        RepaintMode::Nothing
1622    }
1623
1624    pub fn wildmenu_show(&self, items: Vec<String>) -> RepaintMode {
1625        self.cmd_line
1626            .show_wildmenu(items, &*self.render_state.borrow(), self.max_popup_width());
1627        RepaintMode::Nothing
1628    }
1629
1630    pub fn wildmenu_hide(&self) -> RepaintMode {
1631        self.cmd_line.hide_wildmenu();
1632        RepaintMode::Nothing
1633    }
1634
1635    pub fn wildmenu_select(&self, selected: i64) -> RepaintMode {
1636        self.cmd_line.wildmenu_select(selected);
1637        RepaintMode::Nothing
1638    }
1639}
1640
1641impl CursorRedrawCb for State {
1642    fn queue_redraw_cursor(&mut self) {
1643        if let Some(cur_point) = self.grids.current().map(|g| g.cur_point()) {
1644            self.on_redraw(&RepaintMode::Area(cur_point));
1645        }
1646    }
1647}