2017-05-27 16:50:25 +00:00
|
|
|
use std::cell::{RefMut, RefCell};
|
2017-04-12 10:12:05 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
use std::sync::Arc;
|
2017-05-27 16:50:25 +00:00
|
|
|
use std::ops::Deref;
|
2017-06-04 10:14:09 +00:00
|
|
|
use std::thread;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
|
|
|
use cairo;
|
2017-05-21 12:47:29 +00:00
|
|
|
use pangocairo::CairoContextExt;
|
2017-03-15 15:33:47 +00:00
|
|
|
use pango;
|
|
|
|
use pango::FontDescription;
|
2017-05-13 14:31:19 +00:00
|
|
|
use gdk::{ModifierType, EventConfigure, EventButton, EventMotion, EventType, EventScroll};
|
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-04-01 14:48:11 +00:00
|
|
|
use neovim_lib::{Neovim, NeovimApi, Value};
|
2017-05-27 16:50:25 +00:00
|
|
|
use neovim_lib::neovim_api::Tabpage;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
use settings::{Settings, FontSource};
|
2017-03-20 15:18:08 +00:00
|
|
|
use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED};
|
2017-04-12 10:12:05 +00:00
|
|
|
use nvim;
|
2017-07-06 09:41:35 +00:00
|
|
|
use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient};
|
2017-04-19 08:40:53 +00:00
|
|
|
use input;
|
|
|
|
use input::keyval_to_input_string;
|
2017-03-22 15:37:18 +00:00
|
|
|
use cursor::Cursor;
|
2017-04-12 10:12:05 +00:00
|
|
|
use ui::UiMutex;
|
2017-04-19 08:40:53 +00:00
|
|
|
use popup_menu::PopupMenu;
|
2017-05-27 16:50:25 +00:00
|
|
|
use tabline::Tabline;
|
2017-07-06 09:41:35 +00:00
|
|
|
use error;
|
2017-07-08 20:45:55 +00:00
|
|
|
use mode;
|
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";
|
|
|
|
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.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-04-12 10:12:05 +00:00
|
|
|
pub struct State {
|
2017-03-15 15:33:47 +00:00
|
|
|
pub model: UiModel,
|
|
|
|
bg_color: Color,
|
|
|
|
fg_color: Color,
|
|
|
|
sp_color: Color,
|
2017-04-12 10:12:05 +00:00
|
|
|
cur_attrs: Option<Attrs>,
|
2017-03-15 15:33:47 +00:00
|
|
|
mouse_enabled: bool,
|
2017-07-06 09:41:35 +00:00
|
|
|
nvim: Rc<RefCell<NeovimClient>>,
|
2017-03-15 15:33:47 +00:00
|
|
|
font_desc: FontDescription,
|
2017-04-12 10:12:05 +00:00
|
|
|
cursor: Option<Cursor>,
|
2017-05-25 14:50:56 +00:00
|
|
|
popup_menu: RefCell<PopupMenu>,
|
2017-04-12 10:12:05 +00:00
|
|
|
settings: Rc<RefCell<Settings>>,
|
|
|
|
|
2017-07-08 20:45:55 +00:00
|
|
|
pub mode: mode::Mode,
|
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
stack: gtk::Stack,
|
2017-05-27 16:50:25 +00:00
|
|
|
drawing_area: gtk::DrawingArea,
|
|
|
|
tabs: Tabline,
|
2017-07-06 09:41:35 +00:00
|
|
|
error_area: error::ErrorArea,
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
line_height: Option<f64>,
|
|
|
|
char_width: Option<f64>,
|
2017-05-16 09:01:32 +00:00
|
|
|
request_resize: bool,
|
2017-03-15 15:33:47 +00:00
|
|
|
resize_timer: Option<glib::SourceId>,
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
options: ShellOptions,
|
|
|
|
|
|
|
|
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
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-05-25 14:50:56 +00:00
|
|
|
let popup_menu = RefCell::new(PopupMenu::new(&drawing_area));
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
State {
|
2017-06-04 10:14:09 +00:00
|
|
|
model: UiModel::new(1, 1),
|
2017-07-06 09:41:35 +00:00
|
|
|
nvim: Rc::new(RefCell::new(NeovimClient::new())),
|
2017-03-15 15:33:47 +00:00
|
|
|
cur_attrs: None,
|
|
|
|
bg_color: COLOR_BLACK,
|
|
|
|
fg_color: COLOR_WHITE,
|
|
|
|
sp_color: COLOR_RED,
|
2017-03-16 15:12:56 +00:00
|
|
|
mouse_enabled: true,
|
2017-03-15 15:33:47 +00:00
|
|
|
font_desc: FontDescription::from_string(DEFAULT_FONT_NAME),
|
2017-04-12 10:12:05 +00:00
|
|
|
cursor: None,
|
2017-05-22 15:38:36 +00:00
|
|
|
popup_menu,
|
2017-04-12 10:12:05 +00:00
|
|
|
settings: settings,
|
|
|
|
|
2017-07-08 20:45:55 +00:00
|
|
|
mode: mode::Mode::new(),
|
|
|
|
|
|
|
|
// 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-07-06 09:41:35 +00:00
|
|
|
error_area: error::ErrorArea::new(),
|
2017-05-27 16:50:25 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
line_height: None,
|
|
|
|
char_width: None,
|
2017-03-15 15:33:47 +00:00
|
|
|
resize_timer: None,
|
2017-05-16 09:01:32 +00:00
|
|
|
request_resize: false,
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
options,
|
|
|
|
|
|
|
|
detach_cb: None,
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-25 14:50:56 +00:00
|
|
|
pub fn get_foreground(&self) -> &Color {
|
|
|
|
&self.fg_color
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_background(&self) -> &Color {
|
|
|
|
&self.bg_color
|
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
pub fn nvim(&self) -> RefMut<Neovim> {
|
2017-07-06 09:41:35 +00:00
|
|
|
RefMut::map(self.nvim.borrow_mut(), |n| n.nvim_mut())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
pub fn nvim_clone(&self) -> Rc<RefCell<NeovimClient>> {
|
|
|
|
self.nvim.clone()
|
2017-05-25 14:50:56 +00:00
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn set_detach_cb<F>(&mut self, cb: Option<F>)
|
|
|
|
where F: FnMut() + Send + 'static
|
|
|
|
{
|
|
|
|
if cb.is_some() {
|
|
|
|
self.detach_cb = Some(Box::new(RefCell::new(cb.unwrap())));
|
|
|
|
} else {
|
|
|
|
self.detach_cb = None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
fn create_pango_font(&self) -> FontDescription {
|
|
|
|
self.font_desc.clone()
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn colors<'a>(&'a self, cell: &'a Cell) -> (&'a Color, &'a Color) {
|
2017-03-15 15:33:47 +00:00
|
|
|
let bg = if let Some(ref bg) = cell.attrs.background {
|
|
|
|
bg
|
|
|
|
} else {
|
|
|
|
&self.bg_color
|
|
|
|
};
|
|
|
|
let fg = if let Some(ref fg) = cell.attrs.foreground {
|
|
|
|
fg
|
|
|
|
} else {
|
|
|
|
&self.fg_color
|
|
|
|
};
|
|
|
|
|
|
|
|
if cell.attrs.reverse {
|
|
|
|
(fg, bg)
|
|
|
|
} else {
|
|
|
|
(bg, fg)
|
|
|
|
}
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-05-25 14:50:56 +00:00
|
|
|
pub fn get_font_desc(&self) -> &FontDescription {
|
|
|
|
&self.font_desc
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub fn set_font_desc(&mut self, desc: &str) {
|
|
|
|
self.font_desc = FontDescription::from_string(desc);
|
|
|
|
self.line_height = None;
|
|
|
|
self.char_width = None;
|
|
|
|
}
|
|
|
|
|
2017-05-13 14:31:19 +00:00
|
|
|
pub fn open_file(&self, path: &str) {
|
|
|
|
let mut nvim = self.nvim();
|
2017-06-04 10:14:09 +00:00
|
|
|
nvim.command(&format!("e {}", path)).report_err(&mut *nvim);
|
2017-05-13 14:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cd(&self, path: &str) {
|
|
|
|
let mut nvim = self.nvim();
|
2017-06-04 10:14:09 +00:00
|
|
|
nvim.command(&format!("cd {}", path)).report_err(&mut *nvim);
|
2017-05-13 14:31:19 +00:00
|
|
|
}
|
|
|
|
|
2017-05-16 09:01:32 +00:00
|
|
|
fn request_resize(&mut self) {
|
|
|
|
self.request_resize = true;
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
|
|
|
fn close_popup_menu(&self) {
|
2017-05-25 14:50:56 +00:00
|
|
|
if self.popup_menu.borrow().is_open() {
|
2017-04-19 08:40:53 +00:00
|
|
|
let mut nvim = self.nvim();
|
|
|
|
nvim.input("<Esc>").report_err(&mut *nvim);
|
|
|
|
}
|
|
|
|
}
|
2017-04-22 11:31:06 +00:00
|
|
|
|
|
|
|
fn queue_draw_area<M: AsRef<ModelRect>>(&self, rect_list: &Vec<M>) {
|
|
|
|
match (&self.line_height, &self.char_width) {
|
|
|
|
(&Some(line_height), &Some(char_width)) => {
|
|
|
|
for rect in rect_list {
|
|
|
|
let mut rect = rect.as_ref().clone();
|
|
|
|
// this need to repain also line under curren line
|
|
|
|
// in case underscore or 'g' symbol is go here
|
2017-04-26 09:35:09 +00:00
|
|
|
// right one for italic symbol
|
2017-04-23 09:54:46 +00:00
|
|
|
rect.extend(0, 1, 0, 1);
|
2017-04-22 11:31:06 +00:00
|
|
|
let (x, y, width, height) = rect.to_area(line_height, char_width);
|
|
|
|
self.drawing_area.queue_draw_area(x, y, width, height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.drawing_area.queue_draw(),
|
|
|
|
}
|
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
|
|
|
fn calc_char_bounds(&self, ctx: &cairo::Context) -> (i32, i32) {
|
|
|
|
let layout = ctx.create_pango_layout();
|
|
|
|
|
|
|
|
let desc = self.create_pango_font();
|
|
|
|
layout.set_font_description(Some(&desc));
|
|
|
|
layout.set_text("A", -1);
|
|
|
|
|
|
|
|
layout.get_pixel_size()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_line_metrics(&mut self, ctx: &cairo::Context) {
|
|
|
|
if self.line_height.is_none() {
|
|
|
|
let (width, height) = self.calc_char_bounds(ctx);
|
|
|
|
self.line_height = Some(height as f64);
|
|
|
|
self.char_width = Some(width as f64);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_nvim_size(&self) -> Option<(usize, usize)> {
|
|
|
|
if let Some(line_height) = self.line_height {
|
|
|
|
if let Some(char_width) = self.char_width {
|
|
|
|
let alloc = self.drawing_area.get_allocation();
|
|
|
|
return Some(((alloc.width as f64 / char_width).trunc() as usize,
|
|
|
|
(alloc.height as f64 / line_height).trunc() as usize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
2017-07-12 15:32:10 +00:00
|
|
|
|
|
|
|
fn show_error_area(&self) {
|
|
|
|
let stack = self.stack.clone();
|
|
|
|
gtk::idle_add(move || {
|
|
|
|
stack.set_visible_child_name("Error");
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UiState {
|
|
|
|
mouse_pressed: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UiState {
|
|
|
|
pub fn new() -> UiState {
|
|
|
|
UiState { mouse_pressed: false }
|
|
|
|
}
|
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>,
|
|
|
|
open_path: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShellOptions {
|
|
|
|
pub fn new(nvim_bin_path: Option<String>, open_path: Option<String>) -> Self {
|
|
|
|
ShellOptions {
|
|
|
|
nvim_bin_path,
|
|
|
|
open_path,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
shell.state.borrow_mut().cursor = Some(Cursor::new(shell_ref));
|
|
|
|
|
|
|
|
shell
|
|
|
|
}
|
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
pub fn is_nvim_initialized(&self) -> bool {
|
|
|
|
let state = self.state.borrow();
|
|
|
|
let nvim = state.nvim.borrow();
|
|
|
|
nvim.is_initialized()
|
|
|
|
}
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
pub fn init(&mut self) {
|
2017-06-04 10:14:09 +00:00
|
|
|
let mut state = self.state.borrow_mut();
|
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-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
|
|
|
|
2017-04-12 10:12:05 +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)
|
|
|
|
.bits() as i32);
|
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
let ref_ui_state = self.ui_state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_button_press_event(move |_, ev| {
|
|
|
|
gtk_button_press(&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev)
|
|
|
|
});
|
|
|
|
|
|
|
|
let ref_ui_state = self.ui_state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_button_release_event(move |_, _| {
|
|
|
|
gtk_button_release(&mut *ref_ui_state.borrow_mut())
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
let ref_ui_state = self.ui_state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_motion_notify_event(move |_, ev| {
|
|
|
|
gtk_motion_notify(&mut *ref_state.borrow_mut(),
|
|
|
|
&mut *ref_ui_state.borrow_mut(),
|
|
|
|
ev)
|
|
|
|
});
|
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
2017-06-04 10:14:09 +00:00
|
|
|
.connect_draw(move |_, ctx| gtk_draw(&ref_state, ctx));
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
2017-04-19 08:40:53 +00:00
|
|
|
.connect_key_press_event(move |_, ev| {
|
|
|
|
let mut shell = ref_state.borrow_mut();
|
|
|
|
shell.cursor.as_mut().unwrap().reset_state();
|
|
|
|
let mut nvim = shell.nvim();
|
|
|
|
input::gtk_key_press(&mut *nvim, ev)
|
|
|
|
});
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
2017-04-15 17:12:40 +00:00
|
|
|
.connect_scroll_event(move |_, ev| gtk_scroll_event(&mut *ref_state.borrow_mut(), ev));
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_focus_in_event(move |_, _| gtk_focus_in(&mut *ref_state.borrow_mut()));
|
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_focus_out_event(move |_, _| gtk_focus_out(&mut *ref_state.borrow_mut()));
|
2017-06-04 10:14:09 +00:00
|
|
|
|
|
|
|
let ref_state = self.state.clone();
|
|
|
|
state
|
|
|
|
.drawing_area
|
|
|
|
.connect_configure_event(move |_, ev| gtk_configure_event(&ref_state, ev));
|
|
|
|
|
|
|
|
state.cursor.as_mut().unwrap().start();
|
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 redraw(&self, mode: &RepaintMode) {
|
|
|
|
self.state.borrow_mut().on_redraw(mode);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
state.nvim().ui_detach().expect("Error in ui_detach");
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn edit_paste(&self) {
|
2017-04-19 08:40:53 +00:00
|
|
|
let state = self.state.borrow();
|
2017-07-08 20:45:55 +00:00
|
|
|
let paste_command = if state.mode.is(&mode::NvimMode::Normal) {
|
2017-04-12 10:12:05 +00:00
|
|
|
"\"*p"
|
|
|
|
} else {
|
|
|
|
"<Esc>\"*pa"
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut nvim = state.nvim();
|
2017-04-19 08:40:53 +00:00
|
|
|
nvim.input(paste_command).report_err(&mut *nvim);
|
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();
|
|
|
|
let mut nvim = &mut *state.nvim();
|
2017-04-12 10:12:05 +00:00
|
|
|
nvim.command(":wa").report_err(nvim);
|
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
|
|
|
pub fn set_detach_cb<F>(&self, cb: Option<F>)
|
|
|
|
where F: FnMut() + Send + 'static
|
|
|
|
{
|
|
|
|
let mut state = self.state.borrow_mut();
|
|
|
|
state.set_detach_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-04 10:14:09 +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 {
|
|
|
|
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 {
|
|
|
|
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-04-15 17:12:40 +00:00
|
|
|
fn gtk_scroll_event(state: &mut State, 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-04-12 10:12:05 +00:00
|
|
|
match ev.as_ref().direction {
|
2017-05-13 14:31:19 +00:00
|
|
|
gdk_sys::GdkScrollDirection::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-05-13 14:31:19 +00:00
|
|
|
gdk_sys::GdkScrollDirection::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-05-13 14:31:19 +00:00
|
|
|
gdk_sys::GdkScrollDirection::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-05-13 14:31:19 +00:00
|
|
|
gdk_sys::GdkScrollDirection::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-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-04-22 15:00:06 +00:00
|
|
|
mouse_input(shell, "LeftMouse", ev.get_state(), ev.get_position());
|
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-03-15 15:33:47 +00:00
|
|
|
if let Some(line_height) = shell.line_height {
|
|
|
|
if let Some(char_width) = shell.char_width {
|
|
|
|
|
2017-04-19 08:40:53 +00:00
|
|
|
let mut nvim = shell.nvim();
|
2017-03-15 15:33:47 +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-04-12 10:12:05 +00:00
|
|
|
nvim.input(&input_str)
|
|
|
|
.expect("Can't send mouse input event");
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_button_release(ui_state: &mut UiState) -> Inhibit {
|
|
|
|
ui_state.mouse_pressed = false;
|
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)
|
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
#[inline]
|
|
|
|
fn update_line_metrics(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) {
|
|
|
|
let mut state = state_arc.borrow_mut();
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
if state.line_height.is_none() {
|
2017-06-04 10:14:09 +00:00
|
|
|
state.calc_line_metrics(ctx);
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
|
|
|
|
update_line_metrics(state_arc, ctx);
|
|
|
|
init_nvim(state_arc);
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-07-06 09:41:35 +00:00
|
|
|
let mut state = state_arc.borrow_mut();
|
|
|
|
// in case nvim not initialized
|
|
|
|
if !state.nvim.borrow().is_error() {
|
|
|
|
draw(&*state, ctx);
|
|
|
|
request_window_resize(&mut *state);
|
|
|
|
}
|
2017-03-31 10:22:05 +00:00
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
2017-07-13 15:12:20 +00:00
|
|
|
fn show_nvim_start_error(err: nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
|
|
|
|
let source = err.source();
|
|
|
|
let cmd = err.cmd().unwrap().to_owned();
|
|
|
|
|
|
|
|
glib::idle_add(move || {
|
|
|
|
let state = state_arc.borrow();
|
|
|
|
state.nvim.borrow_mut().set_error();
|
|
|
|
state.error_area.show_nvim_start_error(&source, &cmd);
|
|
|
|
state.show_error_area();
|
|
|
|
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn show_nvim_init_error(err: nvim::NvimInitError, state_arc: Arc<UiMutex<State>>) {
|
|
|
|
let source = err.source();
|
|
|
|
|
|
|
|
glib::idle_add(move || {
|
|
|
|
let state = state_arc.borrow();
|
|
|
|
state.nvim.borrow_mut().set_error();
|
|
|
|
state.error_area.show_nvim_init_error(&source);
|
|
|
|
state.show_error_area();
|
|
|
|
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
fn init_nvim_async(state_arc: Arc<UiMutex<State>>,
|
|
|
|
options: ShellOptions,
|
|
|
|
cols: usize,
|
|
|
|
rows: usize) {
|
|
|
|
// execute nvim
|
|
|
|
let mut nvim = match nvim::start(state_arc.clone(), options.nvim_bin_path.as_ref()) {
|
|
|
|
Ok(nvim) => nvim,
|
|
|
|
Err(err) => {
|
2017-07-13 15:12:20 +00:00
|
|
|
show_nvim_start_error(err, state_arc);
|
2017-07-11 15:14:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// add callback on session end
|
|
|
|
let guard = nvim.session.take_dispatch_guard();
|
|
|
|
let state_ref = state_arc.clone();
|
|
|
|
thread::spawn(move || {
|
|
|
|
guard.join().expect("Can't join dispatch thread");
|
|
|
|
|
|
|
|
idle_cb_call!(state_ref.detach_cb());
|
|
|
|
});
|
|
|
|
|
|
|
|
// attach ui
|
2017-07-12 15:32:10 +00:00
|
|
|
let mut nvim = Some(nvim);
|
|
|
|
glib::idle_add(move || {
|
|
|
|
let mut nvim = nvim.take().unwrap();
|
2017-07-11 15:14:46 +00:00
|
|
|
if let Err(err) = nvim::post_start_init(&mut nvim,
|
|
|
|
options.open_path.as_ref(),
|
|
|
|
cols as u64,
|
|
|
|
rows as u64) {
|
2017-07-13 15:12:20 +00:00
|
|
|
show_nvim_init_error(err, state_arc.clone());
|
2017-07-12 15:32:10 +00:00
|
|
|
} else {
|
|
|
|
let state = state_arc.borrow_mut();
|
|
|
|
state.nvim.borrow_mut().set_initialized(nvim);
|
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_nvim(state_arc: &Arc<UiMutex<State>>) {
|
|
|
|
let state = state_arc.borrow();
|
|
|
|
|
2017-07-12 15:32:10 +00:00
|
|
|
let mut nvim = state.nvim.borrow_mut();
|
|
|
|
if nvim.is_uninitialized() {
|
|
|
|
nvim.set_in_progress();
|
|
|
|
|
2017-07-11 15:14:46 +00:00
|
|
|
let (cols, rows) = state.calc_nvim_size().unwrap();
|
|
|
|
|
|
|
|
let state_arc = state_arc.clone();
|
|
|
|
let options = state.options.clone();
|
|
|
|
thread::spawn(move || { init_nvim_async(state_arc, options, cols, rows); });
|
2017-06-04 10:14:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-22 09:08:03 +00:00
|
|
|
#[inline]
|
2017-04-22 15:00:06 +00:00
|
|
|
fn get_model_clip(state: &State,
|
|
|
|
line_height: f64,
|
|
|
|
char_width: f64,
|
|
|
|
clip: (f64, f64, f64, f64))
|
|
|
|
-> ModelRect {
|
2017-04-22 09:08:03 +00:00
|
|
|
let mut model_clip =
|
|
|
|
ModelRect::from_area(line_height, char_width, clip.0, clip.1, clip.2, clip.3);
|
|
|
|
// in some cases symbols from previous row affect next row
|
|
|
|
// for example underscore symbol or 'g'
|
2017-04-23 09:54:46 +00:00
|
|
|
// also for italic text it is possible that symbol can affect next one
|
2017-04-22 09:08:03 +00:00
|
|
|
// see deference between logical rect and ink rect
|
2017-04-23 09:54:46 +00:00
|
|
|
model_clip.extend(1, 0, 1, 0);
|
2017-04-22 09:08:03 +00:00
|
|
|
state.model.limit_to_model(&mut model_clip);
|
|
|
|
|
|
|
|
model_clip
|
|
|
|
}
|
|
|
|
|
2017-04-22 09:33:36 +00:00
|
|
|
#[inline]
|
2017-04-22 15:00:06 +00:00
|
|
|
fn draw_backgound(state: &State,
|
2017-04-26 09:35:09 +00:00
|
|
|
draw_bitmap: &ModelBitamp,
|
2017-04-22 15:00:06 +00:00
|
|
|
ctx: &cairo::Context,
|
|
|
|
line_height: f64,
|
|
|
|
char_width: f64,
|
|
|
|
model_clip: &ModelRect) {
|
2017-03-20 15:18:08 +00:00
|
|
|
let line_x = model_clip.left as f64 * char_width;
|
|
|
|
let mut line_y: f64 = model_clip.top as f64 * line_height;
|
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
for (line_idx, line) in state.model.clip_model(model_clip) {
|
2017-03-20 15:18:08 +00:00
|
|
|
ctx.move_to(line_x, line_y);
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-03-21 10:10:55 +00:00
|
|
|
for (col_idx, cell) in line.iter() {
|
2017-04-26 09:35:09 +00:00
|
|
|
let current_point = ctx.get_current_point();
|
|
|
|
|
|
|
|
if !draw_bitmap.get(col_idx, line_idx) {
|
|
|
|
let (bg, _) = state.colors(cell);
|
2017-06-04 10:14:09 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
if &state.bg_color != bg {
|
|
|
|
ctx.set_source_rgb(bg.0, bg.1, bg.2);
|
|
|
|
ctx.rectangle(current_point.0, current_point.1, char_width, line_height);
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
ctx.move_to(current_point.0 + char_width, current_point.1);
|
|
|
|
}
|
2017-04-22 09:33:36 +00:00
|
|
|
line_y += line_height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(state: &State, ctx: &cairo::Context) {
|
2017-05-21 12:47:29 +00:00
|
|
|
let layout = ctx.create_pango_layout();
|
2017-04-22 15:00:06 +00:00
|
|
|
let mut desc = state.create_pango_font();
|
|
|
|
let mut buf = String::with_capacity(4);
|
|
|
|
|
|
|
|
let (row, col) = state.model.get_cursor();
|
2017-04-22 09:33:36 +00:00
|
|
|
|
|
|
|
let line_height = state.line_height.unwrap();
|
|
|
|
let char_width = state.char_width.unwrap();
|
2017-04-26 09:35:09 +00:00
|
|
|
let mut draw_bitmap = ModelBitamp::new(state.model.columns, state.model.rows);
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
ctx.set_source_rgb(state.bg_color.0, state.bg_color.1, state.bg_color.2);
|
|
|
|
ctx.paint();
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
let clip_rects = &ctx.copy_clip_rectangle_list().rectangles;
|
|
|
|
for clip_idx in 0..clip_rects.len() {
|
|
|
|
let clip = clip_rects.get(clip_idx).unwrap();
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
let model_clip =
|
|
|
|
get_model_clip(state,
|
|
|
|
line_height,
|
|
|
|
char_width,
|
|
|
|
(clip.x, clip.y, clip.x + clip.width, clip.y + clip.height));
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
let line_x = model_clip.left as f64 * char_width;
|
|
|
|
let mut line_y: f64 = model_clip.top as f64 * line_height;
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
draw_backgound(state,
|
|
|
|
&draw_bitmap,
|
|
|
|
ctx,
|
|
|
|
line_height,
|
|
|
|
char_width,
|
|
|
|
&model_clip);
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
for (line_idx, line) in state.model.clip_model(&model_clip) {
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
ctx.move_to(line_x, line_y);
|
2017-04-22 09:33:36 +00:00
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
for (col_idx, cell) in line.iter() {
|
|
|
|
let current_point = ctx.get_current_point();
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
if !draw_bitmap.get(col_idx, line_idx) {
|
|
|
|
let double_width = line.is_double_width(col_idx);
|
|
|
|
|
|
|
|
let (bg, fg) = state.colors(cell);
|
|
|
|
|
|
|
|
if row == line_idx && col == col_idx {
|
|
|
|
state
|
|
|
|
.cursor
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.draw(ctx,
|
|
|
|
state,
|
|
|
|
char_width,
|
|
|
|
line_height,
|
|
|
|
line_y,
|
|
|
|
double_width,
|
|
|
|
bg);
|
|
|
|
|
|
|
|
ctx.move_to(current_point.0, current_point.1);
|
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
|
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
if !cell.ch.is_whitespace() {
|
|
|
|
update_font_description(&mut desc, &cell.attrs);
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
layout.set_font_description(Some(&desc));
|
|
|
|
buf.clear();
|
|
|
|
buf.push(cell.ch);
|
|
|
|
layout.set_text(&buf, -1);
|
2017-04-22 15:00:06 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
// correct layout for double_width chars
|
|
|
|
if double_width {
|
|
|
|
let (dw_width, dw_height) = layout.get_pixel_size();
|
|
|
|
let x_offset = (char_width * 2.0 - dw_width as f64) / 2.0;
|
|
|
|
let y_offset = (line_height - dw_height as f64) / 2.0;
|
|
|
|
ctx.rel_move_to(x_offset, y_offset);
|
|
|
|
}
|
2017-04-22 15:00:06 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
ctx.set_source_rgb(fg.0, fg.1, fg.2);
|
2017-05-21 12:47:29 +00:00
|
|
|
ctx.update_pango_layout(&layout);
|
|
|
|
ctx.show_pango_layout(&layout);
|
2017-04-26 09:35:09 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-26 09:35:09 +00:00
|
|
|
if cell.attrs.underline || cell.attrs.undercurl {
|
|
|
|
// [TODO]: Current gtk-rs bindings does not provide fontmetrics access
|
|
|
|
// so it is not possible to find right position for underline or undercurl position
|
|
|
|
// > update_font_description(&mut desc, &cell.attrs);
|
|
|
|
// > layout.get_context().unwrap().get_metrics();
|
|
|
|
let top_offset = line_height * 0.9;
|
|
|
|
|
|
|
|
let sp = if let Some(ref sp) = cell.attrs.special {
|
|
|
|
sp
|
|
|
|
} else {
|
|
|
|
&state.sp_color
|
|
|
|
};
|
|
|
|
|
|
|
|
ctx.set_source_rgba(sp.0, sp.1, sp.2, 0.7);
|
|
|
|
if cell.attrs.undercurl {
|
|
|
|
ctx.set_dash(&[4.0, 2.0], 0.0);
|
|
|
|
ctx.set_line_width(2.0);
|
|
|
|
ctx.move_to(current_point.0, line_y + top_offset);
|
|
|
|
ctx.line_to(current_point.0 + char_width, line_y + top_offset);
|
|
|
|
ctx.stroke();
|
|
|
|
ctx.set_dash(&[], 0.0);
|
|
|
|
} else if cell.attrs.underline {
|
|
|
|
ctx.set_line_width(1.0);
|
|
|
|
ctx.move_to(current_point.0, line_y + top_offset);
|
|
|
|
ctx.line_to(current_point.0 + char_width, line_y + top_offset);
|
|
|
|
ctx.stroke();
|
|
|
|
}
|
2017-04-22 15:00:06 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-04-22 15:00:06 +00:00
|
|
|
|
|
|
|
ctx.move_to(current_point.0 + char_width, current_point.1);
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-22 15:00:06 +00:00
|
|
|
line_y += line_height;
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
2017-04-26 09:35:09 +00:00
|
|
|
|
|
|
|
draw_bitmap.fill_from_model(&model_clip);
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn update_font_description(desc: &mut FontDescription, attrs: &Attrs) {
|
|
|
|
desc.unset_fields(pango::FONT_MASK_STYLE | pango::FONT_MASK_WEIGHT);
|
|
|
|
if attrs.italic {
|
|
|
|
desc.set_style(pango::Style::Italic);
|
|
|
|
}
|
|
|
|
if attrs.bold {
|
|
|
|
desc.set_weight(pango::Weight::Bold);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
fn request_window_resize(state: &mut State) {
|
2017-05-16 09:01:32 +00:00
|
|
|
if !state.request_resize {
|
2017-03-30 15:25:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-04-12 10:12:05 +00:00
|
|
|
if state.resize_timer.is_some() {
|
2017-03-15 15:33:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-16 09:01:32 +00:00
|
|
|
state.request_resize = false;
|
2017-03-30 15:25:54 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
let width = state.drawing_area.get_allocated_width();
|
|
|
|
let height = state.drawing_area.get_allocated_height();
|
|
|
|
let request_height = (state.model.rows as f64 * state.line_height.unwrap()) as i32;
|
|
|
|
let request_width = (state.model.columns as f64 * state.char_width.unwrap()) as i32;
|
2017-03-15 15:33:47 +00:00
|
|
|
|
|
|
|
if width != request_width || height != request_height {
|
2017-06-04 10:14:09 +00:00
|
|
|
let window: gtk::Window = state
|
|
|
|
.drawing_area
|
|
|
|
.get_toplevel()
|
|
|
|
.unwrap()
|
|
|
|
.downcast()
|
|
|
|
.unwrap();
|
2017-04-12 10:12:05 +00:00
|
|
|
let (win_width, win_height) = window.get_size();
|
|
|
|
let h_border = win_width - width;
|
|
|
|
let v_border = win_height - height;
|
|
|
|
window.resize(request_width + h_border, request_height + v_border);
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn split_color(indexed_color: u64) -> Color {
|
|
|
|
let r = ((indexed_color >> 16) & 0xff) as f64;
|
|
|
|
let g = ((indexed_color >> 8) & 0xff) as f64;
|
|
|
|
let b = (indexed_color & 0xff) as f64;
|
|
|
|
Color(r / 255.0, g / 255.0, b / 255.0)
|
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
fn gtk_configure_event(state: &Arc<UiMutex<State>>, _: &EventConfigure) -> bool {
|
2017-04-12 10:12:05 +00:00
|
|
|
let mut state_ref = state.borrow_mut();
|
|
|
|
|
|
|
|
if let Some(timer) = state_ref.resize_timer {
|
|
|
|
glib::source_remove(timer);
|
|
|
|
}
|
2017-07-06 09:41:35 +00:00
|
|
|
|
|
|
|
if !state_ref.nvim.borrow().is_initialized() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
if let Some((columns, rows)) = state_ref.calc_nvim_size() {
|
|
|
|
let state = state.clone();
|
|
|
|
state_ref.resize_timer = Some(glib::timeout_add(250, move || {
|
|
|
|
let mut state_ref = state.borrow_mut();
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
state_ref.resize_timer = None;
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
if state_ref.model.rows != rows || state_ref.model.columns != columns {
|
|
|
|
if let Err(err) = state_ref.nvim().ui_try_resize(columns as u64, rows as u64) {
|
|
|
|
println!("Error trying resize nvim {}", err);
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-06-04 10:14:09 +00:00
|
|
|
}
|
|
|
|
Continue(false)
|
|
|
|
}));
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
impl RedrawEvents for State {
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
|
2017-04-22 19:18:59 +00:00
|
|
|
RepaintMode::AreaList(self.model.set_cursor(row as usize, col as usize))
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_put(&mut self, text: &str) -> RepaintMode {
|
|
|
|
RepaintMode::Area(self.model.put(text, self.cur_attrs.as_ref()))
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_clear(&mut self) -> RepaintMode {
|
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
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_eol_clear(&mut self) -> RepaintMode {
|
|
|
|
RepaintMode::Area(self.model.eol_clear())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
self.model = UiModel::new(rows, columns);
|
2017-05-16 09:01:32 +00:00
|
|
|
self.request_resize();
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::All
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-17 22:18:41 +00:00
|
|
|
fn on_redraw(&self, mode: &RepaintMode) {
|
|
|
|
match mode {
|
|
|
|
&RepaintMode::All => self.drawing_area.queue_draw(),
|
2017-04-22 11:31:06 +00:00
|
|
|
&RepaintMode::Area(ref rect) => self.queue_draw_area(&vec![rect]),
|
|
|
|
&RepaintMode::AreaList(ref list) => self.queue_draw_area(&list.list),
|
2017-03-18 09:45:05 +00:00
|
|
|
&RepaintMode::Nothing => (),
|
2017-03-17 22:18:41 +00:00
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_highlight_set(&mut self, attrs: &Vec<(Value, Value)>) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
let mut model_attrs = Attrs::new();
|
|
|
|
|
|
|
|
for &(ref key_val, ref val) in attrs {
|
2017-04-01 14:48:11 +00:00
|
|
|
if let Some(key) = key_val.as_str() {
|
|
|
|
match key {
|
2017-03-15 15:33:47 +00:00
|
|
|
"foreground" => {
|
2017-04-01 14:48:11 +00:00
|
|
|
if let Some(fg) = val.as_u64() {
|
2017-03-15 15:33:47 +00:00
|
|
|
model_attrs.foreground = Some(split_color(fg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"background" => {
|
2017-04-01 14:48:11 +00:00
|
|
|
if let Some(bg) = val.as_u64() {
|
2017-03-15 15:33:47 +00:00
|
|
|
model_attrs.background = Some(split_color(bg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"special" => {
|
2017-04-01 14:48:11 +00:00
|
|
|
if let Some(bg) = val.as_u64() {
|
2017-03-15 15:33:47 +00:00
|
|
|
model_attrs.special = Some(split_color(bg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"reverse" => model_attrs.reverse = true,
|
|
|
|
"bold" => model_attrs.bold = true,
|
|
|
|
"italic" => model_attrs.italic = true,
|
|
|
|
"underline" => model_attrs.underline = true,
|
|
|
|
"undercurl" => model_attrs.undercurl = true,
|
|
|
|
attr_key => println!("unknown attribute {}", attr_key),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
panic!("attr key must be string");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.cur_attrs = Some(model_attrs);
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_update_bg(&mut self, bg: i64) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
if bg >= 0 {
|
|
|
|
self.bg_color = split_color(bg as u64);
|
|
|
|
} else {
|
|
|
|
self.bg_color = COLOR_BLACK;
|
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_update_fg(&mut self, fg: i64) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
if fg >= 0 {
|
|
|
|
self.fg_color = split_color(fg as u64);
|
|
|
|
} else {
|
|
|
|
self.fg_color = COLOR_WHITE;
|
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 09:45:05 +00:00
|
|
|
fn on_update_sp(&mut self, sp: i64) -> RepaintMode {
|
2017-03-15 15:33:47 +00:00
|
|
|
if sp >= 0 {
|
|
|
|
self.sp_color = split_color(sp as u64);
|
|
|
|
} else {
|
|
|
|
self.sp_color = COLOR_RED;
|
|
|
|
}
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-07-08 20:45:55 +00:00
|
|
|
fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode {
|
|
|
|
self.mode.update(mode, idx as usize);
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Area(self.model.cur_point())
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 15:16:04 +00:00
|
|
|
fn on_mouse(&mut self, on: bool) -> RepaintMode {
|
|
|
|
self.mouse_enabled = on;
|
2017-03-18 09:45:05 +00:00
|
|
|
RepaintMode::Nothing
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-03 15:16:04 +00:00
|
|
|
fn on_busy(&mut self, busy: bool) -> RepaintMode {
|
|
|
|
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
|
|
|
|
|
|
|
fn popupmenu_show(&mut self,
|
|
|
|
menu: &Vec<Vec<&str>>,
|
|
|
|
selected: i64,
|
|
|
|
row: u64,
|
|
|
|
col: u64)
|
|
|
|
-> RepaintMode {
|
|
|
|
match (&self.line_height, &self.char_width) {
|
|
|
|
(&Some(line_height), &Some(char_width)) => {
|
2017-05-17 15:25:50 +00:00
|
|
|
let point = ModelRect::point(col as usize, row as usize);
|
|
|
|
let (x, y, width, height) = point.to_area(line_height, char_width);
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
self.popup_menu
|
|
|
|
.borrow_mut()
|
|
|
|
.show(&self, menu, selected, x, y, width, height);
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
};
|
|
|
|
|
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
fn popupmenu_hide(&mut self) -> RepaintMode {
|
2017-05-25 14:50:56 +00:00
|
|
|
self.popup_menu.borrow_mut().hide();
|
2017-04-19 08:40:53 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
|
|
|
|
|
|
|
fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
|
2017-05-25 14:50:56 +00:00
|
|
|
self.popup_menu.borrow().select(selected);
|
2017-04-19 08:40:53 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2017-05-27 16:50:25 +00:00
|
|
|
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
fn tabline_update(&mut self,
|
|
|
|
selected: Tabpage,
|
2017-07-08 11:02:56 +00:00
|
|
|
tabs: Vec<(Tabpage, Option<String>)>)
|
2017-06-04 10:14:09 +00:00
|
|
|
-> 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
|
|
|
|
}
|
2017-07-06 14:50:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
fn mode_info_set(&mut self,
|
|
|
|
cursor_style_enabled: bool,
|
2017-07-11 15:14:46 +00:00
|
|
|
mode_info: Vec<nvim::ModeInfo>)
|
|
|
|
-> RepaintMode {
|
2017-07-08 20:45:55 +00:00
|
|
|
self.mode.set_info(cursor_style_enabled, mode_info);
|
2017-07-06 14:50:55 +00:00
|
|
|
RepaintMode::Nothing
|
|
|
|
}
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
impl GuiApi for State {
|
2017-03-15 15:33:47 +00:00
|
|
|
fn set_font(&mut self, font_desc: &str) {
|
|
|
|
self.set_font_desc(font_desc);
|
2017-05-16 19:37:13 +00:00
|
|
|
self.request_resize();
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
let mut settings = self.settings.borrow_mut();
|
|
|
|
settings.set_font_source(FontSource::Rpc);
|
2017-03-15 15:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
2017-04-26 09:35:09 +00:00
|
|
|
|
|
|
|
pub struct ModelBitamp {
|
|
|
|
words_for_cols: usize,
|
|
|
|
model: Vec<u64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ModelBitamp {
|
|
|
|
pub fn new(cols: usize, rows: usize) -> ModelBitamp {
|
|
|
|
let words_for_cols = cols / 64 + 1;
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
ModelBitamp {
|
2017-04-26 09:35:09 +00:00
|
|
|
words_for_cols: words_for_cols,
|
|
|
|
model: vec![0; rows * words_for_cols],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fill_from_model(&mut self, rect: &ModelRect) {
|
|
|
|
for row in rect.top..rect.bot + 1 {
|
|
|
|
let row_pos = self.words_for_cols * row;
|
|
|
|
for col in rect.left..rect.right + 1 {
|
|
|
|
let col_pos = col / 64;
|
|
|
|
let col_offset = col % 64;
|
|
|
|
self.model[row_pos + col_pos] |= 1 << col_offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn get(&self, col: usize, row: usize) -> bool {
|
|
|
|
let row_pos = self.words_for_cols * row;
|
|
|
|
let col_pos = col / 64;
|
|
|
|
let col_offset = col % 64;
|
|
|
|
self.model[row_pos + col_pos] & (1 << col_offset) != 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_bitmap() {
|
|
|
|
let mut bitmap = ModelBitamp::new(80, 24);
|
|
|
|
bitmap.fill_from_model(&ModelRect::new(22, 22, 63, 68));
|
|
|
|
|
|
|
|
assert_eq!(true, bitmap.get(63, 22));
|
|
|
|
assert_eq!(true, bitmap.get(68, 22));
|
|
|
|
assert_eq!(false, bitmap.get(62, 22));
|
|
|
|
}
|
|
|
|
}
|