Refactoring for remove global variables.

This commit is contained in:
daa84 2017-04-12 13:12:05 +03:00
parent 057fad0e87
commit ade1f9a5de
8 changed files with 772 additions and 639 deletions

View File

@ -1,9 +1,10 @@
use cairo;
use ui_model::Color;
use ui::{SH, UiMutex};
use shell::{Shell, NvimMode};
use ui::UiMutex;
use shell;
use shell::NvimMode;
use nvim::{RepaintMode, RedrawEvents};
use std::sync::Arc;
use std::sync::{Arc, Weak};
use glib;
@ -43,15 +44,17 @@ enum AnimPhase {
struct State {
alpha: Alpha,
anim_phase: AnimPhase,
shell: Weak<UiMutex<shell::State>>,
timer: Option<glib::SourceId>,
}
impl State {
fn new() -> State {
fn new(shell: Weak<UiMutex<shell::State>>) -> State {
State {
alpha: Alpha(1.0),
anim_phase: AnimPhase::Shown,
shell: shell,
timer: None,
}
}
@ -71,8 +74,8 @@ pub struct Cursor {
}
impl Cursor {
pub fn new() -> Cursor {
Cursor { state: Arc::new(UiMutex::new(State::new())) }
pub fn new(shell: Weak<UiMutex<shell::State>>) -> Cursor {
Cursor { state: Arc::new(UiMutex::new(State::new(shell))) }
}
pub fn start(&mut self) {
@ -85,7 +88,7 @@ impl Cursor {
pub fn reset_state(&mut self) {
self.start();
}
pub fn enter_focus(&mut self) {
self.start();
}
@ -104,7 +107,7 @@ impl Cursor {
pub fn draw(&self,
ctx: &cairo::Context,
shell: &Shell,
shell: &shell::State,
char_width: f64,
line_height: f64,
line_y: f64,
@ -140,7 +143,6 @@ impl Cursor {
}
fn anim_step(state: &Arc<UiMutex<State>>) -> glib::Continue {
let moved_state = state.clone();
let mut mut_state = state.borrow_mut();
let next_event = match mut_state.anim_phase {
@ -175,13 +177,14 @@ fn anim_step(state: &Arc<UiMutex<State>>) -> glib::Continue {
AnimPhase::Busy => None,
};
SHELL!(&shell = {
let point = shell.model.cur_point();
shell.on_redraw(&RepaintMode::Area(point));
});
let shell = mut_state.shell.upgrade().unwrap();
let shell = shell.borrow();
let point = shell.model.cur_point();
shell.on_redraw(&RepaintMode::Area(point));
if let Some(timeout) = next_event {
let moved_state = state.clone();
mut_state.timer = Some(glib::timeout_add(timeout, move || anim_step(&moved_state)));
glib::Continue(false)

View File

@ -9,7 +9,7 @@ extern crate pango;
extern crate pangocairo;
extern crate neovim_lib;
extern crate phf;
#[macro_use]
#[macro_use]
extern crate log;
extern crate env_logger;
@ -23,12 +23,10 @@ mod settings;
mod cursor;
mod shell_dlg;
use std::thread;
use std::env;
use gio::ApplicationExt;
use shell::Shell;
use ui::SH;
use ui::Ui;
const BIN_PATH_ARG: &'static str = "--nvim-bin-path";
@ -41,8 +39,10 @@ fn main() {
app.connect_activate(activate);
let args: Vec<String> = env::args().collect();
let mut argv: Vec<&str> =
args.iter().filter(|a| !a.starts_with(BIN_PATH_ARG)).map(String::as_str).collect();
let mut argv: Vec<&str> = args.iter()
.filter(|a| !a.starts_with(BIN_PATH_ARG))
.map(String::as_str)
.collect();
if open_arg().is_some() {
argv.pop();
}
@ -50,24 +50,11 @@ fn main() {
}
fn activate(app: &gtk::Application) {
ui::UI.with(|ui_cell| {
let mut ui = ui_cell.borrow_mut();
if !ui.initialized {
ui.init(app);
let mut ui = Ui::new();
let path = nvim_bin_path(std::env::args());
SHELL!(shell = {
nvim::initialize(&mut shell, path.as_ref())
.expect("Can't start nvim instance");
guard_dispatch_thread(&mut shell);
});
}
SHELL!(shell = {
nvim::open_file(shell.nvim(), open_arg().as_ref());
});
});
ui.init(app,
nvim_bin_path(std::env::args()).as_ref(),
open_arg().as_ref());
}
fn nvim_bin_path<I>(args: I) -> Option<String>
@ -89,24 +76,13 @@ fn open_arg_impl<I>(args: I) -> Option<String>
args.skip(1)
.last()
.map(|a| if !a.starts_with("-") {
Some(a.to_owned())
} else {
None
})
Some(a.to_owned())
} else {
None
})
.unwrap_or(None)
}
fn guard_dispatch_thread(shell: &mut Shell) {
let guard = shell.nvim().session.take_dispatch_guard();
thread::spawn(move || {
guard.join().expect("Can't join dispatch thread");
glib::idle_add(move || {
ui::UI.with(|ui_cell| { ui_cell.borrow().close_window(); });
glib::Continue(false)
});
});
}
#[cfg(test)]
mod tests {
use super::*;
@ -115,8 +91,8 @@ mod tests {
fn test_bin_path_arg() {
assert_eq!(Some("/test_path".to_string()),
nvim_bin_path(vec!["neovim-gtk", "--nvim-bin-path=/test_path"]
.iter()
.map(|s| s.to_string())));
.iter()
.map(|s| s.to_string())));
}
#[test]
@ -125,8 +101,8 @@ mod tests {
open_arg_impl(vec!["neovim-gtk",
"--nvim-bin-path=/test_path",
"some_file.txt"]
.iter()
.map(|s| s.to_string())));
.iter()
.map(|s| s.to_string())));
}
#[test]

View File

@ -1,9 +1,11 @@
use neovim_lib::{Handler, Neovim, NeovimApi, Session, Value, UiAttachOptions, CallError};
use std::io::{Result, Error, ErrorKind};
use std::result;
use ui_model::{UiModel, ModelRect};
use ui::SH;
use shell::Shell;
use std::sync::Arc;
use ui::UiMutex;
use neovim_lib::{Handler, Neovim, NeovimApi, Session, Value, UiAttachOptions, CallError};
use ui_model::ModelRect;
use shell;
use glib;
pub trait RedrawEvents {
@ -63,98 +65,111 @@ macro_rules! try_uint {
})
}
pub fn initialize(ui: &mut Shell, nvim_bin_path: Option<&String>) -> Result<()> {
pub fn initialize(shell: Arc<UiMutex<shell::State>>,
nvim_bin_path: Option<&String>)
-> Result<Neovim> {
let session = if let Some(path) = nvim_bin_path {
Session::new_child_path(path)?
} else {
Session::new_child()?
};
let nvim = Neovim::new(session);
ui.set_nvim(nvim);
ui.model = UiModel::new(24, 80);
let mut nvim = Neovim::new(session);
let mut nvim = ui.nvim();
nvim.session
.start_event_loop_handler(NvimHandler::new(shell));
nvim.ui_attach(80, 24, UiAttachOptions::new())
.map_err(|e| Error::new(ErrorKind::Other, e))?;
nvim.command("runtime! ginit.vim")
.map_err(|e| Error::new(ErrorKind::Other, e))?;
nvim.session.start_event_loop_handler(NvimHandler::new());
nvim.ui_attach(80, 24, UiAttachOptions::new()).map_err(|e| Error::new(ErrorKind::Other, e))?;
nvim.command("runtime! ginit.vim").map_err(|e| Error::new(ErrorKind::Other, e))?;
Ok(())
Ok(nvim)
}
pub fn open_file(nvim: &mut NeovimApi, file: Option<&String>) {
if let Some(file_name) = file {
nvim.command(&format!("e {}", file_name)).report_err(nvim);
}
pub struct NvimHandler {
shell: Arc<UiMutex<shell::State>>,
}
pub struct NvimHandler {}
impl NvimHandler {
pub fn new() -> NvimHandler {
NvimHandler {}
pub fn new(shell: Arc<UiMutex<shell::State>>) -> NvimHandler {
NvimHandler { shell: shell }
}
fn nvim_cb(&self, method: &str, params: Vec<Value>) {
match method {
"redraw" => {
self.safe_call(move |ui| {
let mut repaint_mode = RepaintMode::Nothing;
for ev in &params {
if let Some(ev_args) = ev.as_array() {
if let Some(ev_name) = ev_args[0].as_str() {
for ref local_args in ev_args.iter().skip(1) {
let args = match *local_args {
&Value::Array(ref ar) => ar.clone(),
_ => vec![],
};
let call_reapint_mode = call(ui, ev_name, &args)?;
repaint_mode = repaint_mode.join(&call_reapint_mode);
}
} else {
println!("Unsupported event {:?}", ev_args);
}
} else {
println!("Unsupported event type {:?}", ev);
}
}
ui.on_redraw(&repaint_mode);
Ok(())
});
}
"Gui" => {
if params.len() > 0 {
if let Some(ev_name) = params[0].as_str().map(String::from) {
let args = params.iter().skip(1).cloned().collect();
self.safe_call(move |ui| {
call_gui_event(ui, &ev_name, &args)?;
ui.on_redraw(&RepaintMode::All);
Ok(())
});
} else {
println!("Unsupported event {:?}", params);
}
} else {
println!("Unsupported event {:?}", params);
}
}
_ => {
println!("Notification {}({:?})", method, params);
}
}
}
fn safe_call<F>(&self, cb: F)
where F: Fn(&mut shell::State) -> result::Result<(), String> + 'static + Send
{
let shell = self.shell.clone();
glib::idle_add(move || {
if let Err(msg) = cb(&mut shell.borrow_mut()) {
println!("Error call function: {}", msg);
}
glib::Continue(false)
});
}
}
impl Handler for NvimHandler {
fn handle_notify(&mut self, name: &str, args: &Vec<Value>) {
nvim_cb(name, args.clone());
self.nvim_cb(name, args.clone());
}
}
fn nvim_cb(method: &str, params: Vec<Value>) {
match method {
"redraw" => {
safe_call(move |ui| {
let mut repaint_mode = RepaintMode::Nothing;
for ev in &params {
if let Some(ev_args) = ev.as_array() {
if let Some(ev_name) = ev_args[0].as_str() {
for ref local_args in ev_args.iter().skip(1) {
let args = match *local_args {
&Value::Array(ref ar) => ar.clone(),
_ => vec![],
};
let call_reapint_mode = call(ui, ev_name, &args)?;
repaint_mode = repaint_mode.join(&call_reapint_mode);
}
} else {
println!("Unsupported event {:?}", ev_args);
}
} else {
println!("Unsupported event type {:?}", ev);
}
}
ui.on_redraw(&repaint_mode);
Ok(())
});
}
"Gui" => {
if params.len() > 0 {
if let Some(ev_name) = params[0].as_str().map(String::from) {
let args = params.iter().skip(1).cloned().collect();
safe_call(move |ui| {
call_gui_event(ui, &ev_name, &args)?;
ui.on_redraw(&RepaintMode::All);
Ok(())
});
} else {
println!("Unsupported event {:?}", params);
}
} else {
println!("Unsupported event {:?}", params);
}
}
_ => {
println!("Notification {}({:?})", method, params);
}
}
}
fn call_gui_event(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Result<(), String> {
fn call_gui_event(ui: &mut shell::State,
method: &str,
args: &Vec<Value>)
-> result::Result<(), String> {
match method {
"Font" => ui.set_font(try_str!(args[0])),
_ => return Err(format!("Unsupported event {}({:?})", method, args)),
@ -162,55 +177,45 @@ fn call_gui_event(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Re
Ok(())
}
fn call(ui: &mut Shell, method: &str, args: &Vec<Value>) -> result::Result<RepaintMode, String> {
fn call(ui: &mut shell::State,
method: &str,
args: &Vec<Value>)
-> result::Result<RepaintMode, String> {
Ok(match method {
"cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])),
"put" => ui.on_put(try_str!(args[0])),
"clear" => ui.on_clear(),
"resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])),
"highlight_set" => {
if let Value::Map(ref attrs) = args[0] {
ui.on_highlight_set(attrs);
} else {
panic!("Supports only map value as argument");
}
RepaintMode::Nothing
"cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])),
"put" => ui.on_put(try_str!(args[0])),
"clear" => ui.on_clear(),
"resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])),
"highlight_set" => {
if let Value::Map(ref attrs) = args[0] {
ui.on_highlight_set(attrs);
} else {
panic!("Supports only map value as argument");
}
"eol_clear" => ui.on_eol_clear(),
"set_scroll_region" => {
ui.on_set_scroll_region(try_uint!(args[0]),
try_uint!(args[1]),
try_uint!(args[2]),
try_uint!(args[3]));
RepaintMode::Nothing
}
"scroll" => ui.on_scroll(try_int!(args[0])),
"update_bg" => ui.on_update_bg(try_int!(args[0])),
"update_fg" => ui.on_update_fg(try_int!(args[0])),
"update_sp" => ui.on_update_sp(try_int!(args[0])),
"mode_change" => ui.on_mode_change(try_str!(args[0])),
"mouse_on" => ui.on_mouse(true),
"mouse_off" => ui.on_mouse(false),
"busy_start" => ui.on_busy(true),
"busy_stop" => ui.on_busy(false),
_ => {
println!("Event {}({:?})", method, args);
RepaintMode::Nothing
}
})
}
fn safe_call<F>(cb: F)
where F: Fn(&mut Shell) -> result::Result<(), String> + 'static + Send
{
glib::idle_add(move || {
SHELL!(shell = {
if let Err(msg) = cb(&mut shell) {
println!("Error call function: {}", msg);
}
});
glib::Continue(false)
});
RepaintMode::Nothing
}
"eol_clear" => ui.on_eol_clear(),
"set_scroll_region" => {
ui.on_set_scroll_region(try_uint!(args[0]),
try_uint!(args[1]),
try_uint!(args[2]),
try_uint!(args[3]));
RepaintMode::Nothing
}
"scroll" => ui.on_scroll(try_int!(args[0])),
"update_bg" => ui.on_update_bg(try_int!(args[0])),
"update_fg" => ui.on_update_fg(try_int!(args[0])),
"update_sp" => ui.on_update_sp(try_int!(args[0])),
"mode_change" => ui.on_mode_change(try_str!(args[0])),
"mouse_on" => ui.on_mouse(true),
"mouse_off" => ui.on_mouse(false),
"busy_start" => ui.on_busy(true),
"busy_stop" => ui.on_busy(false),
_ => {
println!("Event {}({:?})", method, args);
RepaintMode::Nothing
}
})
}
pub trait ErrorReport {

View File

@ -1,12 +1,9 @@
#[cfg(unix)]
use ui::{SET, SH};
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[cfg(unix)]
use nvim::RepaintMode;
#[cfg(unix)]
use nvim::RedrawEvents;
use shell::Shell;
#[cfg(unix)]
use gio;
@ -19,37 +16,27 @@ pub enum FontSource {
Default,
}
pub struct Settings {
struct State {
font_source: FontSource,
#[cfg(unix)]
gnome_interface_settings: gio::Settings,
}
impl Settings {
impl State {
#[cfg(unix)]
pub fn new() -> Settings {
Settings {
pub fn new() -> State {
State {
font_source: FontSource::Default,
gnome_interface_settings: gio::Settings::new("org.gnome.desktop.interface"),
}
}
#[cfg(target_os = "windows")]
pub fn new() -> Settings {
Settings { font_source: FontSource::Default }
pub fn new() -> State {
State { font_source: FontSource::Default }
}
#[cfg(unix)]
pub fn init(&mut self, shell: &mut Shell) {
self.gnome_interface_settings.connect_changed(|_, _| monospace_font_changed());
self.update_font(shell);
}
#[cfg(target_os = "windows")]
pub fn init(&mut self, _: &mut Shell) {}
#[cfg(unix)]
fn update_font(&mut self, shell: &mut Shell) {
// rpc is priority for font
@ -64,22 +51,49 @@ impl Settings {
self.font_source = FontSource::Gnome;
}
}
}
pub struct Settings {
shell: Option<Weak<RefCell<Shell>>>,
state: Rc<RefCell<State>>,
}
impl Settings {
pub fn new() -> Settings {
Settings {
shell: None,
state: Rc::new(RefCell::new(State::new())),
}
}
pub fn set_shell(&mut self, shell: Weak<RefCell<Shell>>) {
self.shell = Some(shell);
}
#[cfg(unix)]
pub fn init(&mut self) {
let shell = Weak::upgrade(self.shell.as_ref().unwrap()).unwrap();
let state = self.state.clone();
self.state.borrow_mut().update_font(&mut *shell.borrow_mut());
self.state
.borrow()
.gnome_interface_settings
.connect_changed(move |_, _| monospace_font_changed(&mut *shell.borrow_mut(), &mut *state.borrow_mut()));
}
#[cfg(target_os = "windows")]
pub fn init(&mut self) {}
pub fn set_font_source(&mut self, src: FontSource) {
self.font_source = src;
self.state.borrow_mut().font_source = src;
}
}
#[cfg(unix)]
fn monospace_font_changed() {
SET.with(|set_cell| {
let mut set = set_cell.borrow_mut();
// rpc is priority for font
if set.font_source != FontSource::Rpc {
SHELL!(shell = {
set.update_font(&mut shell);
shell.on_redraw(&RepaintMode::All);
});
}
});
fn monospace_font_changed(mut shell: &mut Shell, state: &mut State) {
// rpc is priority for font
if state.font_source != FontSource::Rpc {
state.update_font(&mut shell);
shell.redraw(&RepaintMode::All);
}
}

View File

@ -1,11 +1,14 @@
use std::string::String;
use std::cell::{Ref, RefMut, RefCell};
use std::rc::Rc;
use std::sync::Arc;
use cairo;
use pangocairo as pc;
use pango;
use pango::FontDescription;
use gdk::{ModifierType, EventKey, EventConfigure, EventButton, EventMotion, EventType, EventScroll,
ScrollDirection, EventFocus};
use gdk::{ModifierType, EventKey, EventConfigure, EventButton, EventMotion, EventType,
EventScroll, ScrollDirection};
use gdk_sys;
use glib;
use gtk::prelude::*;
@ -13,12 +16,14 @@ use gtk::DrawingArea;
use neovim_lib::{Neovim, NeovimApi, Value};
use settings;
use settings::{Settings, FontSource};
use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED};
use nvim::{RedrawEvents, GuiApi, RepaintMode};
use nvim;
use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport};
use input::{convert_key, keyval_to_input_string};
use ui::{UI, SH, SET};
use cursor::Cursor;
use ui;
use ui::UiMutex;
const DEFAULT_FONT_NAME: &'static str = "DejaVu Sans Mono 12";
@ -30,78 +35,49 @@ pub enum NvimMode {
Other,
}
pub struct Shell {
pub struct State {
pub model: UiModel,
pub drawing_area: DrawingArea,
nvim: Option<Neovim>,
cur_attrs: Option<Attrs>,
bg_color: Color,
fg_color: Color,
sp_color: Color,
cur_attrs: Option<Attrs>,
pub mode: NvimMode,
mouse_enabled: bool,
drawing_area: DrawingArea,
nvim: Option<Neovim>,
font_desc: FontDescription,
cursor: Option<Cursor>,
settings: Rc<RefCell<Settings>>,
line_height: Option<f64>,
char_width: Option<f64>,
request_width: bool,
pub mode: NvimMode,
mouse_enabled: bool,
mouse_pressed: bool,
font_desc: FontDescription,
resize_timer: Option<glib::SourceId>,
cursor: Cursor,
}
impl Shell {
pub fn new() -> Shell {
Shell {
model: UiModel::empty(),
impl State {
pub fn new(settings: Rc<RefCell<Settings>>) -> State {
State {
model: UiModel::new(24, 80),
drawing_area: DrawingArea::new(),
nvim: None,
cur_attrs: None,
bg_color: COLOR_BLACK,
fg_color: COLOR_WHITE,
sp_color: COLOR_RED,
line_height: None,
char_width: None,
request_width: true,
mode: NvimMode::Normal,
mouse_enabled: true,
mouse_pressed: false,
font_desc: FontDescription::from_string(DEFAULT_FONT_NAME),
cursor: None,
settings: settings,
line_height: None,
char_width: None,
resize_timer: None,
cursor: Cursor::new(),
request_width: true,
}
}
pub fn init(&mut self) {
self.drawing_area.set_size_request(500, 300);
self.drawing_area.set_hexpand(true);
self.drawing_area.set_vexpand(true);
self.drawing_area.set_can_focus(true);
self.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);
self.drawing_area.connect_button_press_event(gtk_button_press);
self.drawing_area.connect_button_release_event(gtk_button_release);
self.drawing_area.connect_motion_notify_event(gtk_motion_notify);
self.drawing_area.connect_draw(gtk_draw);
self.drawing_area.connect_key_press_event(gtk_key_press);
self.drawing_area.connect_scroll_event(gtk_scroll_event);
self.drawing_area.connect_focus_in_event(gtk_focus_in);
self.drawing_area.connect_focus_out_event(gtk_focus_out);
}
pub fn add_configure_event(&mut self) {
self.drawing_area.connect_configure_event(gtk_configure_event);
self.cursor.start();
}
pub fn set_nvim(&mut self, nvim: Neovim) {
self.nvim = Some(nvim);
}
pub fn nvim(&mut self) -> &mut Neovim {
self.nvim.as_mut().unwrap()
}
@ -110,17 +86,7 @@ impl Shell {
self.font_desc.clone()
}
fn request_width(&mut self) {
self.request_width = true;
}
pub fn set_font_desc(&mut self, desc: &str) {
self.font_desc = FontDescription::from_string(desc);
self.line_height = None;
self.char_width = None;
}
pub fn colors<'a>(&'a self, cell: &'a Cell) -> (&'a Color, &'a Color) {
fn colors<'a>(&'a self, cell: &'a Cell) -> (&'a Color, &'a Color) {
let bg = if let Some(ref bg) = cell.attrs.background {
bg
} else {
@ -138,142 +104,323 @@ impl Shell {
(bg, fg)
}
}
pub fn set_font_desc(&mut self, desc: &str) {
self.font_desc = FontDescription::from_string(desc);
self.line_height = None;
self.char_width = None;
}
fn request_width(&mut self) {
self.request_width = true;
}
}
fn gtk_focus_in(_: &DrawingArea, _: &EventFocus) -> Inhibit {
SHELL!(shell = {
shell.cursor.enter_focus();
let point = shell.model.cur_point();
shell.on_redraw(&RepaintMode::Area(point));
});
pub struct UiState {
mouse_pressed: bool,
}
impl UiState {
pub fn new() -> UiState {
UiState { mouse_pressed: false }
}
}
pub struct Shell {
state: Arc<UiMutex<State>>,
ui_state: Rc<RefCell<UiState>>,
}
impl Shell {
pub fn new(settings: Rc<RefCell<Settings>>) -> Shell {
let shell = Shell {
state: Arc::new(UiMutex::new(State::new(settings))),
ui_state: Rc::new(RefCell::new(UiState::new())),
};
let shell_ref = Arc::downgrade(&shell.state);
shell.state.borrow_mut().cursor = Some(Cursor::new(shell_ref));
shell
}
pub fn init(&mut self, parent: Arc<UiMutex<ui::Components>>) {
let state = self.state.borrow_mut();
state.drawing_area.set_size_request(500, 300);
state.drawing_area.set_hexpand(true);
state.drawing_area.set_vexpand(true);
state.drawing_area.set_can_focus(true);
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
.connect_draw(move |_, ctx| {
gtk_draw(&*parent.borrow(), &mut *ref_state.borrow_mut(), ctx)
});
let ref_state = self.state.clone();
state
.drawing_area
.connect_key_press_event(move |_, ev| gtk_key_press(&mut *ref_state.borrow_mut(), ev));
let ref_state = self.state.clone();
let ref_ui_state = self.ui_state.clone();
state
.drawing_area
.connect_scroll_event(move |_, ev| {
gtk_scroll_event(&mut *ref_state.borrow_mut(),
&mut *ref_ui_state.borrow_mut(),
ev)
});
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()));
}
pub fn state(&self) -> Ref<State> {
self.state.borrow()
}
pub fn drawing_area(&self) -> Ref<DrawingArea> {
Ref::map(self.state(), |s| &s.drawing_area)
}
pub fn redraw(&self, mode: &RepaintMode) {
self.state.borrow_mut().on_redraw(mode);
}
pub fn set_font_desc(&self, font_name: &str) {
self.state.borrow_mut().set_font_desc(font_name);
}
pub fn add_configure_event(&mut self) {
let mut state = self.state.borrow_mut();
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();
}
pub fn init_nvim(&mut self, nvim_bin_path: Option<&String>) {
let nvim =
nvim::initialize(self.state.clone(), nvim_bin_path).expect("Can't start nvim instance");
let mut state = self.state.borrow_mut();
state.nvim = Some(nvim);
state.request_width();
}
pub fn open_file(&self, path: &str) {
let mut nvim = self.nvim();
nvim.command(&format!("e {}", path))
.report_err(&mut *nvim);
}
pub fn detach_ui(&mut self) {
self.nvim().ui_detach().expect("Error in ui_detach");
}
pub fn edit_paste(&self) {
let mut state = self.state.borrow_mut();
let paste_command = if state.mode == NvimMode::Normal {
"\"*p"
} else {
"<Esc>\"*pa"
};
let mut nvim = state.nvim();
nvim.input(paste_command).report_err(nvim);
}
pub fn edit_save_all(&self) {
let mut nvim = &mut *self.nvim();
nvim.command(":wa").report_err(nvim);
}
pub fn nvim(&self) -> RefMut<Neovim> {
let state = self.state.borrow_mut();
RefMut::map(state, |s| s.nvim())
}
}
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));
Inhibit(false)
}
fn gtk_focus_out(_: &DrawingArea, _: &EventFocus) -> Inhibit {
SHELL!(shell = {
shell.cursor.leave_focus();
let point = shell.model.cur_point();
shell.on_redraw(&RepaintMode::Area(point));
});
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));
Inhibit(false)
}
fn gtk_scroll_event(_: &DrawingArea, ev: &EventScroll) -> Inhibit {
SHELL!(shell = {
if !shell.mouse_enabled {
return;
fn gtk_scroll_event(state: &mut State, ui_state: &mut UiState, ev: &EventScroll) -> Inhibit {
if state.mouse_enabled {
return Inhibit(false);
}
match ev.as_ref().direction {
ScrollDirection::Right => {
mouse_input(state,
ui_state,
"ScrollWheelRight",
ev.get_state(),
ev.get_position())
}
match ev.as_ref().direction {
ScrollDirection::Right => {
mouse_input(&mut shell,
"ScrollWheelRight",
ev.get_state(),
ev.get_position())
}
ScrollDirection::Left => {
mouse_input(&mut shell,
"ScrollWheelLeft",
ev.get_state(),
ev.get_position())
}
ScrollDirection::Up => {
mouse_input(&mut shell,
"ScrollWheelUp",
ev.get_state(),
ev.get_position())
}
ScrollDirection::Down => {
mouse_input(&mut shell,
"ScrollWheelDown",
ev.get_state(),
ev.get_position())
}
_ => (),
ScrollDirection::Left => {
mouse_input(state,
ui_state,
"ScrollWheelLeft",
ev.get_state(),
ev.get_position())
}
});
ScrollDirection::Up => {
mouse_input(state,
ui_state,
"ScrollWheelUp",
ev.get_state(),
ev.get_position())
}
ScrollDirection::Down => {
mouse_input(state,
ui_state,
"ScrollWheelDown",
ev.get_state(),
ev.get_position())
}
_ => (),
}
Inhibit(false)
}
fn gtk_button_press(_: &DrawingArea, ev: &EventButton) -> Inhibit {
fn gtk_button_press(shell: &mut State, ui_state: &mut UiState, ev: &EventButton) -> Inhibit {
if ev.get_event_type() != EventType::ButtonPress {
return Inhibit(false);
}
SHELL!(shell = {
if !shell.mouse_enabled {
return;
}
mouse_input(&mut shell, "LeftMouse", ev.get_state(), ev.get_position());
});
if shell.mouse_enabled {
mouse_input(shell,
ui_state,
"LeftMouse",
ev.get_state(),
ev.get_position());
}
Inhibit(false)
}
fn mouse_input(shell: &mut Shell, input: &str, state: ModifierType, position: (f64, f64)) {
fn mouse_input(shell: &mut State,
ui_state: &mut UiState,
input: &str,
state: ModifierType,
position: (f64, f64)) {
if let Some(line_height) = shell.line_height {
if let Some(char_width) = shell.char_width {
shell.mouse_pressed = true;
ui_state.mouse_pressed = true;
let nvim = shell.nvim();
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);
nvim.input(&input_str).expect("Can't send mouse input event");
nvim.input(&input_str)
.expect("Can't send mouse input event");
}
}
}
fn gtk_button_release(_: &DrawingArea, _: &EventButton) -> Inhibit {
SHELL!(shell = {
shell.mouse_pressed = false;
});
fn gtk_button_release(ui_state: &mut UiState) -> Inhibit {
ui_state.mouse_pressed = false;
Inhibit(false)
}
fn gtk_motion_notify(_: &DrawingArea, ev: &EventMotion) -> Inhibit {
SHELL!(shell = {
if !shell.mouse_enabled || !shell.mouse_pressed {
return;
}
mouse_input(&mut shell, "LeftDrag", ev.get_state(), ev.get_position());
});
fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion) -> Inhibit {
if shell.mouse_enabled && ui_state.mouse_pressed {
mouse_input(shell,
ui_state,
"LeftDrag",
ev.get_state(),
ev.get_position());
}
Inhibit(false)
}
fn gtk_key_press(_: &DrawingArea, ev: &EventKey) -> Inhibit {
fn gtk_key_press(shell: &mut State, ev: &EventKey) -> Inhibit {
if let Some(input) = convert_key(ev) {
SHELL!(shell = {
debug!("nvim_input -> {}", input);
shell.nvim().input(&input).expect("Error run input command to nvim");
shell.cursor.reset_state();
});
debug!("nvim_input -> {}", input);
shell
.nvim()
.input(&input)
.expect("Error run input command to nvim");
shell.cursor.as_mut().unwrap().reset_state();
Inhibit(true)
} else {
Inhibit(false)
}
}
fn gtk_draw(_: &DrawingArea, ctx: &cairo::Context) -> Inhibit {
SHELL!(shell = {
if shell.line_height.is_none() {
let (width, height) = calc_char_bounds(&shell, ctx);
shell.line_height = Some(height as f64);
shell.char_width = Some(width as f64);
}
fn gtk_draw(parent: &ui::Components, state: &mut State, ctx: &cairo::Context) -> Inhibit {
if state.line_height.is_none() {
let (width, height) = calc_char_bounds(state, ctx);
state.line_height = Some(height as f64);
state.char_width = Some(width as f64);
}
draw(&shell, ctx);
request_width(&mut shell);
});
draw(state, ctx);
request_width(parent, state);
Inhibit(false)
}
#[inline]
fn draw_joined_rect(shell: &Shell,
fn draw_joined_rect(state: &State,
ctx: &cairo::Context,
from_col_idx: usize,
col_idx: usize,
@ -283,7 +430,7 @@ fn draw_joined_rect(shell: &Shell,
let current_point = ctx.get_current_point();
let rect_width = char_width * (col_idx - from_col_idx) as f64;
if &shell.bg_color != color {
if &state.bg_color != color {
ctx.set_source_rgb(color.0, color.1, color.2);
ctx.rectangle(current_point.0, current_point.1, rect_width, line_height);
ctx.fill();
@ -292,29 +439,29 @@ fn draw_joined_rect(shell: &Shell,
ctx.move_to(current_point.0 + rect_width, current_point.1);
}
fn draw(shell: &Shell, ctx: &cairo::Context) {
ctx.set_source_rgb(shell.bg_color.0, shell.bg_color.1, shell.bg_color.2);
fn draw(state: &State, ctx: &cairo::Context) {
ctx.set_source_rgb(state.bg_color.0, state.bg_color.1, state.bg_color.2);
ctx.paint();
let line_height = shell.line_height.unwrap();
let char_width = shell.char_width.unwrap();
let line_height = state.line_height.unwrap();
let char_width = state.char_width.unwrap();
let clip = ctx.clip_extents();
let mut model_clip =
ModelRect::from_area(line_height, char_width, clip.0, clip.1, clip.2, clip.3);
shell.model.limit_to_model(&mut model_clip);
state.model.limit_to_model(&mut model_clip);
let line_x = model_clip.left as f64 * char_width;
let mut line_y: f64 = model_clip.top as f64 * line_height;
let (row, col) = shell.model.get_cursor();
let (row, col) = state.model.get_cursor();
let mut buf = String::with_capacity(4);
let layout = pc::create_layout(ctx);
let mut desc = shell.create_pango_font();
let mut desc = state.create_pango_font();
for (line_idx, line) in shell.model.clip_model(&model_clip) {
for (line_idx, line) in state.model.clip_model(&model_clip) {
ctx.move_to(line_x, line_y);
// first draw background
@ -323,13 +470,13 @@ fn draw(shell: &Shell, ctx: &cairo::Context) {
let mut from_col_idx = model_clip.left;
let mut from_bg = None;
for (col_idx, cell) in line.iter() {
let (bg, _) = shell.colors(cell);
let (bg, _) = state.colors(cell);
if from_bg.is_none() {
from_bg = Some(bg);
from_col_idx = col_idx;
} else if from_bg != Some(bg) {
draw_joined_rect(shell,
draw_joined_rect(state,
ctx,
from_col_idx,
col_idx,
@ -340,7 +487,7 @@ fn draw(shell: &Shell, ctx: &cairo::Context) {
from_col_idx = col_idx;
}
}
draw_joined_rect(shell,
draw_joined_rect(state,
ctx,
from_col_idx,
model_clip.right + 1,
@ -351,19 +498,25 @@ fn draw(shell: &Shell, ctx: &cairo::Context) {
ctx.move_to(line_x, line_y);
for (col_idx, cell) in line.iter() {
let double_width = line.get(col_idx + 1).map(|c| c.attrs.double_width).unwrap_or(false);
let double_width = line.get(col_idx + 1)
.map(|c| c.attrs.double_width)
.unwrap_or(false);
let current_point = ctx.get_current_point();
let (bg, fg) = shell.colors(cell);
let (bg, fg) = state.colors(cell);
if row == line_idx && col == col_idx {
shell.cursor.draw(ctx,
shell,
char_width,
line_height,
line_y,
double_width,
bg);
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);
}
@ -400,7 +553,7 @@ fn draw(shell: &Shell, ctx: &cairo::Context) {
let sp = if let Some(ref sp) = cell.attrs.special {
sp
} else {
&shell.sp_color
&state.sp_color
};
ctx.set_source_rgba(sp.0, sp.1, sp.2, 0.7);
@ -437,7 +590,7 @@ fn update_font_description(desc: &mut FontDescription, attrs: &Attrs) {
}
}
fn calc_char_bounds(shell: &Shell, ctx: &cairo::Context) -> (i32, i32) {
fn calc_char_bounds(shell: &State, ctx: &cairo::Context) -> (i32, i32) {
let layout = pc::create_layout(ctx);
let desc = shell.create_pango_font();
@ -447,31 +600,27 @@ fn calc_char_bounds(shell: &Shell, ctx: &cairo::Context) -> (i32, i32) {
layout.get_pixel_size()
}
fn request_width(shell: &mut Shell) {
if !shell.request_width {
fn request_width(parent: &ui::Components, state: &mut State) {
if !state.request_width {
return;
}
if shell.resize_timer.is_some() {
if state.resize_timer.is_some() {
return;
}
shell.request_width = false;
state.request_width = false;
let width = shell.drawing_area.get_allocated_width();
let height = shell.drawing_area.get_allocated_height();
let request_height = (shell.model.rows as f64 * shell.line_height.unwrap()) as i32;
let request_width = (shell.model.columns as f64 * shell.char_width.unwrap()) as i32;
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;
if width != request_width || height != request_height {
UI.with(|ui_cell| {
let ui = ui_cell.borrow();
let window = ui.window.as_ref().unwrap();
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);
});
let window = parent.window();
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);
}
}
@ -482,39 +631,41 @@ fn split_color(indexed_color: u64) -> Color {
Color(r / 255.0, g / 255.0, b / 255.0)
}
fn gtk_configure_event(_: &DrawingArea, ev: &EventConfigure) -> bool {
SHELL!(shell = {
let (width, height) = ev.get_size();
fn gtk_configure_event(state: &Arc<UiMutex<State>>, ev: &EventConfigure) -> bool {
let (width, height) = ev.get_size();
if let Some(timer) = shell.resize_timer {
glib::source_remove(timer);
let mut state_ref = state.borrow_mut();
if let Some(timer) = state_ref.resize_timer {
glib::source_remove(timer);
}
if let Some(line_height) = state_ref.line_height {
if let Some(char_width) = state_ref.char_width {
let state = state.clone();
state_ref.resize_timer = Some(glib::timeout_add(250, move || {
let mut state_ref = state.borrow_mut();
state_ref.resize_timer = None;
let rows = (height as f64 / line_height).trunc() as usize;
let columns = (width as f64 / char_width).trunc() as usize;
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);
}
}
state_ref.request_width();
Continue(false)
}));
}
if let Some(line_height) = shell.line_height {
if let Some(char_width) = shell.char_width {
shell.resize_timer = Some(glib::timeout_add(250, move || {
SHELL!(shell = {
shell.resize_timer = None;
let rows = (height as f64 / line_height).trunc() as usize;
let columns = (width as f64 / char_width).trunc() as usize;
if shell.model.rows != rows || shell.model.columns != columns {
if let Err(err) = shell.nvim()
.ui_try_resize(columns as u64, rows as u64) {
println!("Error trying resize nvim {}", err);
}
}
shell.request_width();
});
Continue(false)
}));
}
}
});
}
false
}
impl RedrawEvents for Shell {
impl RedrawEvents for State {
fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
RepaintMode::Area(self.model.set_cursor(row as usize, col as usize))
}
@ -643,21 +794,19 @@ impl RedrawEvents for Shell {
fn on_busy(&mut self, busy: bool) -> RepaintMode {
if busy {
self.cursor.busy_on();
self.cursor.as_mut().unwrap().busy_on();
} else {
self.cursor.busy_off();
self.cursor.as_mut().unwrap().busy_off();
}
RepaintMode::Area(self.model.cur_point())
}
}
impl GuiApi for Shell {
impl GuiApi for State {
fn set_font(&mut self, font_desc: &str) {
self.set_font_desc(font_desc);
SET.with(|settings| {
let mut settings = settings.borrow_mut();
settings.set_font_source(settings::FontSource::Rpc);
});
let mut settings = self.settings.borrow_mut();
settings.set_font_source(FontSource::Rpc);
}
}

View File

@ -1,14 +1,18 @@
use ui::{SH, Ui};
use std::cell::RefCell;
use ui::{Components, UiMutex};
use shell::Shell;
use neovim_lib::{NeovimApi, CallError, Value};
use gtk;
use gtk::prelude::*;
use gtk::{MessageDialog, MessageType, ButtonsType};
pub fn can_close_window(ui: &Ui) -> bool {
match get_changed_buffers() {
pub fn can_close_window(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> bool {
let shell = shell.borrow();
match get_changed_buffers(&*shell) {
Ok(vec) => {
if !vec.is_empty() {
show_not_saved_dlg(ui, &vec)
show_not_saved_dlg(comps, &*shell, &vec)
} else {
true
}
@ -20,14 +24,18 @@ pub fn can_close_window(ui: &Ui) -> bool {
}
}
fn show_not_saved_dlg(ui: &Ui, changed_bufs: &Vec<String>) -> bool {
let mut changed_files = changed_bufs.iter()
fn show_not_saved_dlg(comps: &UiMutex<Components>,
shell: &Shell,
changed_bufs: &Vec<String>)
-> bool {
let mut changed_files = changed_bufs
.iter()
.map(|n| if n.is_empty() { "<No name>" } else { n })
.fold(String::new(), |acc, v| acc + v + "\n");
changed_files.pop();
let flags = gtk::DIALOG_MODAL | gtk::DIALOG_DESTROY_WITH_PARENT;
let dlg = MessageDialog::new(ui.window.as_ref(),
let dlg = MessageDialog::new(Some(comps.borrow().window()),
flags,
MessageType::Question,
ButtonsType::None,
@ -37,21 +45,21 @@ fn show_not_saved_dlg(ui: &Ui, changed_bufs: &Vec<String>) -> bool {
const CLOSE_WITHOUT_SAVE: i32 = 1;
const CANCEL_ID: i32 = 2;
dlg.add_buttons(&[("_Yes", SAVE_ID), ("_No", CLOSE_WITHOUT_SAVE), ("_Cancel", CANCEL_ID)]);
dlg.add_buttons(&[("_Yes", SAVE_ID),
("_No", CLOSE_WITHOUT_SAVE),
("_Cancel", CANCEL_ID)]);
let res = match dlg.run() {
SAVE_ID => {
SHELL!(shell = {
let mut nvim = shell.nvim();
match nvim.command("wa") {
Err(ref err) => {
println!("Error: {}", err);
false
}
_ => true
let mut nvim = shell.nvim();
match nvim.command("wa") {
Err(ref err) => {
println!("Error: {}", err);
false
}
})
},
_ => true,
}
}
CLOSE_WITHOUT_SAVE => true,
CANCEL_ID => false,
_ => false,
@ -62,34 +70,33 @@ fn show_not_saved_dlg(ui: &Ui, changed_bufs: &Vec<String>) -> bool {
res
}
fn get_changed_buffers() -> Result<Vec<String>, CallError> {
SHELL!(shell = {
let mut nvim = shell.nvim();
let buffers = nvim.get_buffers().unwrap();
fn get_changed_buffers(shell: &Shell) -> Result<Vec<String>, CallError> {
let mut nvim = shell.nvim();
let buffers = nvim.get_buffers().unwrap();
Ok(buffers.iter()
.map(|buf| {
(match buf.get_option(&mut nvim, "modified") {
Ok(Value::Boolean(val)) => val,
Ok(_) => {
println!("Value must be boolean");
false
}
Err(ref err) => {
println!("Something going wrong while getting buffer option: {}", err);
false
}
},
match buf.get_name(&mut nvim) {
Ok(name) => name,
Err(ref err) => {
println!("Something going wrong while getting buffer name: {}", err);
"<Error>".to_owned()
}
})
})
.filter(|e| e.0)
.map(|e| e.1)
.collect())
})
Ok(buffers
.iter()
.map(|buf| {
(match buf.get_option(&mut nvim, "modified") {
Ok(Value::Boolean(val)) => val,
Ok(_) => {
println!("Value must be boolean");
false
}
Err(ref err) => {
println!("Something going wrong while getting buffer option: {}", err);
false
}
},
match buf.get_name(&mut nvim) {
Ok(name) => name,
Err(ref err) => {
println!("Something going wrong while getting buffer name: {}", err);
"<Error>".to_owned()
}
})
})
.filter(|e| e.0)
.map(|e| e.1)
.collect())
}

263
src/ui.rs
View File

@ -1,61 +1,36 @@
use std::cell::{RefCell, Ref, RefMut};
use std::thread;
use std::rc::Rc;
use std::sync::Arc;
use gtk;
use gtk_sys;
use gtk::prelude::*;
use gtk::{ApplicationWindow, HeaderBar, ToolButton, Image, AboutDialog};
use gdk::Event;
use gio::{Menu, MenuItem, SimpleAction};
use glib::variant::Variant;
use glib;
use neovim_lib::NeovimApi;
use settings;
use shell::{Shell, NvimMode};
use settings::Settings;
use shell::Shell;
use shell_dlg;
use nvim::ErrorReport;
macro_rules! ui_thread_var {
($id:ident, $ty:ty, $expr:expr) => (thread_local!(pub static $id: RefCell<$ty> = {
assert_ui_thread();
RefCell::new($expr)
});)
}
ui_thread_var!(UI, Ui, Ui::new());
ui_thread_var!(SH, Shell, Shell::new());
ui_thread_var!(SET, settings::Settings, settings::Settings::new());
#[macro_export]
macro_rules! SHELL {
(&$id:ident = $expr:expr) => (
SH.with(|shell_cell| {
let $id = &shell_cell.borrow();
$expr
});
);
($id:ident = $expr:expr) => (
SH.with(|shell_cell| {
let mut $id = &mut shell_cell.borrow_mut();
$expr
});
);
}
pub struct Ui {
pub initialized: bool,
pub window: Option<ApplicationWindow>,
initialized: bool,
comps: Arc<UiMutex<Components>>,
settings: Rc<RefCell<Settings>>,
shell: Rc<RefCell<Shell>>,
}
pub struct Components {
window: Option<ApplicationWindow>,
header_bar: HeaderBar,
}
impl Ui {
pub fn new() -> Ui {
Ui {
impl Components {
fn new() -> Components {
Components {
window: None,
header_bar: HeaderBar::new(),
initialized: false,
}
}
@ -63,14 +38,29 @@ impl Ui {
self.window.as_ref().unwrap().destroy();
}
pub fn destroy(&mut self) {
self.close_window();
SHELL!(shell = {
shell.nvim().ui_detach().expect("Error in ui_detach");
});
pub fn window(&self) -> &ApplicationWindow {
self.window.as_ref().unwrap()
}
}
impl Ui {
pub fn new() -> Ui {
let settings = Rc::new(RefCell::new(Settings::new()));
let shell = Rc::new(RefCell::new(Shell::new(settings.clone())));
settings.borrow_mut().set_shell(Rc::downgrade(&shell));
Ui {
initialized: false,
comps: Arc::new(UiMutex::new(Components::new())),
shell: shell.clone(),
settings: settings,
}
}
pub fn init(&mut self, app: &gtk::Application) {
pub fn init(&mut self,
app: &gtk::Application,
nvim_bin_path: Option<&String>,
open_path: Option<&String>) {
if self.initialized {
return;
}
@ -78,39 +68,66 @@ impl Ui {
self.create_main_menu(app);
SHELL!(shell = {
SET.with(|settings| {
let mut settings = settings.borrow_mut();
settings.init(&mut shell);
});
let mut settings = self.settings.borrow_mut();
settings.init();
self.header_bar.set_show_close_button(true);
let mut comps = self.comps.borrow_mut();
let save_image = Image::new_from_icon_name("document-save",
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
let save_btn = ToolButton::new(Some(&save_image), None);
save_btn.connect_clicked(|_| edit_save_all());
self.header_bar.pack_start(&save_btn);
comps.header_bar.set_show_close_button(true);
let paste_image = Image::new_from_icon_name("edit-paste",
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
let paste_btn = ToolButton::new(Some(&paste_image), None);
paste_btn.connect_clicked(|_| edit_paste());
self.header_bar.pack_start(&paste_btn);
let save_image = Image::new_from_icon_name("document-save",
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
let save_btn = ToolButton::new(Some(&save_image), None);
shell.init();
let shell = self.shell.clone();
save_btn.connect_clicked(move |_| { shell.borrow_mut().edit_save_all(); });
comps.header_bar.pack_start(&save_btn);
self.window = Some(ApplicationWindow::new(app));
let window = self.window.as_ref().unwrap();
let paste_image = Image::new_from_icon_name("edit-paste",
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
let paste_btn = ToolButton::new(Some(&paste_image), None);
let shell = self.shell.clone();
paste_btn.connect_clicked(move |_| { shell.borrow_mut().edit_paste(); });
comps.header_bar.pack_start(&paste_btn);
window.set_titlebar(Some(&self.header_bar));
window.add(&shell.drawing_area);
window.show_all();
window.connect_delete_event(gtk_delete);
window.set_title("Neovim-gtk");
self.shell.borrow_mut().init(self.comps.clone());
shell.add_configure_event();
});
comps.window = Some(ApplicationWindow::new(app));
let window = comps.window.as_ref().unwrap();
window.set_titlebar(Some(&comps.header_bar));
let mut shell = self.shell.borrow_mut();
window.add(&*shell.drawing_area());
window.show_all();
window.set_title("Neovim-gtk");
let comps_ref = self.comps.clone();
let shell_ref = self.shell.clone();
window.connect_delete_event(move |_, _| gtk_delete(&*comps_ref, &*shell_ref));
shell.add_configure_event();
shell.init_nvim(nvim_bin_path);
if open_path.is_some() {
shell.open_file(open_path.unwrap());
}
self.guard_dispatch_thread(&mut shell);
}
fn guard_dispatch_thread(&self, shell: &mut Shell) {
let guard = shell.nvim().session.take_dispatch_guard();
let comps = self.comps.clone();
thread::spawn(move || {
guard.join().expect("Can't join dispatch thread");
glib::idle_add(move || {
comps.borrow().close_window();
glib::Continue(false)
});
});
}
fn create_main_menu(&self, app: &gtk::Application) {
@ -128,96 +145,76 @@ impl Ui {
let about_action = SimpleAction::new("HelpAbout", None);
about_action.connect_activate(on_help_about);
let comps = self.comps.clone();
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
about_action.set_enabled(true);
app.add_action(&about_action);
}
}
fn on_help_about(_: &SimpleAction, _: &Option<Variant>) {
UI.with(|ui_cell| {
let ui = ui_cell.borrow();
fn on_help_about(comps: &Components) {
let about = AboutDialog::new();
about.set_transient_for(comps.window.as_ref());
about.set_program_name("NeovimGtk");
about.set_version(env!("CARGO_PKG_VERSION"));
about.set_logo(None);
about.set_authors(&[env!("CARGO_PKG_AUTHORS")]);
let about = AboutDialog::new();
about.set_transient_for(ui.window.as_ref());
about.set_program_name("NeovimGtk");
about.set_version(env!("CARGO_PKG_VERSION"));
about.set_logo(None);
about.set_authors(&[env!("CARGO_PKG_AUTHORS")]);
about.connect_response(|about, _| about.destroy());
about.show();
});
about.connect_response(|about, _| about.destroy());
about.show();
}
fn edit_paste() {
SHELL!(shell = {
let paste_command = if shell.mode == NvimMode::Normal {
"\"*p"
} else {
"<Esc>\"*pa"
};
let mut nvim = shell.nvim();
nvim.input(paste_command).report_err(nvim);
});
}
fn edit_save_all() {
SHELL!(shell = {
let mut nvim = shell.nvim();
nvim.command(":wa").report_err(nvim);
});
}
fn gtk_delete(_: &ApplicationWindow, _: &Event) -> Inhibit {
Inhibit(UI.with(|ui_cell| {
let mut ui = ui_cell.borrow_mut();
if shell_dlg::can_close_window(&ui) {
ui.destroy();
false
} else {
true
}
}))
fn gtk_delete(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> Inhibit {
Inhibit(if shell_dlg::can_close_window(comps, shell) {
let comps = comps.borrow();
comps.close_window();
shell.borrow_mut().detach_ui();
false
} else {
true
})
}
pub struct UiMutex<T: ?Sized> {
thread: String,
data: RefCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for UiMutex<T> {}
unsafe impl<T: ?Sized + Send> Sync for UiMutex<T> {}
unsafe impl<T: ?Sized> Send for UiMutex<T> {}
unsafe impl<T: ?Sized> Sync for UiMutex<T> {}
impl<T> UiMutex<T> {
pub fn new(t: T) -> UiMutex<T> {
UiMutex { data: RefCell::new(t) }
UiMutex {
thread: thread::current()
.name()
.expect("Can create UI only from main thread, current thiread has no name")
.to_owned(),
data: RefCell::new(t),
}
}
}
impl<T: ?Sized> UiMutex<T> {
pub fn borrow(&self) -> Ref<T> {
assert_ui_thread();
self.assert_ui_thread();
self.data.borrow()
}
pub fn borrow_mut(&self) -> RefMut<T> {
assert_ui_thread();
self.assert_ui_thread();
self.data.borrow_mut()
}
}
#[inline]
fn assert_ui_thread() {
match thread::current().name() {
Some("main") => (),
Some(ref name) => {
panic!("Can create UI only from main thread, {}", name);
#[inline]
fn assert_ui_thread(&self) {
match thread::current().name() {
Some(name) if name == self.thread => (),
Some(name) => {
panic!("Can create UI only from main thread, {}", name);
}
None => panic!("Can create UI only from main thread, current thread has no name"),
}
None => panic!("Can create UI only from main thread, current thiread has no name"),
}
}

View File

@ -81,20 +81,6 @@ pub struct UiModel {
}
impl UiModel {
pub fn empty() -> UiModel {
UiModel {
columns: 0,
rows: 0,
cur_row: 0,
cur_col: 0,
model: vec![],
top: 0,
bot: 0,
left: 0,
right: 0,
}
}
pub fn new(rows: u64, columns: u64) -> UiModel {
let mut model = Vec::with_capacity(rows as usize);
for i in 0..rows as usize {
@ -314,21 +300,13 @@ impl ModelRect {
} else {
x1
};
let x2 = if x2 > 0.0 {
x2 - 1.0
} else {
x2
};
let x2 = if x2 > 0.0 { x2 - 1.0 } else { x2 };
let y1 = if y1 > 0.0 {
y1 // - 1.0
} else {
y1
};
let y2 = if y2 > 0.0 {
y2 - 1.0
} else {
y2
};
let y2 = if y2 > 0.0 { y2 - 1.0 } else { y2 };
let left = (x1 / char_width) as usize;
let right = (x2 / char_width) as usize;
let top = (y1 / line_height) as usize;
@ -359,7 +337,9 @@ impl<'a> Iterator for ClipRowIterator<'a> {
fn next(&mut self) -> Option<(usize, ClipLine<'a>)> {
self.pos += 1;
self.iter.next().map(|line| (self.rect.top + self.pos - 1, ClipLine::new(line, self.rect)))
self.iter
.next()
.map(|line| (self.rect.top + self.pos - 1, ClipLine::new(line, self.rect)))
}
}
@ -406,7 +386,9 @@ impl<'a> Iterator for ClipColIterator<'a> {
fn next(&mut self) -> Option<(usize, &'a Cell)> {
self.pos += 1;
self.iter.next().map(|line| (self.rect.left + self.pos - 1, line))
self.iter
.next()
.map(|line| (self.rect.left + self.pos - 1, line))
}
}