Refactoring for remove global variables.
This commit is contained in:
parent
057fad0e87
commit
ade1f9a5de
@ -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)
|
||||
|
60
src/main.rs
60
src/main.rs
@ -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: >k::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]
|
||||
|
247
src/nvim.rs
247
src/nvim.rs
@ -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 ¶ms {
|
||||
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 ¶ms {
|
||||
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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
597
src/shell.rs
597
src/shell.rs
@ -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);
|
||||
}
|
||||
}
|
||||
|
101
src/shell_dlg.rs
101
src/shell_dlg.rs
@ -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
263
src/ui.rs
@ -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: >k::Application) {
|
||||
pub fn init(&mut self,
|
||||
app: >k::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: >k::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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user