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 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 #[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 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 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 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 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 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(©);
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) -> >k::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 ui_state.scroll_delta.0 += ev.as_ref().delta_x;
1009 ui_state.scroll_delta.1 += ev.as_ref().delta_y;
1010 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 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: >k::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
1068fn 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 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 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 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 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 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 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
1308impl 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 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}