2018-04-01 10:01:51 +00:00
|
|
|
use std;
|
2018-02-19 10:03:46 +00:00
|
|
|
use std::cell::{Cell, RefCell};
|
2017-04-12 10:12:05 +00:00
|
|
|
use std::rc::Rc;
|
2017-11-10 15:36:54 +00:00
|
|
|
use std::sync::{Arc, Condvar, Mutex};
|
2017-05-27 16:50:25 +00:00
|
|
|
use std::ops::Deref;
|
2017-06-04 10:14:09 +00:00
|
|
|
use std::thread;
|
2017-11-17 15:40:09 +00:00
|
|
|
use std::collections::HashMap;
|
2017-12-10 09:46:36 +00:00
|
|
|
use std::time::Duration;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
|
|
|
use cairo;
|
2018-02-24 20:36:40 +00:00
|
|
|
use cairo::prelude::*;
|
2018-02-19 10:03:46 +00:00
|
|
|
use pango::{FontDescription, LayoutExt};
|
2017-06-02 14:54:24 +00:00
|
|
|
use gdk;
|
2018-02-19 10:03:46 +00:00
|
|
|
use gdk::{EventButton, EventMotion, EventScroll, EventType, ModifierType};
|
2017-03-15 15:33:47 +00:00
|
|
|
use gdk_sys;
|
|
|
|
use glib;
|
2017-05-27 16:50:25 +00:00
|
|
|
use gtk;
|
2017-03-15 15:33:47 +00:00
|
|
|
use gtk::prelude::*;
|
2017-12-31 09:47:50 +00:00
|
|
|
use pangocairo;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2018-01-05 12:56:55 +00:00
|
|
|
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
|
2017-05-27 16:50:25 +00:00
|
|
|
use neovim_lib::neovim_api::Tabpage;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2018-03-19 18:00:40 +00:00
|
|
|
use misc::{decode_uri, escape_filename};
|
2018-02-19 10:03:46 +00:00
|
|
|
use settings::{FontSource, Settings};
|
|
|
|
use ui_model::{Attrs, ModelRect, UiModel};
|
|
|
|
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
2018-03-30 20:54:37 +00:00
|
|
|
use nvim::{self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef,
|
2018-04-01 10:01:51 +00:00
|
|
|
NvimHandler, RepaintMode};
|
2018-04-01 14:29:05 +00:00
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
use input;
|
|
|
|
use input::keyval_to_input_string;
|
2018-01-21 20:19:00 +00:00
|
|
|
use cursor::{BlinkCursor, Cursor, CursorRedrawCb};
|
2017-04-12 10:12:05 +00:00
|
|
|
use ui::UiMutex;
|
2017-11-18 12:56:37 +00:00
|
|
|
use popup_menu::{self, PopupMenu};
|
2017-05-27 16:50:25 +00:00
|
|
|
use tabline::Tabline;
|
2018-01-03 09:04:01 +00:00
|
|
|
use cmd_line::{CmdLine, CmdLineContext};
|
2017-07-06 09:41:35 +00:00
|
|
|
use error;
|
2017-07-08 20:45:55 +00:00
|
|
|
use mode;
|
2017-08-18 15:35:33 +00:00
|
|
|
use render;
|
2017-09-05 15:23:46 +00:00
|
|
|
use render::CellMetrics;
|
2018-03-04 12:22:56 +00:00
|
|
|
use subscriptions::{SubscriptionHandle, Subscriptions};
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12";
|
2018-01-08 11:09:40 +00:00
|
|
|
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2";
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
macro_rules! idle_cb_call {
|
|
|
|
($state:ident.$cb:ident($( $x:expr ),*)) => (
|
2017-07-09 11:05:55 +00:00
|
|
|
glib::idle_add(move || {
|
2017-06-04 10:14:09 +00:00
|
|
|
if let Some(ref cb) = $state.borrow().$cb {
|
|
|
|
(&mut *cb.borrow_mut())($($x),*);
|
|
|
|
}
|
|
|
|
|
|
|
|
glib::Continue(false)
|
|
|
|
});
|
|
|
|
)
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-02-24 20:36:40 +00:00
|
|
|
/// Double buffer surface
|
|
|
|
pub struct Surface {
|
|
|
|
surface: cairo::Surface,
|
|
|
|
ctx: cairo::Context,
|
|
|
|
width: i32,
|
|
|
|
height: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Surface {
|
|
|
|
pub fn new(drawing_area: >k::DrawingArea) -> Self {
|
|
|
|
let alloc = drawing_area.get_allocation();
|
|
|
|
let surface = drawing_area
|
|
|
|
.get_window()
|
|
|
|
.unwrap()
|
|
|
|
.create_similar_surface(cairo::Content::Color, alloc.width, alloc.height)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let ctx = cairo::Context::new(&surface);
|
|
|
|
|
|
|
|
Surface {
|
|
|
|
surface,
|
|
|
|
ctx,
|
|
|
|
width: alloc.width,
|
|
|
|
height: alloc.height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-23 14:57:39 +00:00
|
|
|
pub struct RenderState {
|
|
|
|
pub font_ctx: render::Context,
|
|
|
|
pub color_model: ColorModel,
|
|
|
|
pub mode: mode::Mode,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RenderState {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
RenderState {
|
|
|
|
font_ctx: render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)),
|
|
|
|
color_model: ColorModel::new(),
|
|
|
|
mode: mode::Mode::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub struct State {
|
2017-03-15 15:33:47 +00:00
|
|
|
pub model: UiModel,
|
2017-04-12 10:12:05 +00:00
|
|
|
cur_attrs: Option<Attrs>,
|
2017-03-15 15:33:47 +00:00
|
|
|
mouse_enabled: bool,
|
2017-11-10 15:36:54 +00:00
|
|
|
nvim: Rc<NeovimClient>,
|
2018-01-21 20:19:00 +00:00
|
|
|
cursor: Option<BlinkCursor<State>>,
|
2017-11-18 12:56:37 +00:00
|
|
|
popup_menu: PopupMenu,
|
|
|
|
cmd_line: CmdLine,
|
2017-04-12 10:12:05 +00:00
|
|
|
settings: Rc<RefCell<Settings>>,
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state: Rc<RefCell<RenderState>>,
|
2018-02-23 10:07:38 +00:00
|
|
|
|
2018-02-24 20:36:40 +00:00
|
|
|
surface: Option<Surface>,
|
2018-04-01 10:01:51 +00:00
|
|
|
enable_double_buffer: bool,
|
2018-02-24 20:36:40 +00:00
|
|
|
|
2018-02-23 10:07:38 +00:00
|
|
|
resize_request: (i64, i64),
|
2018-02-19 10:03:46 +00:00
|
|
|
resize_timer: Rc<Cell<Option<glib::SourceId>>>,
|
2018-02-23 10:07:38 +00:00
|
|
|
|
2018-01-02 19:38:35 +00:00
|
|
|
pub clipboard_clipboard: gtk::Clipboard,
|
|
|
|
pub clipboard_primary: gtk::Clipboard,
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
stack: gtk::Stack,
|
2017-12-21 19:54:27 +00:00
|
|
|
pub drawing_area: gtk::DrawingArea,
|
2017-05-27 16:50:25 +00:00
|
|
|
tabs: Tabline,
|
2017-06-02 14:54:24 +00:00
|
|
|
im_context: gtk::IMMulticontext,
|
2017-07-06 09:41:35 +00:00
|
|
|
error_area: error::ErrorArea,
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
options: ShellOptions,
|
|
|
|
|
|
|
|
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
2017-10-15 19:50:59 +00:00
|
|
|
nvim_started_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
2018-03-30 20:54:37 +00:00
|
|
|
command_cb: Option<Box<FnMut(Vec<Value>) + Send + 'static>>,
|
2018-02-25 14:54:15 +00:00
|
|
|
|
|
|
|
subscriptions: RefCell<Subscriptions>,
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
impl State {
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> State {
|
2017-05-27 16:50:25 +00:00
|
|
|
let drawing_area = gtk::DrawingArea::new();
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = Rc::new(RefCell::new(RenderState::new()));
|
2017-11-18 12:56:37 +00:00
|
|
|
let popup_menu = PopupMenu::new(&drawing_area);
|
2017-11-23 14:57:39 +00:00
|
|
|
let cmd_line = CmdLine::new(&drawing_area, render_state.clone());
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
State {
|
2017-10-11 15:55:29 +00:00
|
|
|
model: UiModel::empty(),
|
2017-11-10 15:36:54 +00:00
|
|
|
nvim: Rc::new(NeovimClient::new()),
|
2017-03-15 15:33:47 +00:00
|
|
|
cur_attrs: None,
|
2017-03-16 15:12:56 +00:00
|
|
|
mouse_enabled: true,
|
2017-04-12 10:12:05 +00:00
|
|
|
cursor: None,
|
2017-05-22 15:38:36 +00:00
|
|
|
popup_menu,
|
2017-11-18 20:15:03 +00:00
|
|
|
cmd_line,
|
2017-09-05 15:23:46 +00:00
|
|
|
settings,
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state,
|
2018-02-23 10:07:38 +00:00
|
|
|
|
2018-02-24 20:36:40 +00:00
|
|
|
surface: None,
|
2018-04-01 10:01:51 +00:00
|
|
|
enable_double_buffer: std::env::var("NVIM_GTK_DOUBLE_BUFFER")
|
|
|
|
.map(|opt| opt.trim() == "1")
|
|
|
|
.unwrap_or(false),
|
2018-02-24 20:36:40 +00:00
|
|
|
|
2018-02-23 10:07:38 +00:00
|
|
|
resize_request: (-1, -1),
|
2018-02-19 10:03:46 +00:00
|
|
|
resize_timer: Rc::new(Cell::new(None)),
|
2018-02-23 10:07:38 +00:00
|
|
|
|
2018-01-02 19:38:35 +00:00
|
|
|
clipboard_clipboard: gtk::Clipboard::get(&gdk::Atom::intern("CLIPBOARD")),
|
|
|
|
clipboard_primary: gtk::Clipboard::get(&gdk::Atom::intern("PRIMARY")),
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-07-08 20:45:55 +00:00
|
|
|
// UI
|
2017-07-06 09:41:35 +00:00
|
|
|
stack: gtk::Stack::new(),
|
2017-05-27 16:50:25 +00:00
|
|
|
drawing_area,
|
|
|
|
tabs: Tabline::new(),
|
2017-06-02 14:54:24 +00:00
|
|
|
im_context: gtk::IMMulticontext::new(),
|
2017-07-06 09:41:35 +00:00
|
|
|
error_area: error::ErrorArea::new(),
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
options,
|
|
|
|
|
|
|
|
detach_cb: None,
|
2017-10-15 19:50:59 +00:00
|
|
|
nvim_started_cb: None,
|
2018-03-30 20:54:37 +00:00
|
|
|
command_cb: None,
|
2018-02-25 14:54:15 +00:00
|
|
|
|
|
|
|
subscriptions: RefCell::new(Subscriptions::new()),
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-24 20:36:40 +00:00
|
|
|
fn resize_surface(&mut self) {
|
2018-04-01 10:01:51 +00:00
|
|
|
if !self.enable_double_buffer {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-24 20:36:40 +00:00
|
|
|
if let Some(Surface { width, height, .. }) = self.surface {
|
|
|
|
let alloc = self.drawing_area.get_allocation();
|
|
|
|
|
|
|
|
if width != alloc.width || height != alloc.height {
|
|
|
|
self.surface = Some(Surface::new(&self.drawing_area));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.surface = Some(Surface::new(&self.drawing_area));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-10 18:09:36 +00:00
|
|
|
/// Return NeovimRef only if vim in non blocking state
|
|
|
|
///
|
|
|
|
/// Note that this call also do neovim api call get_mode
|
2018-01-05 12:56:55 +00:00
|
|
|
#[allow(dead_code)]
|
2017-12-10 18:09:36 +00:00
|
|
|
pub fn nvim_non_blocked(&self) -> Option<NeovimRef> {
|
2017-12-11 20:15:17 +00:00
|
|
|
self.nvim().and_then(NeovimRef::non_blocked)
|
2017-12-10 18:09:36 +00:00
|
|
|
}
|
|
|
|
|
2017-11-10 15:36:54 +00:00
|
|
|
pub fn nvim(&self) -> Option<NeovimRef> {
|
|
|
|
self.nvim.nvim()
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-11-10 15:36:54 +00:00
|
|
|
pub fn nvim_clone(&self) -> Rc<NeovimClient> {
|
2017-07-06 09:41:35 +00:00
|
|
|
self.nvim.clone()
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 15:59:23 +00:00
|
|
|
pub fn start_nvim_initialization(&self) -> bool {
|
2017-11-10 15:36:54 +00:00
|
|
|
if self.nvim.is_uninitialized() {
|
|
|
|
self.nvim.set_in_progress();
|
2017-10-11 15:55:29 +00:00
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn set_detach_cb<F>(&mut self, cb: Option<F>)
|
2017-08-16 09:41:22 +00:00
|
|
|
where
|
|
|
|
F: FnMut() + Send + 'static,
|
2017-06-04 10:14:09 +00:00
|
|
|
{
|
|
|
|
if cb.is_some() {
|
|
|
|
self.detach_cb = Some(Box::new(RefCell::new(cb.unwrap())));
|
|
|
|
} else {
|
|
|
|
self.detach_cb = None;
|
|
|
|
}
|
2017-05-25 14:50:56 +00:00
|
|
|
}
|
|
|
|
|
2017-10-15 19:50:59 +00:00
|
|
|
pub fn set_nvim_started_cb<F>(&mut self, cb: Option<F>)
|
|
|
|
where
|
|
|
|
F: FnMut() + Send + 'static,
|
|
|
|
{
|
|
|
|
if cb.is_some() {
|
|
|
|
self.nvim_started_cb = Some(Box::new(RefCell::new(cb.unwrap())));
|
|
|
|
} else {
|
|
|
|
self.nvim_started_cb = None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn set_nvim_command_cb<F>(&mut self, cb: Option<F>)
|
|
|
|
where
|
|
|
|
F: FnMut(Vec<Value>) + Send + 'static,
|
|
|
|
{
|
|
|
|
if cb.is_some() {
|
|
|
|
self.command_cb = Some(Box::new(cb.unwrap()));
|
|
|
|
} else {
|
|
|
|
self.command_cb = None;
|
|
|
|
}
|
2017-05-25 14:50:56 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub fn set_font_desc(&mut self, desc: &str) {
|
2018-02-23 11:16:20 +00:00
|
|
|
self.render_state
|
|
|
|
.borrow_mut()
|
|
|
|
.font_ctx
|
|
|
|
.update(FontDescription::from_string(desc));
|
2017-09-07 14:48:10 +00:00
|
|
|
self.model.clear_glyphs();
|
2017-09-13 15:34:18 +00:00
|
|
|
self.try_nvim_resize();
|
2017-09-07 14:48:10 +00:00
|
|
|
self.on_redraw(&RepaintMode::All);
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
2017-05-13 14:31:19 +00:00
|
|
|
pub fn open_file(&self, path: &str) {
|
2017-09-13 14:40:43 +00:00
|
|
|
if let Some(mut nvim) = self.nvim() {
|
2018-03-18 17:45:06 +00:00
|
|
|
nvim.command_async(&format!("e {}", path))
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-05-13 14:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cd(&self, path: &str) {
|
2017-09-13 14:40:43 +00:00
|
|
|
if let Some(mut nvim) = self.nvim() {
|
2018-03-18 17:45:06 +00:00
|
|
|
nvim.command_async(&format!("cd {}", path))
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-07-22 13:27:17 +00:00
|
|
|
}
|
|
|
|
|
2018-01-02 19:38:35 +00:00
|
|
|
pub fn clipboard_clipboard_set(&self, text: &str) {
|
|
|
|
self.clipboard_clipboard.set_text(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clipboard_primary_set(&self, text: &str) {
|
|
|
|
self.clipboard_primary.set_text(text);
|
2017-12-31 23:23:37 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
fn close_popup_menu(&self) {
|
2017-11-18 12:56:37 +00:00
|
|
|
if self.popup_menu.is_open() {
|
2017-09-13 14:40:43 +00:00
|
|
|
if let Some(mut nvim) = self.nvim() {
|
2017-12-11 20:15:17 +00:00
|
|
|
nvim.input("<Esc>").report_err();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-22 11:31:06 +00:00
|
|
|
|
2017-09-07 14:48:10 +00:00
|
|
|
fn queue_draw_area<M: AsRef<ModelRect>>(&mut self, rect_list: &[M]) {
|
2017-09-08 15:26:16 +00:00
|
|
|
// extends by items before, then after changes
|
|
|
|
|
2017-09-13 14:40:43 +00:00
|
|
|
let rects: Vec<_> = rect_list
|
|
|
|
.iter()
|
|
|
|
.map(|rect| rect.as_ref().clone())
|
|
|
|
.map(|mut rect| {
|
|
|
|
rect.extend_by_items(&self.model);
|
|
|
|
rect
|
|
|
|
})
|
|
|
|
.collect();
|
2017-09-08 15:26:16 +00:00
|
|
|
|
2017-09-07 14:48:10 +00:00
|
|
|
self.update_dirty_glyphs();
|
2017-09-06 15:31:13 +00:00
|
|
|
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
|
|
|
let cell_metrics = render_state.font_ctx.cell_metrics();
|
|
|
|
|
2017-09-08 15:26:16 +00:00
|
|
|
for mut rect in rects {
|
2017-09-06 15:31:13 +00:00
|
|
|
rect.extend_by_items(&self.model);
|
|
|
|
|
2017-11-23 14:57:39 +00:00
|
|
|
let (x, y, width, height) = rect.to_area_extend_ink(&self.model, cell_metrics);
|
2017-09-05 15:23:46 +00:00
|
|
|
self.drawing_area.queue_draw_area(x, y, width, height);
|
2017-04-22 11:31:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-06-02 14:54:24 +00:00
|
|
|
|
2017-09-07 14:48:10 +00:00
|
|
|
#[inline]
|
|
|
|
fn update_dirty_glyphs(&mut self) {
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
|
|
|
render::shape_dirty(
|
|
|
|
&render_state.font_ctx,
|
|
|
|
&mut self.model,
|
|
|
|
&render_state.color_model,
|
|
|
|
);
|
2017-09-07 14:48:10 +00:00
|
|
|
}
|
|
|
|
|
2017-06-02 14:54:24 +00:00
|
|
|
fn im_commit(&self, ch: &str) {
|
2017-11-10 15:36:54 +00:00
|
|
|
if let Some(mut nvim) = self.nvim() {
|
|
|
|
input::im_input(&mut nvim, ch);
|
|
|
|
}
|
2017-07-11 09:33:55 +00:00
|
|
|
}
|
|
|
|
|
2017-09-05 15:23:46 +00:00
|
|
|
fn calc_nvim_size(&self) -> (usize, usize) {
|
|
|
|
let &CellMetrics {
|
|
|
|
line_height,
|
|
|
|
char_width,
|
|
|
|
..
|
2017-11-23 14:57:39 +00:00
|
|
|
} = self.render_state.borrow().font_ctx.cell_metrics();
|
2017-09-05 15:23:46 +00:00
|
|
|
let alloc = self.drawing_area.get_allocation();
|
|
|
|
(
|
|
|
|
(alloc.width as f64 / char_width).trunc() as usize,
|
|
|
|
(alloc.height as f64 / line_height).trunc() as usize,
|
|
|
|
)
|
2017-06-02 14:54:24 +00:00
|
|
|
}
|
2017-07-12 15:32:10 +00:00
|
|
|
|
|
|
|
fn show_error_area(&self) {
|
|
|
|
let stack = self.stack.clone();
|
|
|
|
gtk::idle_add(move || {
|
2017-08-16 09:41:22 +00:00
|
|
|
stack.set_visible_child_name("Error");
|
|
|
|
Continue(false)
|
|
|
|
});
|
2017-07-12 15:32:10 +00:00
|
|
|
}
|
2017-08-22 08:59:06 +00:00
|
|
|
|
|
|
|
fn set_im_location(&self) {
|
2017-09-05 15:23:46 +00:00
|
|
|
let (row, col) = self.model.get_cursor();
|
|
|
|
|
2017-09-07 15:51:12 +00:00
|
|
|
let (x, y, width, height) =
|
2017-11-23 14:57:39 +00:00
|
|
|
ModelRect::point(col, row).to_area(self.render_state.borrow().font_ctx.cell_metrics());
|
2017-09-05 15:23:46 +00:00
|
|
|
|
|
|
|
self.im_context.set_cursor_location(&gdk::Rectangle {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
});
|
2017-08-22 08:59:06 +00:00
|
|
|
|
|
|
|
self.im_context.reset();
|
|
|
|
}
|
2017-09-13 14:40:43 +00:00
|
|
|
|
2018-01-02 20:05:32 +00:00
|
|
|
fn try_nvim_resize(&mut self) {
|
2017-09-14 13:17:50 +00:00
|
|
|
let (columns, rows) = self.calc_nvim_size();
|
|
|
|
|
2017-09-13 15:34:18 +00:00
|
|
|
if self.model.rows == rows && self.model.columns == columns {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-23 10:07:38 +00:00
|
|
|
let (requested_rows, requested_cols) = self.resize_request;
|
|
|
|
|
|
|
|
if requested_rows == rows as i64 && requested_cols == columns as i64 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-19 10:03:46 +00:00
|
|
|
let resize_timer = self.resize_timer.take();
|
|
|
|
if let Some(resize_timer) = resize_timer {
|
|
|
|
glib::source_remove(resize_timer);
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2018-02-19 10:03:46 +00:00
|
|
|
|
2018-02-23 10:07:38 +00:00
|
|
|
self.resize_request = (rows as i64, columns as i64);
|
|
|
|
|
2018-02-19 10:03:46 +00:00
|
|
|
let nvim = self.nvim.clone();
|
|
|
|
let resize_timer = self.resize_timer.clone();
|
|
|
|
|
|
|
|
let resize_id = gtk::timeout_add(200, move || {
|
|
|
|
resize_timer.set(None);
|
2018-02-19 10:05:04 +00:00
|
|
|
|
2018-02-19 10:03:46 +00:00
|
|
|
if let Some(mut nvim) = nvim.nvim() {
|
2018-02-19 10:05:04 +00:00
|
|
|
debug!("ui_try_resize {}/{}", columns, rows);
|
2018-02-19 10:03:46 +00:00
|
|
|
nvim.ui_try_resize_async(columns as u64, rows as u64)
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
|
|
|
}
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
|
|
|
|
self.resize_timer.set(Some(resize_id));
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-10-12 05:59:16 +00:00
|
|
|
|
|
|
|
fn edit_paste(&self, clipboard: &str) {
|
|
|
|
let nvim = self.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
2018-02-23 11:16:20 +00:00
|
|
|
if render_state.mode.is(&mode::NvimMode::Insert)
|
|
|
|
|| render_state.mode.is(&mode::NvimMode::Normal)
|
2017-11-23 14:57:39 +00:00
|
|
|
{
|
2017-10-14 11:42:50 +00:00
|
|
|
let paste_code = format!("normal! \"{}P", clipboard);
|
2018-01-05 12:56:55 +00:00
|
|
|
nvim.command_async(&paste_code)
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
2017-10-12 05:59:16 +00:00
|
|
|
} else {
|
2017-10-13 09:04:40 +00:00
|
|
|
let paste_code = format!("<C-r>{}", clipboard);
|
2018-01-05 12:56:55 +00:00
|
|
|
nvim.input_async(&paste_code).cb(|r| r.report_err()).call();
|
2017-10-12 05:59:16 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2018-01-13 15:02:47 +00:00
|
|
|
|
|
|
|
fn max_popup_width(&self) -> i32 {
|
|
|
|
self.drawing_area.get_allocated_width() - 20
|
|
|
|
}
|
2018-03-23 10:15:53 +00:00
|
|
|
|
2018-02-25 14:54:15 +00:00
|
|
|
pub fn subscribe<F>(&self, event_name: &str, args: &[&str], cb: F) -> SubscriptionHandle
|
|
|
|
where
|
|
|
|
F: Fn(Vec<String>) + 'static,
|
|
|
|
{
|
2018-03-04 12:22:56 +00:00
|
|
|
self.subscriptions
|
|
|
|
.borrow_mut()
|
|
|
|
.subscribe(event_name, args, cb)
|
2018-02-25 14:54:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_autocmds(&self) {
|
2018-03-04 12:22:56 +00:00
|
|
|
self.subscriptions
|
|
|
|
.borrow()
|
|
|
|
.set_autocmds(&mut self.nvim().unwrap());
|
2018-02-25 14:54:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify(&self, params: Vec<Value>) -> Result<(), String> {
|
|
|
|
self.subscriptions.borrow().notify(params)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_now(&self, handle: &SubscriptionHandle) {
|
2018-03-04 12:22:56 +00:00
|
|
|
self.subscriptions
|
|
|
|
.borrow()
|
|
|
|
.run_now(handle, &mut self.nvim().unwrap());
|
2018-02-25 14:54:15 +00:00
|
|
|
}
|
2018-03-30 20:54:37 +00:00
|
|
|
|
|
|
|
pub fn set_font(&mut self, font_desc: String) {
|
|
|
|
{
|
|
|
|
let mut settings = self.settings.borrow_mut();
|
|
|
|
settings.set_font_source(FontSource::Rpc);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.set_font_desc(&font_desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn on_command(&mut self, args: Vec<Value>) {
|
|
|
|
if let Some(ref mut cb) = self.command_cb {
|
|
|
|
cb(args);
|
|
|
|
}
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UiState {
|
|
|
|
mouse_pressed: bool,
|
2017-12-06 17:21:43 +00:00
|
|
|
scroll_delta: (f64, f64),
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl UiState {
|
|
|
|
pub fn new() -> UiState {
|
2017-12-06 17:21:43 +00:00
|
|
|
UiState {
|
|
|
|
mouse_pressed: false,
|
|
|
|
scroll_delta: (0.0, 0.0),
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
#[derive(Clone)]
|
2017-06-04 10:14:09 +00:00
|
|
|
pub struct ShellOptions {
|
|
|
|
nvim_bin_path: Option<String>,
|
2018-03-20 17:43:31 +00:00
|
|
|
open_paths: Vec<String>,
|
2017-12-10 09:46:36 +00:00
|
|
|
timeout: Option<Duration>,
|
2017-06-04 10:14:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ShellOptions {
|
2017-12-10 09:46:36 +00:00
|
|
|
pub fn new(
|
|
|
|
nvim_bin_path: Option<String>,
|
2018-03-20 17:43:31 +00:00
|
|
|
open_paths: Vec<String>,
|
2017-12-10 09:46:36 +00:00
|
|
|
timeout: Option<Duration>,
|
|
|
|
) -> Self {
|
2017-06-04 10:14:09 +00:00
|
|
|
ShellOptions {
|
|
|
|
nvim_bin_path,
|
2018-03-20 17:43:31 +00:00
|
|
|
open_paths,
|
2017-12-10 09:46:36 +00:00
|
|
|
timeout,
|
2017-06-04 10:14:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub struct Shell {
|
2017-04-19 08:40:53 +00:00
|
|
|
pub state: Arc<UiMutex<State>>,
|
2017-04-12 10:12:05 +00:00
|
|
|
ui_state: Rc<RefCell<UiState>>,
|
2017-05-27 16:50:25 +00:00
|
|
|
|
|
|
|
widget: gtk::Box,
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Shell {
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> Shell {
|
2017-04-12 10:12:05 +00:00
|
|
|
let shell = Shell {
|
2017-06-04 10:14:09 +00:00
|
|
|
state: Arc::new(UiMutex::new(State::new(settings, options))),
|
2017-04-12 10:12:05 +00:00
|
|
|
ui_state: Rc::new(RefCell::new(UiState::new())),
|
2017-05-27 16:50:25 +00:00
|
|
|
|
|
|
|
widget: gtk::Box::new(gtk::Orientation::Vertical, 0),
|
2017-04-12 10:12:05 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let shell_ref = Arc::downgrade(&shell.state);
|
2018-01-21 20:19:00 +00:00
|
|
|
shell.state.borrow_mut().cursor = Some(BlinkCursor::new(shell_ref));
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
shell
|
|
|
|
}
|
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
pub fn is_nvim_initialized(&self) -> bool {
|
|
|
|
let state = self.state.borrow();
|
2017-11-10 15:36:54 +00:00
|
|
|
state.nvim.is_initialized()
|
2017-07-06 09:41:35 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
pub fn init(&mut self) {
|
2017-07-22 10:32:56 +00:00
|
|
|
let state = self.state.borrow();
|
2017-04-12 10:12:05 +00:00
|
|
|
state.drawing_area.set_hexpand(true);
|
|
|
|
state.drawing_area.set_vexpand(true);
|
|
|
|
state.drawing_area.set_can_focus(true);
|
|
|
|
|
2017-06-02 14:54:24 +00:00
|
|
|
state.im_context.set_use_preedit(false);
|
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
let nvim_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
|
|
|
|
|
|
|
nvim_box.pack_start(&*state.tabs, false, true, 0);
|
|
|
|
nvim_box.pack_start(&state.drawing_area, true, true, 0);
|
|
|
|
|
|
|
|
state.stack.add_named(&nvim_box, "Nvim");
|
|
|
|
state.stack.add_named(&*state.error_area, "Error");
|
|
|
|
|
|
|
|
self.widget.pack_start(&state.stack, true, true, 0);
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.set_events(
|
|
|
|
(gdk_sys::GDK_BUTTON_RELEASE_MASK | gdk_sys::GDK_BUTTON_PRESS_MASK
|
|
|
|
| gdk_sys::GDK_BUTTON_MOTION_MASK | gdk_sys::GDK_SCROLL_MASK
|
|
|
|
| gdk_sys::GDK_SMOOTH_SCROLL_MASK)
|
|
|
|
.bits() as i32,
|
|
|
|
);
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
let ref_ui_state = self.ui_state.clone();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.drawing_area.connect_button_press_event(move |_, ev| {
|
|
|
|
gtk_button_press(
|
|
|
|
&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev,
|
|
|
|
)
|
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-10-13 08:43:07 +00:00
|
|
|
let ref_state = self.state.clone();
|
2017-04-12 10:12:05 +00:00
|
|
|
let ref_ui_state = self.ui_state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_button_release_event(move |_, ev| {
|
2017-10-13 08:43:07 +00:00
|
|
|
gtk_button_release(
|
|
|
|
&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev,
|
|
|
|
)
|
2018-02-19 10:03:46 +00:00
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
let ref_ui_state = self.ui_state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_motion_notify_event(move |_, ev| {
|
2017-08-16 09:41:22 +00:00
|
|
|
gtk_motion_notify(
|
|
|
|
&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev,
|
|
|
|
)
|
2018-02-19 10:03:46 +00:00
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_draw(move |_, ctx| gtk_draw(&ref_state, ctx));
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.drawing_area.connect_key_press_event(move |_, ev| {
|
2017-12-03 09:26:15 +00:00
|
|
|
ref_state
|
|
|
|
.borrow_mut()
|
|
|
|
.cursor
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.reset_state();
|
|
|
|
|
|
|
|
if ref_state.borrow().im_context.filter_keypress(ev) {
|
2017-08-16 09:41:22 +00:00
|
|
|
Inhibit(true)
|
|
|
|
} else {
|
2017-12-03 09:26:15 +00:00
|
|
|
let state = ref_state.borrow();
|
|
|
|
let nvim = state.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
2017-11-10 15:36:54 +00:00
|
|
|
input::gtk_key_press(&mut nvim, ev)
|
2017-06-02 14:54:24 +00:00
|
|
|
} else {
|
2017-08-16 09:41:22 +00:00
|
|
|
Inhibit(false)
|
2017-06-02 14:54:24 +00:00
|
|
|
}
|
2017-08-16 09:41:22 +00:00
|
|
|
}
|
|
|
|
});
|
2017-06-02 14:54:24 +00:00
|
|
|
let ref_state = self.state.clone();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.drawing_area.connect_key_release_event(move |_, ev| {
|
|
|
|
ref_state.borrow().im_context.filter_keypress(ev);
|
|
|
|
Inhibit(false)
|
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2017-12-06 17:21:43 +00:00
|
|
|
let ref_ui_state = self.ui_state.clone();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.drawing_area.connect_scroll_event(move |_, ev| {
|
2017-12-31 09:47:50 +00:00
|
|
|
gtk_scroll_event(
|
|
|
|
&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev,
|
|
|
|
)
|
2017-08-16 09:41:22 +00:00
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_focus_in_event(move |_, _| gtk_focus_in(&mut *ref_state.borrow_mut()));
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_focus_out_event(move |_, _| gtk_focus_out(&mut *ref_state.borrow_mut()));
|
2017-06-02 14:54:24 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.drawing_area.connect_realize(move |w| {
|
2017-08-22 08:59:06 +00:00
|
|
|
// sometime set_client_window does not work without idle_add
|
|
|
|
// and looks like not enabled im_context
|
2018-01-02 20:05:32 +00:00
|
|
|
gtk::idle_add(clone!(ref_state, w => move || {
|
2017-08-22 08:59:06 +00:00
|
|
|
ref_state.borrow().im_context.set_client_window(
|
|
|
|
w.get_window().as_ref(),
|
|
|
|
);
|
|
|
|
Continue(false)
|
2018-01-02 20:05:32 +00:00
|
|
|
}));
|
2017-08-16 09:41:22 +00:00
|
|
|
});
|
2017-06-02 14:54:24 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state
|
|
|
|
.im_context
|
|
|
|
.connect_commit(move |_, ch| ref_state.borrow().im_commit(ch));
|
2017-06-02 14:54:24 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
let ref_state = self.state.clone();
|
2018-02-17 10:07:06 +00:00
|
|
|
state.drawing_area.connect_configure_event(move |_, ev| {
|
|
|
|
debug!("configure_event {:?}", ev.get_size());
|
2018-02-24 20:36:40 +00:00
|
|
|
|
|
|
|
let mut state = ref_state.borrow_mut();
|
|
|
|
state.resize_surface();
|
|
|
|
state.try_nvim_resize();
|
|
|
|
|
2017-08-16 09:41:22 +00:00
|
|
|
false
|
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-09-13 14:40:43 +00:00
|
|
|
let ref_state = self.state.clone();
|
2018-02-19 10:03:46 +00:00
|
|
|
state.drawing_area.connect_size_allocate(move |_, _| {
|
|
|
|
init_nvim(&ref_state);
|
|
|
|
});
|
2018-03-19 18:00:40 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
let targets = vec![
|
|
|
|
gtk::TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP, 0),
|
|
|
|
];
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY);
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_drag_data_received(move |_, _, _, _, s, _, _| {
|
|
|
|
let uris = s.get_uris();
|
2018-03-20 17:43:31 +00:00
|
|
|
let command = uris.iter().filter_map(|uri| decode_uri(uri)).fold(
|
|
|
|
":ar".to_owned(),
|
|
|
|
|command, filename| {
|
2018-03-19 18:00:40 +00:00
|
|
|
let filename = escape_filename(&filename);
|
|
|
|
command + " " + &filename
|
2018-03-20 17:43:31 +00:00
|
|
|
},
|
|
|
|
);
|
2018-03-19 18:00:40 +00:00
|
|
|
let state = ref_state.borrow_mut();
|
|
|
|
let mut nvim = state.nvim().unwrap();
|
|
|
|
nvim.command_async(&command).cb(|r| r.report_err()).call()
|
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
#[cfg(unix)]
|
2017-04-12 10:12:05 +00:00
|
|
|
pub fn set_font_desc(&self, font_name: &str) {
|
|
|
|
self.state.borrow_mut().set_font_desc(font_name);
|
|
|
|
}
|
|
|
|
|
2017-05-27 16:50:25 +00:00
|
|
|
pub fn grab_focus(&self) {
|
|
|
|
self.state.borrow().drawing_area.grab_focus();
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub fn open_file(&self, path: &str) {
|
2017-05-13 14:31:19 +00:00
|
|
|
self.state.borrow().open_file(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cd(&self, path: &str) {
|
|
|
|
self.state.borrow().cd(path);
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn detach_ui(&mut self) {
|
2017-04-19 08:40:53 +00:00
|
|
|
let state = self.state.borrow();
|
2017-09-13 14:40:43 +00:00
|
|
|
|
|
|
|
let nvim = state.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
|
|
|
nvim.ui_detach().expect("Error in ui_detach");
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn edit_paste(&self) {
|
2017-10-12 05:59:16 +00:00
|
|
|
self.state.borrow().edit_paste("+");
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn edit_save_all(&self) {
|
2017-04-19 08:40:53 +00:00
|
|
|
let state = self.state.borrow();
|
2017-09-13 14:40:43 +00:00
|
|
|
|
|
|
|
let nvim = state.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
2018-03-18 17:45:06 +00:00
|
|
|
nvim.command_async(":wa").cb(|r| r.report_err()).call();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2018-02-25 14:54:15 +00:00
|
|
|
pub fn new_tab(&self) {
|
|
|
|
let state = self.state.borrow();
|
|
|
|
|
|
|
|
let nvim = state.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
2018-03-18 17:45:06 +00:00
|
|
|
nvim.command_async(":tabe").cb(|r| r.report_err()).call();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
|
|
|
pub fn set_detach_cb<F>(&self, cb: Option<F>)
|
2017-08-16 09:41:22 +00:00
|
|
|
where
|
|
|
|
F: FnMut() + Send + 'static,
|
2017-06-04 10:14:09 +00:00
|
|
|
{
|
|
|
|
let mut state = self.state.borrow_mut();
|
|
|
|
state.set_detach_cb(cb);
|
|
|
|
}
|
2017-10-15 19:50:59 +00:00
|
|
|
|
|
|
|
pub fn set_nvim_started_cb<F>(&self, cb: Option<F>)
|
|
|
|
where
|
|
|
|
F: FnMut() + Send + 'static,
|
|
|
|
{
|
|
|
|
let mut state = self.state.borrow_mut();
|
|
|
|
state.set_nvim_started_cb(cb);
|
|
|
|
}
|
2018-03-30 20:54:37 +00:00
|
|
|
|
|
|
|
pub fn set_nvim_command_cb<F>(&self, cb: Option<F>)
|
|
|
|
where
|
|
|
|
F: FnMut(Vec<Value>) + Send + 'static,
|
|
|
|
{
|
|
|
|
let mut state = self.state.borrow_mut();
|
|
|
|
state.set_nvim_command_cb(cb);
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 16:50:25 +00:00
|
|
|
impl Deref for Shell {
|
|
|
|
type Target = gtk::Box;
|
2017-06-02 14:54:24 +00:00
|
|
|
|
2017-05-27 16:50:25 +00:00
|
|
|
fn deref(&self) -> >k::Box {
|
|
|
|
&self.widget
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_focus_in(state: &mut State) -> Inhibit {
|
2018-01-05 12:56:55 +00:00
|
|
|
if let Some(mut nvim) = state.nvim() {
|
|
|
|
nvim.command_async("if exists('#FocusGained') | doautocmd FocusGained | endif")
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
2017-12-10 18:09:36 +00:00
|
|
|
}
|
2017-11-12 08:04:55 +00:00
|
|
|
|
2017-06-02 14:54:24 +00:00
|
|
|
state.im_context.focus_in();
|
2017-04-12 10:12:05 +00:00
|
|
|
state.cursor.as_mut().unwrap().enter_focus();
|
|
|
|
let point = state.model.cur_point();
|
|
|
|
state.on_redraw(&RepaintMode::Area(point));
|
2017-03-31 09:49:14 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_focus_out(state: &mut State) -> Inhibit {
|
2018-01-05 12:56:55 +00:00
|
|
|
if let Some(mut nvim) = state.nvim() {
|
|
|
|
nvim.command_async("if exists('#FocusLost') | doautocmd FocusLost | endif")
|
|
|
|
.cb(|r| r.report_err())
|
|
|
|
.call();
|
2017-12-10 18:09:36 +00:00
|
|
|
}
|
2017-11-12 08:04:55 +00:00
|
|
|
|
2017-06-02 14:54:24 +00:00
|
|
|
state.im_context.focus_out();
|
2017-04-12 10:12:05 +00:00
|
|
|
state.cursor.as_mut().unwrap().leave_focus();
|
|
|
|
let point = state.model.cur_point();
|
|
|
|
state.on_redraw(&RepaintMode::Area(point));
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-03-31 09:49:14 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:21:43 +00:00
|
|
|
fn gtk_scroll_event(state: &mut State, ui_state: &mut UiState, ev: &EventScroll) -> Inhibit {
|
2017-04-15 17:07:09 +00:00
|
|
|
if !state.mouse_enabled {
|
2017-04-12 10:12:05 +00:00
|
|
|
return Inhibit(false);
|
|
|
|
}
|
2017-03-16 15:12:56 +00:00
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
state.close_popup_menu();
|
|
|
|
|
2017-12-31 09:47:50 +00:00
|
|
|
match ev.get_direction() {
|
|
|
|
gdk::ScrollDirection::Right => {
|
2017-04-22 15:00:06 +00:00
|
|
|
mouse_input(state, "ScrollWheelRight", ev.get_state(), ev.get_position())
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-12-31 09:47:50 +00:00
|
|
|
gdk::ScrollDirection::Left => {
|
2017-04-22 15:00:06 +00:00
|
|
|
mouse_input(state, "ScrollWheelLeft", ev.get_state(), ev.get_position())
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-12-31 09:47:50 +00:00
|
|
|
gdk::ScrollDirection::Up => {
|
2017-04-22 15:00:06 +00:00
|
|
|
mouse_input(state, "ScrollWheelUp", ev.get_state(), ev.get_position())
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-12-31 09:47:50 +00:00
|
|
|
gdk::ScrollDirection::Down => {
|
2017-04-22 15:00:06 +00:00
|
|
|
mouse_input(state, "ScrollWheelDown", ev.get_state(), ev.get_position())
|
2017-03-16 15:12:56 +00:00
|
|
|
}
|
2017-12-31 09:47:50 +00:00
|
|
|
gdk::ScrollDirection::Smooth => {
|
2017-12-06 17:21:43 +00:00
|
|
|
// Remember and accumulate scroll deltas, so slow scrolling still
|
|
|
|
// works.
|
|
|
|
ui_state.scroll_delta.0 += ev.as_ref().delta_x;
|
|
|
|
ui_state.scroll_delta.1 += ev.as_ref().delta_y;
|
|
|
|
// Perform scroll action for deltas with abs(delta) >= 1.
|
|
|
|
let x = ui_state.scroll_delta.0 as isize;
|
|
|
|
let y = ui_state.scroll_delta.1 as isize;
|
|
|
|
for _ in 0..x {
|
|
|
|
mouse_input(state, "ScrollWheelRight", ev.get_state(), ev.get_position())
|
|
|
|
}
|
|
|
|
for _ in 0..-x {
|
|
|
|
mouse_input(state, "ScrollWheelLeft", ev.get_state(), ev.get_position())
|
|
|
|
}
|
|
|
|
for _ in 0..y {
|
|
|
|
mouse_input(state, "ScrollWheelDown", ev.get_state(), ev.get_position())
|
|
|
|
}
|
|
|
|
for _ in 0..-y {
|
|
|
|
mouse_input(state, "ScrollWheelUp", ev.get_state(), ev.get_position())
|
|
|
|
}
|
|
|
|
// Subtract performed scroll deltas.
|
|
|
|
ui_state.scroll_delta.0 -= x as f64;
|
|
|
|
ui_state.scroll_delta.1 -= y as f64;
|
|
|
|
}
|
2017-12-31 09:47:50 +00:00
|
|
|
_ => (),
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-16 15:12:56 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_button_press(shell: &mut State, ui_state: &mut UiState, ev: &EventButton) -> Inhibit {
|
2017-03-15 15:33:47 +00:00
|
|
|
if ev.get_event_type() != EventType::ButtonPress {
|
|
|
|
return Inhibit(false);
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
if shell.mouse_enabled {
|
2017-04-15 17:07:09 +00:00
|
|
|
ui_state.mouse_pressed = true;
|
|
|
|
|
2017-10-13 08:43:07 +00:00
|
|
|
match ev.get_button() {
|
|
|
|
1 => mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position()),
|
2017-10-18 15:12:29 +00:00
|
|
|
2 => mouse_input(shell, "MiddleMouse", ev.get_state(), ev.get_position()),
|
2017-10-13 08:43:07 +00:00
|
|
|
3 => mouse_input(shell, "RightMouse", ev.get_state(), ev.get_position()),
|
|
|
|
_ => (),
|
2017-10-12 05:59:16 +00:00
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
fn mouse_input(shell: &mut State, input: &str, state: ModifierType, position: (f64, f64)) {
|
2017-09-05 15:23:46 +00:00
|
|
|
let &CellMetrics {
|
|
|
|
line_height,
|
|
|
|
char_width,
|
|
|
|
..
|
2017-11-23 14:57:39 +00:00
|
|
|
} = shell.render_state.borrow().font_ctx.cell_metrics();
|
2017-09-05 15:23:46 +00:00
|
|
|
let (x, y) = position;
|
|
|
|
let col = (x / char_width).trunc() as u64;
|
|
|
|
let row = (y / line_height).trunc() as u64;
|
|
|
|
let input_str = format!("{}<{},{}>", keyval_to_input_string(input, state), col, row);
|
2017-09-13 14:40:43 +00:00
|
|
|
|
|
|
|
let nvim = shell.nvim();
|
|
|
|
if let Some(mut nvim) = nvim {
|
2018-02-19 10:03:46 +00:00
|
|
|
nvim.input(&input_str)
|
|
|
|
.expect("Can't send mouse input event");
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-10-13 08:43:07 +00:00
|
|
|
fn gtk_button_release(shell: &mut State, ui_state: &mut UiState, ev: &EventButton) -> Inhibit {
|
2017-04-12 10:12:05 +00:00
|
|
|
ui_state.mouse_pressed = false;
|
2017-10-13 08:43:07 +00:00
|
|
|
|
|
|
|
if shell.mouse_enabled {
|
|
|
|
match ev.get_button() {
|
|
|
|
1 => mouse_input(shell, "LeftRelease", ev.get_state(), ev.get_position()),
|
|
|
|
2 => mouse_input(shell, "MiddleRelease", ev.get_state(), ev.get_position()),
|
|
|
|
3 => mouse_input(shell, "RightRelease", ev.get_state(), ev.get_position()),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion) -> Inhibit {
|
|
|
|
if shell.mouse_enabled && ui_state.mouse_pressed {
|
2017-04-22 15:00:06 +00:00
|
|
|
mouse_input(shell, "LeftDrag", ev.get_state(), ev.get_position());
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:01:51 +00:00
|
|
|
fn gtk_draw_double_buffer(state: &State, ctx: &cairo::Context) {
|
|
|
|
let (x1, y1, x2, y2) = ctx.clip_extents();
|
|
|
|
let surface = state.surface.as_ref().unwrap();
|
|
|
|
let buf_ctx = &surface.ctx;
|
|
|
|
|
|
|
|
surface.surface.flush();
|
|
|
|
|
|
|
|
buf_ctx.save();
|
|
|
|
buf_ctx.rectangle(x1, y1, x2 - x1, y2 - y1);
|
|
|
|
buf_ctx.clip();
|
|
|
|
|
|
|
|
let render_state = state.render_state.borrow();
|
|
|
|
render::clear(buf_ctx, &render_state.color_model);
|
|
|
|
render::render(
|
|
|
|
&buf_ctx,
|
|
|
|
state.cursor.as_ref().unwrap(),
|
|
|
|
&render_state.font_ctx,
|
|
|
|
&state.model,
|
|
|
|
&render_state.color_model,
|
|
|
|
);
|
2018-02-23 17:55:43 +00:00
|
|
|
|
2018-04-01 10:01:51 +00:00
|
|
|
ctx.set_source_surface(&surface.surface, 0.0, 0.0);
|
|
|
|
ctx.paint();
|
|
|
|
buf_ctx.restore();
|
|
|
|
}
|
2018-02-23 17:55:43 +00:00
|
|
|
|
2018-04-01 10:01:51 +00:00
|
|
|
fn gtk_draw_direct(state: &State, ctx: &cairo::Context) {
|
|
|
|
let render_state = state.render_state.borrow();
|
|
|
|
render::clear(ctx, &render_state.color_model);
|
|
|
|
render::render(
|
|
|
|
ctx,
|
|
|
|
state.cursor.as_ref().unwrap(),
|
|
|
|
&render_state.font_ctx,
|
|
|
|
&state.model,
|
|
|
|
&render_state.color_model,
|
|
|
|
);
|
|
|
|
}
|
2018-02-23 17:55:43 +00:00
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
|
2017-09-13 14:40:43 +00:00
|
|
|
let state = state_arc.borrow();
|
2017-11-10 15:36:54 +00:00
|
|
|
if state.nvim.is_initialized() {
|
2018-04-01 10:01:51 +00:00
|
|
|
if state.enable_double_buffer {
|
|
|
|
gtk_draw_double_buffer(&*state, ctx);
|
|
|
|
} else {
|
|
|
|
gtk_draw_direct(&*state, ctx);
|
|
|
|
}
|
2017-11-10 15:36:54 +00:00
|
|
|
} else if state.nvim.is_initializing() {
|
2017-08-25 15:32:30 +00:00
|
|
|
draw_initializing(&*state, ctx);
|
|
|
|
}
|
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-09-11 15:31:15 +00:00
|
|
|
fn show_nvim_start_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
|
2017-07-13 15:12:20 +00:00
|
|
|
let source = err.source();
|
|
|
|
let cmd = err.cmd().unwrap().to_owned();
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-07-13 15:12:20 +00:00
|
|
|
glib::idle_add(move || {
|
2017-08-16 09:41:22 +00:00
|
|
|
let state = state_arc.borrow();
|
2017-11-10 15:36:54 +00:00
|
|
|
state.nvim.set_error();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.error_area.show_nvim_start_error(&source, &cmd);
|
|
|
|
state.show_error_area();
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-08-16 09:41:22 +00:00
|
|
|
Continue(false)
|
|
|
|
});
|
2017-07-13 15:12:20 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 15:31:15 +00:00
|
|
|
fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
|
2017-07-13 15:12:20 +00:00
|
|
|
let source = err.source();
|
|
|
|
|
|
|
|
glib::idle_add(move || {
|
2017-08-16 09:41:22 +00:00
|
|
|
let state = state_arc.borrow();
|
2017-11-10 15:36:54 +00:00
|
|
|
state.nvim.set_error();
|
2017-08-16 09:41:22 +00:00
|
|
|
state.error_area.show_nvim_init_error(&source);
|
|
|
|
state.show_error_area();
|
2017-07-13 15:12:20 +00:00
|
|
|
|
2017-08-16 09:41:22 +00:00
|
|
|
Continue(false)
|
|
|
|
});
|
2017-07-13 15:12:20 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-08-16 09:41:22 +00:00
|
|
|
fn init_nvim_async(
|
|
|
|
state_arc: Arc<UiMutex<State>>,
|
2018-03-31 19:57:53 +00:00
|
|
|
nvim_handler: NvimHandler,
|
2017-08-16 09:41:22 +00:00
|
|
|
options: ShellOptions,
|
|
|
|
cols: usize,
|
|
|
|
rows: usize,
|
|
|
|
) {
|
2017-07-11 15:14:46 +00:00
|
|
|
// execute nvim
|
2017-12-10 09:46:36 +00:00
|
|
|
let nvim = match nvim::start(
|
2018-03-31 19:57:53 +00:00
|
|
|
nvim_handler,
|
2017-12-10 09:46:36 +00:00
|
|
|
options.nvim_bin_path.as_ref(),
|
|
|
|
options.timeout,
|
|
|
|
) {
|
2017-07-11 15:14:46 +00:00
|
|
|
Ok(nvim) => nvim,
|
|
|
|
Err(err) => {
|
2017-09-11 15:31:15 +00:00
|
|
|
show_nvim_start_error(&err, state_arc);
|
2017-07-11 15:14:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-11-10 15:36:54 +00:00
|
|
|
let nvim = set_nvim_to_state(state_arc.clone(), nvim);
|
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
// add callback on session end
|
2017-11-11 15:56:23 +00:00
|
|
|
let guard = nvim.borrow().unwrap().session.take_dispatch_guard();
|
2017-07-11 15:14:46 +00:00
|
|
|
let state_ref = state_arc.clone();
|
|
|
|
thread::spawn(move || {
|
2017-08-16 09:41:22 +00:00
|
|
|
guard.join().expect("Can't join dispatch thread");
|
2017-07-11 15:14:46 +00:00
|
|
|
|
2017-11-13 09:15:13 +00:00
|
|
|
glib::idle_add(move || {
|
|
|
|
state_ref.borrow().nvim.clear();
|
|
|
|
if let Some(ref cb) = state_ref.borrow().detach_cb {
|
|
|
|
(&mut *cb.borrow_mut())();
|
|
|
|
}
|
|
|
|
|
|
|
|
glib::Continue(false)
|
|
|
|
});
|
2017-08-16 09:41:22 +00:00
|
|
|
});
|
2017-07-11 15:14:46 +00:00
|
|
|
|
|
|
|
// attach ui
|
2018-03-20 17:43:31 +00:00
|
|
|
if let Err(err) = nvim::post_start_init(nvim, options.open_paths, cols as u64, rows as u64) {
|
2017-10-15 11:34:48 +00:00
|
|
|
show_nvim_init_error(&err, state_arc.clone());
|
|
|
|
} else {
|
2017-11-10 15:36:54 +00:00
|
|
|
set_nvim_initialized(state_arc);
|
2017-10-15 11:34:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 15:36:54 +00:00
|
|
|
fn set_nvim_to_state(state_arc: Arc<UiMutex<State>>, nvim: Neovim) -> NeovimClientAsync {
|
|
|
|
let pair = Arc::new((Mutex::new(None), Condvar::new()));
|
|
|
|
let pair2 = pair.clone();
|
2017-11-11 15:56:23 +00:00
|
|
|
let mut nvim = Some(nvim);
|
2017-11-10 15:36:54 +00:00
|
|
|
|
|
|
|
glib::idle_add(move || {
|
2017-11-11 15:56:23 +00:00
|
|
|
let nvim_aync = state_arc.borrow().nvim.set_nvim_async(nvim.take().unwrap());
|
2017-11-10 15:36:54 +00:00
|
|
|
|
|
|
|
let &(ref lock, ref cvar) = &*pair2;
|
|
|
|
let mut started = lock.lock().unwrap();
|
|
|
|
*started = Some(nvim_aync);
|
|
|
|
cvar.notify_one();
|
|
|
|
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
|
|
|
|
// Wait idle set nvim properly
|
|
|
|
let &(ref lock, ref cvar) = &*pair;
|
|
|
|
let mut started = lock.lock().unwrap();
|
|
|
|
while started.is_none() {
|
|
|
|
started = cvar.wait(started).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
started.take().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_nvim_initialized(state_arc: Arc<UiMutex<State>>) {
|
2017-11-06 19:05:11 +00:00
|
|
|
glib::idle_add(clone!(state_arc => move || {
|
2017-10-15 11:34:48 +00:00
|
|
|
let mut state = state_arc.borrow_mut();
|
2017-11-10 15:36:54 +00:00
|
|
|
state.nvim.async_to_sync();
|
|
|
|
state.nvim.set_initialized();
|
2018-02-17 13:57:57 +00:00
|
|
|
// in some case resize can happens while initilization in progress
|
|
|
|
// so force resize here
|
|
|
|
state.try_nvim_resize();
|
2017-10-15 11:34:48 +00:00
|
|
|
state.cursor.as_mut().unwrap().start();
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
Continue(false)
|
2017-11-06 19:05:11 +00:00
|
|
|
}));
|
2017-10-15 19:50:59 +00:00
|
|
|
|
|
|
|
idle_cb_call!(state_arc.nvim_started_cb());
|
2017-07-11 15:14:46 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-07-22 10:32:56 +00:00
|
|
|
fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = state.render_state.borrow();
|
|
|
|
let color_model = &render_state.color_model;
|
2017-12-31 09:47:50 +00:00
|
|
|
let layout = pangocairo::functions::create_layout(ctx).unwrap();
|
2017-07-22 10:32:56 +00:00
|
|
|
let alloc = state.drawing_area.get_allocation();
|
|
|
|
|
2017-08-31 15:37:55 +00:00
|
|
|
ctx.set_source_rgb(
|
2017-11-23 14:57:39 +00:00
|
|
|
color_model.bg_color.0,
|
|
|
|
color_model.bg_color.1,
|
|
|
|
color_model.bg_color.2,
|
2017-08-31 15:37:55 +00:00
|
|
|
);
|
2017-07-22 10:32:56 +00:00
|
|
|
ctx.paint();
|
|
|
|
|
2017-09-01 13:49:10 +00:00
|
|
|
layout.set_text("Loading->");
|
2017-07-22 10:32:56 +00:00
|
|
|
let (width, height) = layout.get_pixel_size();
|
|
|
|
|
|
|
|
let x = alloc.width as f64 / 2.0 - width as f64 / 2.0;
|
|
|
|
let y = alloc.height as f64 / 2.0 - height as f64 / 2.0;
|
|
|
|
|
|
|
|
ctx.move_to(x, y);
|
2017-08-31 15:37:55 +00:00
|
|
|
ctx.set_source_rgb(
|
2017-11-23 14:57:39 +00:00
|
|
|
color_model.fg_color.0,
|
|
|
|
color_model.fg_color.1,
|
|
|
|
color_model.fg_color.2,
|
2017-08-31 15:37:55 +00:00
|
|
|
);
|
2017-12-31 09:47:50 +00:00
|
|
|
pangocairo::functions::update_layout(ctx, &layout);
|
|
|
|
pangocairo::functions::show_layout(ctx, &layout);
|
2017-07-22 10:32:56 +00:00
|
|
|
|
|
|
|
ctx.move_to(x + width as f64, y);
|
2017-08-16 09:41:22 +00:00
|
|
|
state.cursor.as_ref().unwrap().draw(
|
|
|
|
ctx,
|
2017-11-23 14:57:39 +00:00
|
|
|
&render_state.font_ctx,
|
2017-08-16 09:41:22 +00:00
|
|
|
y,
|
|
|
|
false,
|
2017-11-23 14:57:39 +00:00
|
|
|
&color_model.bg_color,
|
2017-08-16 09:41:22 +00:00
|
|
|
);
|
2017-07-22 10:32:56 +00:00
|
|
|
}
|
|
|
|
|
2017-09-13 14:40:43 +00:00
|
|
|
fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
|
2017-10-11 15:55:29 +00:00
|
|
|
let mut state = state_ref.borrow_mut();
|
2017-10-11 15:59:23 +00:00
|
|
|
if state.start_nvim_initialization() {
|
2017-09-13 14:40:43 +00:00
|
|
|
let (cols, rows) = state.calc_nvim_size();
|
2018-02-17 10:07:06 +00:00
|
|
|
|
|
|
|
debug!("Init nvim {}/{}", cols, rows);
|
|
|
|
|
2017-10-11 15:55:29 +00:00
|
|
|
state.model = UiModel::new(rows as u64, cols as u64);
|
2017-09-13 14:40:43 +00:00
|
|
|
|
|
|
|
let state_arc = state_ref.clone();
|
2018-03-31 19:57:53 +00:00
|
|
|
let nvim_handler = NvimHandler::new(state_ref.clone());
|
2017-09-13 14:40:43 +00:00
|
|
|
let options = state.options.clone();
|
2018-03-31 19:57:53 +00:00
|
|
|
thread::spawn(move || init_nvim_async(state_arc, nvim_handler, options, cols, rows));
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
// Neovim redraw events
|
|
|
|
impl State {
|
|
|
|
pub fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
|
2017-08-22 08:59:06 +00:00
|
|
|
let repaint_area = self.model.set_cursor(row as usize, col as usize);
|
|
|
|
self.set_im_location();
|
|
|
|
RepaintMode::AreaList(repaint_area)
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_put(&mut self, text: String) -> RepaintMode {
|
2017-11-19 09:44:28 +00:00
|
|
|
let ch = text.chars().last().unwrap_or(' ');
|
|
|
|
let double_width = text.is_empty();
|
|
|
|
RepaintMode::Area(self.model.put(ch, double_width, self.cur_attrs.as_ref()))
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_clear(&mut self) -> RepaintMode {
|
2018-02-23 16:39:53 +00:00
|
|
|
debug!("clear model");
|
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
self.model.clear();
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::All
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_eol_clear(&mut self) -> RepaintMode {
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Area(self.model.eol_clear())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode {
|
2018-02-17 10:07:06 +00:00
|
|
|
debug!("on_resize {}/{}", columns, rows);
|
|
|
|
|
2018-01-02 20:05:32 +00:00
|
|
|
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
|
|
|
self.model = UiModel::new(rows, columns);
|
2017-09-14 12:08:45 +00:00
|
|
|
}
|
2017-11-14 15:38:24 +00:00
|
|
|
|
|
|
|
if let Some(mut nvim) = self.nvim.nvim() {
|
2017-11-23 14:57:39 +00:00
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
|
|
|
render_state.color_model.theme.update(&mut *nvim);
|
2017-11-14 15:38:24 +00:00
|
|
|
}
|
2017-09-09 19:44:28 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_redraw(&mut self, mode: &RepaintMode) {
|
2017-07-25 14:55:31 +00:00
|
|
|
match *mode {
|
2017-09-09 19:44:28 +00:00
|
|
|
RepaintMode::All => {
|
|
|
|
self.update_dirty_glyphs();
|
|
|
|
self.drawing_area.queue_draw();
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-07-25 14:55:31 +00:00
|
|
|
RepaintMode::Area(ref rect) => self.queue_draw_area(&[rect]),
|
|
|
|
RepaintMode::AreaList(ref list) => self.queue_draw_area(&list.list),
|
|
|
|
RepaintMode::Nothing => (),
|
2017-03-17 22:18:41 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_set_scroll_region(
|
|
|
|
&mut self,
|
|
|
|
top: u64,
|
|
|
|
bot: u64,
|
|
|
|
left: u64,
|
|
|
|
right: u64,
|
|
|
|
) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
self.model.set_scroll_region(top, bot, left, right);
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_scroll(&mut self, count: i64) -> RepaintMode {
|
2017-03-18 10:21:20 +00:00
|
|
|
RepaintMode::Area(self.model.scroll(count))
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_highlight_set(&mut self, attrs: HashMap<String, Value>) -> RepaintMode {
|
2017-11-17 15:40:09 +00:00
|
|
|
let model_attrs = Attrs::from_value_map(&attrs);
|
2017-03-15 15:33:47 +00:00
|
|
|
|
|
|
|
self.cur_attrs = Some(model_attrs);
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_update_bg(&mut self, bg: i64) -> RepaintMode {
|
2017-11-23 14:57:39 +00:00
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
2017-03-15 15:33:47 +00:00
|
|
|
if bg >= 0 {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.bg_color = Color::from_indexed_color(bg as u64);
|
2017-03-15 15:33:47 +00:00
|
|
|
} else {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.bg_color = COLOR_BLACK;
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_update_fg(&mut self, fg: i64) -> RepaintMode {
|
2017-11-23 14:57:39 +00:00
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
2017-03-15 15:33:47 +00:00
|
|
|
if fg >= 0 {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.fg_color = Color::from_indexed_color(fg as u64);
|
2017-03-15 15:33:47 +00:00
|
|
|
} else {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.fg_color = COLOR_WHITE;
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_update_sp(&mut self, sp: i64) -> RepaintMode {
|
2017-11-23 14:57:39 +00:00
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
2017-03-15 15:33:47 +00:00
|
|
|
if sp >= 0 {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.sp_color = Color::from_indexed_color(sp as u64);
|
2017-03-15 15:33:47 +00:00
|
|
|
} else {
|
2017-11-23 14:57:39 +00:00
|
|
|
render_state.color_model.sp_color = COLOR_RED;
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode {
|
2017-11-23 14:57:39 +00:00
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
|
|
|
render_state.mode.update(&mode, idx as usize);
|
2018-04-01 14:29:05 +00:00
|
|
|
self.cursor
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.set_mode_info(render_state.mode.mode_info().cloned());
|
|
|
|
self.cmd_line
|
|
|
|
.set_mode_info(render_state.mode.mode_info().cloned());
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Area(self.model.cur_point())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_mouse(&mut self, on: bool) -> RepaintMode {
|
2017-04-03 15:16:04 +00:00
|
|
|
self.mouse_enabled = on;
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn on_busy(&mut self, busy: bool) -> RepaintMode {
|
2017-04-03 15:16:04 +00:00
|
|
|
if busy {
|
2017-04-12 10:12:05 +00:00
|
|
|
self.cursor.as_mut().unwrap().busy_on();
|
2017-04-03 15:16:04 +00:00
|
|
|
} else {
|
2017-04-12 10:12:05 +00:00
|
|
|
self.cursor.as_mut().unwrap().busy_off();
|
2017-04-03 15:16:04 +00:00
|
|
|
}
|
|
|
|
RepaintMode::Area(self.model.cur_point())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn popupmenu_show(
|
2017-08-16 09:41:22 +00:00
|
|
|
&mut self,
|
2017-12-21 19:54:27 +00:00
|
|
|
menu: &[CompleteItem],
|
2017-08-16 09:41:22 +00:00
|
|
|
selected: i64,
|
|
|
|
row: u64,
|
|
|
|
col: u64,
|
|
|
|
) -> RepaintMode {
|
2017-09-05 15:23:46 +00:00
|
|
|
let point = ModelRect::point(col as usize, row as usize);
|
2017-11-23 14:57:39 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
|
|
|
let (x, y, width, height) = point.to_area(render_state.font_ctx.cell_metrics());
|
2017-09-05 15:23:46 +00:00
|
|
|
|
2017-11-18 12:56:37 +00:00
|
|
|
let context = popup_menu::PopupMenuContext {
|
|
|
|
nvim: &self.nvim,
|
2017-11-23 14:57:39 +00:00
|
|
|
color_model: &render_state.color_model,
|
|
|
|
font_ctx: &render_state.font_ctx,
|
2017-11-18 12:56:37 +00:00
|
|
|
menu_items: &menu,
|
2017-09-05 15:23:46 +00:00
|
|
|
selected,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
2017-11-23 14:57:39 +00:00
|
|
|
height,
|
2018-01-13 15:02:47 +00:00
|
|
|
max_width: self.max_popup_width(),
|
2017-11-18 12:56:37 +00:00
|
|
|
};
|
2017-09-05 15:23:46 +00:00
|
|
|
|
2017-11-18 12:56:37 +00:00
|
|
|
self.popup_menu.show(context);
|
2017-04-19 08:40:53 +00:00
|
|
|
|
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn popupmenu_hide(&mut self) -> RepaintMode {
|
2017-11-18 12:56:37 +00:00
|
|
|
self.popup_menu.hide();
|
2017-04-19 08:40:53 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
|
2017-11-18 12:56:37 +00:00
|
|
|
self.popup_menu.select(selected);
|
2017-04-19 08:40:53 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn tabline_update(
|
2017-08-16 09:41:22 +00:00
|
|
|
&mut self,
|
|
|
|
selected: Tabpage,
|
|
|
|
tabs: Vec<(Tabpage, Option<String>)>,
|
|
|
|
) -> RepaintMode {
|
2017-07-06 09:41:35 +00:00
|
|
|
self.tabs.update_tabs(&self.nvim, &selected, &tabs);
|
2017-05-27 16:50:25 +00:00
|
|
|
|
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn mode_info_set(
|
2017-08-16 09:41:22 +00:00
|
|
|
&mut self,
|
|
|
|
cursor_style_enabled: bool,
|
2018-04-01 14:29:05 +00:00
|
|
|
mode_infos: Vec<HashMap<String, Value>>,
|
2017-08-16 09:41:22 +00:00
|
|
|
) -> RepaintMode {
|
2018-04-01 14:29:05 +00:00
|
|
|
let mode_info_arr = mode_infos
|
|
|
|
.iter()
|
|
|
|
.map(|mode_info_map| mode::ModeInfo::new(mode_info_map))
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
match mode_info_arr {
|
|
|
|
Ok(mode_info_arr) => {
|
|
|
|
let mut render_state = self.render_state.borrow_mut();
|
|
|
|
render_state
|
|
|
|
.mode
|
|
|
|
.set_info(cursor_style_enabled, mode_info_arr);
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
error!("Error load mode info: {}", err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 16:50:25 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_show(
|
2017-11-17 15:40:09 +00:00
|
|
|
&mut self,
|
|
|
|
content: Vec<(HashMap<String, Value>, String)>,
|
|
|
|
pos: u64,
|
|
|
|
firstc: String,
|
|
|
|
prompt: String,
|
|
|
|
indent: u64,
|
|
|
|
level: u64,
|
|
|
|
) -> RepaintMode {
|
2017-09-13 14:40:43 +00:00
|
|
|
{
|
2018-02-23 11:16:20 +00:00
|
|
|
let cursor = self.model.cur_point();
|
|
|
|
let render_state = self.render_state.borrow();
|
|
|
|
let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics());
|
|
|
|
let ctx = CmdLineContext {
|
|
|
|
content,
|
|
|
|
pos,
|
|
|
|
firstc,
|
|
|
|
prompt,
|
|
|
|
indent,
|
|
|
|
level_idx: level,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
max_width: self.max_popup_width(),
|
|
|
|
};
|
2018-01-03 09:04:01 +00:00
|
|
|
|
2018-02-23 11:16:20 +00:00
|
|
|
self.cmd_line.show_level(&ctx);
|
2017-09-13 14:40:43 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2018-02-23 11:16:20 +00:00
|
|
|
self.on_busy(true)
|
2017-11-17 15:40:09 +00:00
|
|
|
}
|
2018-01-02 22:26:04 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_hide(&mut self, level: u64) -> RepaintMode {
|
2018-01-02 22:26:04 +00:00
|
|
|
self.cmd_line.hide_level(level);
|
2018-02-23 11:16:20 +00:00
|
|
|
self.on_busy(false)
|
2018-01-02 22:26:04 +00:00
|
|
|
}
|
2018-01-11 19:44:19 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_block_show(
|
2018-01-11 19:44:19 +00:00
|
|
|
&mut self,
|
|
|
|
content: Vec<Vec<(HashMap<String, Value>, String)>>,
|
|
|
|
) -> RepaintMode {
|
2018-01-13 15:02:47 +00:00
|
|
|
let max_width = self.max_popup_width();
|
|
|
|
self.cmd_line.show_block(&content, max_width);
|
2018-02-23 11:16:20 +00:00
|
|
|
self.on_busy(true)
|
2018-01-11 19:44:19 +00:00
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_block_append(
|
2018-01-11 19:44:19 +00:00
|
|
|
&mut self,
|
2018-01-21 11:14:07 +00:00
|
|
|
content: Vec<(HashMap<String, Value>, String)>,
|
2018-01-11 19:44:19 +00:00
|
|
|
) -> RepaintMode {
|
2018-01-13 15:02:47 +00:00
|
|
|
self.cmd_line.block_append(&content);
|
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_block_hide(&mut self) -> RepaintMode {
|
2018-01-13 15:02:47 +00:00
|
|
|
self.cmd_line.block_hide();
|
2018-02-23 11:16:20 +00:00
|
|
|
self.on_busy(false)
|
2018-01-11 19:44:19 +00:00
|
|
|
}
|
2018-02-10 09:51:47 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode {
|
2018-02-11 21:34:51 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
2018-02-23 11:16:20 +00:00
|
|
|
self.cmd_line.pos(&*render_state, pos, level);
|
2018-02-10 09:51:47 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2018-02-27 19:51:23 +00:00
|
|
|
|
2018-03-30 20:54:37 +00:00
|
|
|
pub fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode {
|
2018-02-27 19:51:23 +00:00
|
|
|
let render_state = self.render_state.borrow();
|
|
|
|
self.cmd_line.special_char(&*render_state, c, shift, level);
|
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-11-23 14:57:39 +00:00
|
|
|
impl CursorRedrawCb for State {
|
|
|
|
fn queue_redraw_cursor(&mut self) {
|
|
|
|
let cur_point = self.model.cur_point();
|
|
|
|
self.on_redraw(&RepaintMode::Area(cur_point));
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|