Merge branch 'master' into ext_cmdline
This commit is contained in:
60
src/main.rs
60
src/main.rs
@@ -1,30 +1,30 @@
|
||||
extern crate gtk;
|
||||
extern crate gtk_sys;
|
||||
extern crate gio;
|
||||
extern crate cairo;
|
||||
extern crate env_logger;
|
||||
extern crate gdk;
|
||||
extern crate gdk_sys;
|
||||
extern crate gio;
|
||||
#[macro_use]
|
||||
extern crate glib;
|
||||
extern crate glib_sys as glib_ffi;
|
||||
extern crate gobject_sys as gobject_ffi;
|
||||
extern crate cairo;
|
||||
extern crate pango;
|
||||
extern crate pango_sys;
|
||||
extern crate pangocairo;
|
||||
extern crate pango_cairo_sys;
|
||||
extern crate neovim_lib;
|
||||
extern crate phf;
|
||||
extern crate gtk;
|
||||
extern crate gtk_sys;
|
||||
extern crate htmlescape;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
extern crate htmlescape;
|
||||
extern crate neovim_lib;
|
||||
extern crate pango;
|
||||
extern crate pango_cairo_sys;
|
||||
extern crate pango_sys;
|
||||
extern crate pangocairo;
|
||||
extern crate phf;
|
||||
extern crate rmpv;
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate toml;
|
||||
extern crate serde_json;
|
||||
extern crate toml;
|
||||
|
||||
mod sys;
|
||||
|
||||
@@ -51,7 +51,6 @@ mod project;
|
||||
mod tabline;
|
||||
mod error;
|
||||
|
||||
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
use std::str::FromStr;
|
||||
@@ -63,12 +62,15 @@ use shell::ShellOptions;
|
||||
|
||||
const BIN_PATH_ARG: &str = "--nvim-bin-path";
|
||||
const TIMEOUT_ARG: &str = "--timeout";
|
||||
const DISABLE_WIN_STATE_RESTORE: &str = "--disable-win-restore";
|
||||
|
||||
fn main() {
|
||||
env_logger::init().expect("Can't initialize env_logger");
|
||||
env_logger::init();
|
||||
|
||||
let app_flags = gio::ApplicationFlags::HANDLES_OPEN | gio::ApplicationFlags::NON_UNIQUE;
|
||||
|
||||
glib::set_program_name(Some("NeovimGtk"));
|
||||
|
||||
let app = if cfg!(debug_assertions) {
|
||||
gtk::Application::new(Some("org.daa.NeovimGtkDebug"), app_flags)
|
||||
} else {
|
||||
@@ -89,6 +91,7 @@ fn main() {
|
||||
let argv: Vec<String> = args.iter()
|
||||
.filter(|a| !a.starts_with(BIN_PATH_ARG))
|
||||
.filter(|a| !a.starts_with(TIMEOUT_ARG))
|
||||
.filter(|a| !a.starts_with(DISABLE_WIN_STATE_RESTORE))
|
||||
.cloned()
|
||||
.collect();
|
||||
app.run(&argv);
|
||||
@@ -102,7 +105,7 @@ fn open(app: >k::Application, files: &[gio::File], _: &str) {
|
||||
nvim_timeout(std::env::args()),
|
||||
));
|
||||
|
||||
ui.init(app);
|
||||
ui.init(app, !nvim_disable_win_state(std::env::args()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,16 +116,15 @@ fn activate(app: >k::Application) {
|
||||
nvim_timeout(std::env::args()),
|
||||
));
|
||||
|
||||
ui.init(app);
|
||||
ui.init(app, !nvim_disable_win_state(std::env::args()));
|
||||
}
|
||||
|
||||
fn nvim_bin_path<I>(mut args: I) -> Option<String>
|
||||
where
|
||||
I: Iterator<Item = String>,
|
||||
{
|
||||
args.find(|a| a.starts_with(BIN_PATH_ARG)).and_then(|p| {
|
||||
p.split('=').nth(1).map(str::to_owned)
|
||||
})
|
||||
args.find(|a| a.starts_with(BIN_PATH_ARG))
|
||||
.and_then(|p| p.split('=').nth(1).map(str::to_owned))
|
||||
}
|
||||
|
||||
fn nvim_timeout<I>(mut args: I) -> Option<Duration>
|
||||
@@ -141,6 +143,15 @@ where
|
||||
.map(|timeout| Duration::from_secs(timeout))
|
||||
}
|
||||
|
||||
fn nvim_disable_win_state<I>(mut args: I) -> bool
|
||||
where
|
||||
I: Iterator<Item = String>,
|
||||
{
|
||||
args.find(|a| a.starts_with(DISABLE_WIN_STATE_RESTORE))
|
||||
.map(|_| true)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -157,14 +168,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_timeout_arg() {
|
||||
assert_eq!(
|
||||
Some(Duration::from_secs(100)),
|
||||
nvim_timeout(vec!["neovim-gtk", "--timeout=100"].iter().map(
|
||||
|s| s.to_string(),
|
||||
))
|
||||
nvim_timeout(
|
||||
vec!["neovim-gtk", "--timeout=100"]
|
||||
.iter()
|
||||
.map(|s| s.to_string(),)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, mpsc};
|
||||
|
||||
use neovim_lib::{Handler, Value};
|
||||
|
||||
@@ -24,6 +24,7 @@ impl NvimHandler {
|
||||
match method {
|
||||
"redraw" => {
|
||||
self.safe_call(move |ui| {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
let mut repaint_mode = RepaintMode::Nothing;
|
||||
|
||||
for ev in params {
|
||||
@@ -63,6 +64,7 @@ impl NvimHandler {
|
||||
if let Value::String(ev_name) = ev_name {
|
||||
let args = params_iter.collect();
|
||||
self.safe_call(move |ui| {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
redraw_handler::call_gui_event(
|
||||
ui,
|
||||
ev_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
||||
@@ -87,14 +89,56 @@ impl NvimHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn nvim_cb_req (&self, method: &str, params: Vec<Value>) -> result::Result<Value, Value> {
|
||||
match method {
|
||||
"Gui" => {
|
||||
if !params.is_empty() {
|
||||
let mut params_iter = params.into_iter();
|
||||
if let Some(req_name) = params_iter.next() {
|
||||
if let Value::String(req_name) = req_name {
|
||||
let args = params_iter.collect();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
self.safe_call(move |ui| {
|
||||
sender.send(redraw_handler::call_gui_request(
|
||||
&ui.clone(),
|
||||
req_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
||||
&args,
|
||||
)).unwrap();
|
||||
{
|
||||
let ui = &mut ui.borrow_mut();
|
||||
ui.on_redraw(&RepaintMode::All);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
Ok(receiver.recv().unwrap()?)
|
||||
} else {
|
||||
error!("Unsupported request");
|
||||
Err(Value::Nil)
|
||||
}
|
||||
} else {
|
||||
error!("Request name does not exist");
|
||||
Err(Value::Nil)
|
||||
}
|
||||
} else {
|
||||
error!("Unsupported request {:?}", params);
|
||||
Err(Value::Nil)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
error!("Request {}({:?})", method, params);
|
||||
Err(Value::Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn safe_call<F>(&self, cb: F)
|
||||
where
|
||||
F: FnOnce(&mut shell::State) -> result::Result<(), String> + 'static + Send,
|
||||
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
||||
{
|
||||
let mut cb = Some(cb);
|
||||
let shell = self.shell.clone();
|
||||
glib::idle_add(move || {
|
||||
if let Err(msg) = cb.take().unwrap()(&mut shell.borrow_mut()) {
|
||||
if let Err(msg) = cb.take().unwrap()(&shell) {
|
||||
error!("Error call function: {}", msg);
|
||||
}
|
||||
glib::Continue(false)
|
||||
@@ -106,4 +150,8 @@ impl Handler for NvimHandler {
|
||||
fn handle_notify(&mut self, name: &str, args: Vec<Value>) {
|
||||
self.nvim_cb(name, args);
|
||||
}
|
||||
|
||||
fn handle_request(&mut self, name: &str, args: Vec<Value>) -> result::Result<Value, Value> {
|
||||
self.nvim_cb_req(name, args)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use std::result;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use neovim_lib::{Value, UiOption};
|
||||
use neovim_lib::neovim_api::Tabpage;
|
||||
|
||||
use ui::UiMutex;
|
||||
use shell;
|
||||
use gtk::ClipboardExt;
|
||||
|
||||
use value::ValueMapExt;
|
||||
use rmpv;
|
||||
@@ -170,6 +173,17 @@ pub fn call_gui_event(
|
||||
) -> result::Result<(), String> {
|
||||
match method {
|
||||
"Font" => ui.set_font(try_str!(args[0])),
|
||||
"Clipboard" => {
|
||||
match try_str!(args[0]) {
|
||||
"Set" => {
|
||||
match try_str!(args[1]) {
|
||||
"*" => ui.clipboard_primary_set(try_str!(args[2])),
|
||||
_ => ui.clipboard_clipboard_set(try_str!(args[2])),
|
||||
}
|
||||
},
|
||||
opt => error!("Unknown option {}", opt),
|
||||
}
|
||||
},
|
||||
"Option" => {
|
||||
match try_str!(args[0]) {
|
||||
"Popupmenu" => {
|
||||
@@ -196,6 +210,38 @@ pub fn call_gui_event(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn call_gui_request(
|
||||
ui: &Arc<UiMutex<shell::State>>,
|
||||
method: &str,
|
||||
args: &Vec<Value>,
|
||||
) -> result::Result<Value, Value> {
|
||||
match method {
|
||||
"Clipboard" => {
|
||||
match try_str!(args[0]) {
|
||||
"Get" => {
|
||||
// NOTE: wait_for_text waits on the main loop. We can't have the ui borrowed
|
||||
// while it runs, otherwise ui callbacks will get called and try to borrow
|
||||
// mutably twice!
|
||||
let clipboard = {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
match try_str!(args[1]) {
|
||||
"*" => ui.clipboard_primary.clone(),
|
||||
_ => ui.clipboard_clipboard.clone(),
|
||||
}
|
||||
};
|
||||
let t = clipboard.wait_for_text().unwrap_or_else(|| String::new());
|
||||
Ok(Value::Array(t.split("\n").map(|s| s.into()).collect::<Vec<Value>>()))
|
||||
},
|
||||
opt => {
|
||||
error!("Unknown option {}", opt);
|
||||
Err(Value::Nil)
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => Err(Value::String(format!("Unsupported request {}({:?})", method, args).into())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
ui: &mut shell::State,
|
||||
method: &str,
|
||||
|
||||
@@ -6,6 +6,7 @@ use gtk;
|
||||
use gtk::prelude::*;
|
||||
use glib;
|
||||
use gdk::{EventButton, EventType};
|
||||
use pango::{self, LayoutExt};
|
||||
|
||||
use neovim_lib::{Neovim, NeovimApi};
|
||||
|
||||
@@ -23,6 +24,9 @@ struct State {
|
||||
scroll: gtk::ScrolledWindow,
|
||||
css_provider: gtk::CssProvider,
|
||||
info_label: gtk::Label,
|
||||
word_column: gtk::TreeViewColumn,
|
||||
kind_column: gtk::TreeViewColumn,
|
||||
menu_column: gtk::TreeViewColumn,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@@ -34,24 +38,25 @@ impl State {
|
||||
style_context.add_provider(&css_provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
|
||||
let renderer = gtk::CellRendererText::new();
|
||||
renderer.set_property_ellipsize(pango::EllipsizeMode::End);
|
||||
|
||||
// word
|
||||
let column = gtk::TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", 0);
|
||||
tree.append_column(&column);
|
||||
let word_column = gtk::TreeViewColumn::new();
|
||||
word_column.pack_start(&renderer, true);
|
||||
word_column.add_attribute(&renderer, "text", 0);
|
||||
tree.append_column(&word_column);
|
||||
|
||||
// kind
|
||||
let column = gtk::TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", 1);
|
||||
tree.append_column(&column);
|
||||
let kind_column = gtk::TreeViewColumn::new();
|
||||
kind_column.pack_start(&renderer, true);
|
||||
kind_column.add_attribute(&renderer, "text", 1);
|
||||
tree.append_column(&kind_column);
|
||||
|
||||
// menu
|
||||
let column = gtk::TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", 2);
|
||||
tree.append_column(&column);
|
||||
let menu_column = gtk::TreeViewColumn::new();
|
||||
menu_column.pack_start(&renderer, true);
|
||||
menu_column.add_attribute(&renderer, "text", 2);
|
||||
tree.append_column(&menu_column);
|
||||
|
||||
let info_label = gtk::Label::new(None);
|
||||
info_label.set_line_wrap(true);
|
||||
@@ -63,6 +68,9 @@ impl State {
|
||||
renderer,
|
||||
css_provider,
|
||||
info_label,
|
||||
word_column,
|
||||
kind_column,
|
||||
menu_column,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,14 +85,56 @@ impl State {
|
||||
self.select(ctx.selected);
|
||||
}
|
||||
|
||||
fn limit_column_widths(&self, ctx: &PopupMenuContext) {
|
||||
const DEFAULT_PADDING: i32 = 5;
|
||||
|
||||
let layout = ctx.font_ctx.create_layout();
|
||||
let kind_exists = ctx.menu_items.iter().find(|i| i.kind.len() > 0).is_some();
|
||||
let max_width = self.scroll.get_max_content_width();
|
||||
let (xpad, _) = self.renderer.get_padding();
|
||||
|
||||
let max_word_line = ctx.menu_items.iter().max_by_key(|m| m.word.len()).unwrap();
|
||||
layout.set_text(max_word_line.word);
|
||||
let (word_max_width, _) = layout.get_pixel_size();
|
||||
let word_column_width = word_max_width + xpad * 2 + DEFAULT_PADDING;
|
||||
|
||||
|
||||
if kind_exists {
|
||||
layout.set_text("[v]");
|
||||
let (kind_width, _) = layout.get_pixel_size();
|
||||
|
||||
self.kind_column.set_fixed_width(kind_width + xpad * 2 + DEFAULT_PADDING);
|
||||
self.kind_column.set_visible(true);
|
||||
|
||||
self.word_column.set_fixed_width(min(max_width - kind_width, word_column_width));
|
||||
} else {
|
||||
self.kind_column.set_visible(false);
|
||||
self.word_column.set_fixed_width(min(max_width, word_column_width));
|
||||
}
|
||||
|
||||
|
||||
let max_menu_line = ctx.menu_items.iter().max_by_key(|m| m.menu.len()).unwrap();
|
||||
|
||||
if max_menu_line.menu.len() > 0 {
|
||||
layout.set_text(max_menu_line.menu);
|
||||
let (menu_max_width, _) = layout.get_pixel_size();
|
||||
self.menu_column.set_fixed_width(menu_max_width + xpad * 2 + DEFAULT_PADDING);
|
||||
self.menu_column.set_visible(true);
|
||||
} else {
|
||||
self.menu_column.set_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_tree(&self, ctx: &PopupMenuContext) {
|
||||
if ctx.menu_items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.renderer.set_property_font(Some(
|
||||
&ctx.font_ctx.font_description().to_string(),
|
||||
));
|
||||
self.limit_column_widths(ctx);
|
||||
|
||||
self.renderer.set_property_font(
|
||||
Some(&ctx.font_ctx.font_description().to_string()),
|
||||
);
|
||||
|
||||
let color_model = &ctx.color_model;
|
||||
self.renderer.set_property_foreground_rgba(
|
||||
|
||||
@@ -40,6 +40,10 @@ impl Context {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn create_layout(&self) -> pango::Layout {
|
||||
pango::Layout::new(&self.state.pango_context)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn font_description(&self) -> &pango::FontDescription {
|
||||
&self.state.font_desc
|
||||
|
||||
@@ -107,13 +107,7 @@ fn draw_underline(
|
||||
let undercurl_height = (underline_thickness * 4.0).min(max_undercurl_height);
|
||||
let undercurl_y = line_y + underline_position - undercurl_height / 2.0;
|
||||
|
||||
pangocairo::functions::error_underline_path(
|
||||
ctx,
|
||||
line_x,
|
||||
undercurl_y,
|
||||
char_width,
|
||||
undercurl_height,
|
||||
);
|
||||
pangocairo::functions::show_error_underline(ctx, line_x, undercurl_y, char_width, undercurl_height);
|
||||
} else if cell.attrs.underline {
|
||||
let fg = color_model.actual_cell_fg(cell);
|
||||
ctx.set_source_rgb(fg.0, fg.1, fg.2);
|
||||
|
||||
285
src/shell.rs
285
src/shell.rs
@@ -1,4 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::ops::Deref;
|
||||
@@ -7,23 +7,23 @@ use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use cairo;
|
||||
use pango::{LayoutExt, FontDescription};
|
||||
use pango::{FontDescription, LayoutExt};
|
||||
use gdk;
|
||||
use gdk::{ModifierType, EventButton, EventMotion, EventType, EventScroll};
|
||||
use gdk::{EventButton, EventMotion, EventScroll, EventType, ModifierType};
|
||||
use gdk_sys;
|
||||
use glib;
|
||||
use gtk;
|
||||
use gtk::prelude::*;
|
||||
use pangocairo;
|
||||
|
||||
use neovim_lib::{Neovim, NeovimApi, Value};
|
||||
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
|
||||
use neovim_lib::neovim_api::Tabpage;
|
||||
|
||||
use settings::{Settings, FontSource};
|
||||
use ui_model::{UiModel, Attrs, ModelRect};
|
||||
use color::{ColorModel, Color, COLOR_BLACK, COLOR_WHITE, COLOR_RED};
|
||||
use nvim::{self, RedrawEvents, GuiApi, RepaintMode, ErrorReport, NeovimClient,
|
||||
NeovimRef, NeovimClientAsync, CompleteItem};
|
||||
use settings::{FontSource, Settings};
|
||||
use ui_model::{Attrs, ModelRect, UiModel};
|
||||
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
||||
use nvim::{self, CompleteItem, ErrorReport, GuiApi, NeovimClient, NeovimClientAsync, NeovimRef,
|
||||
RedrawEvents, RepaintMode};
|
||||
use input;
|
||||
use input::keyval_to_input_string;
|
||||
use cursor::{BlinkCursor, Cursor, CursorRedrawCb};
|
||||
@@ -37,7 +37,7 @@ use render;
|
||||
use render::CellMetrics;
|
||||
|
||||
const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12";
|
||||
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.1";
|
||||
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2";
|
||||
|
||||
macro_rules! idle_cb_call {
|
||||
($state:ident.$cb:ident($( $x:expr ),*)) => (
|
||||
@@ -51,28 +51,6 @@ macro_rules! idle_cb_call {
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ResizeStateEnum {
|
||||
NvimResizeTimer(usize, usize),
|
||||
NvimResizeRequest(usize, usize),
|
||||
Wait,
|
||||
}
|
||||
|
||||
pub struct ResizeState {
|
||||
state: ResizeStateEnum,
|
||||
timer: Option<glib::SourceId>,
|
||||
}
|
||||
|
||||
impl ResizeState {
|
||||
pub fn new() -> Self {
|
||||
ResizeState {
|
||||
state: ResizeStateEnum::Wait,
|
||||
timer: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct RenderState {
|
||||
pub font_ctx: render::Context,
|
||||
pub color_model: ColorModel,
|
||||
@@ -99,6 +77,11 @@ pub struct State {
|
||||
cmd_line: CmdLine,
|
||||
settings: Rc<RefCell<Settings>>,
|
||||
render_state: Rc<RefCell<RenderState>>,
|
||||
resize_timer: Rc<Cell<Option<glib::SourceId>>>,
|
||||
pub clipboard_clipboard: gtk::Clipboard,
|
||||
pub clipboard_primary: gtk::Clipboard,
|
||||
|
||||
pub mode: mode::Mode,
|
||||
|
||||
stack: gtk::Stack,
|
||||
pub drawing_area: gtk::DrawingArea,
|
||||
@@ -106,8 +89,6 @@ pub struct State {
|
||||
im_context: gtk::IMMulticontext,
|
||||
error_area: error::ErrorArea,
|
||||
|
||||
resize_state: Rc<RefCell<ResizeState>>,
|
||||
|
||||
options: ShellOptions,
|
||||
|
||||
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||
@@ -131,6 +112,11 @@ impl State {
|
||||
cmd_line,
|
||||
settings,
|
||||
render_state,
|
||||
resize_timer: Rc::new(Cell::new(None)),
|
||||
clipboard_clipboard: gtk::Clipboard::get(&gdk::Atom::intern("CLIPBOARD")),
|
||||
clipboard_primary: gtk::Clipboard::get(&gdk::Atom::intern("PRIMARY")),
|
||||
|
||||
mode: mode::Mode::new(),
|
||||
|
||||
// UI
|
||||
stack: gtk::Stack::new(),
|
||||
@@ -139,8 +125,6 @@ impl State {
|
||||
im_context: gtk::IMMulticontext::new(),
|
||||
error_area: error::ErrorArea::new(),
|
||||
|
||||
resize_state: Rc::new(RefCell::new(ResizeState::new())),
|
||||
|
||||
options,
|
||||
|
||||
detach_cb: None,
|
||||
@@ -151,6 +135,7 @@ impl State {
|
||||
/// Return NeovimRef only if vim in non blocking state
|
||||
///
|
||||
/// Note that this call also do neovim api call get_mode
|
||||
#[allow(dead_code)]
|
||||
pub fn nvim_non_blocked(&self) -> Option<NeovimRef> {
|
||||
self.nvim().and_then(NeovimRef::non_blocked)
|
||||
}
|
||||
@@ -215,6 +200,14 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clipboard_clipboard_set(&self, text: &str) {
|
||||
self.clipboard_clipboard.set_text(text);
|
||||
}
|
||||
|
||||
pub fn clipboard_primary_set(&self, text: &str) {
|
||||
self.clipboard_primary.set_text(text);
|
||||
}
|
||||
|
||||
fn close_popup_menu(&self) {
|
||||
if self.popup_menu.is_open() {
|
||||
if let Some(mut nvim) = self.nvim() {
|
||||
@@ -301,78 +294,34 @@ impl State {
|
||||
self.im_context.reset();
|
||||
}
|
||||
|
||||
fn try_nvim_resize(&self) {
|
||||
fn try_nvim_resize(&mut self) {
|
||||
let (columns, rows) = self.calc_nvim_size();
|
||||
|
||||
let mut resize_state = self.resize_state.borrow_mut();
|
||||
|
||||
match resize_state.state {
|
||||
ResizeStateEnum::NvimResizeTimer(req_columns, req_rows) => {
|
||||
if req_columns == columns && req_rows == rows {
|
||||
return;
|
||||
}
|
||||
glib::source_remove(resize_state.timer.take().unwrap());
|
||||
resize_state.state = ResizeStateEnum::Wait;
|
||||
}
|
||||
ResizeStateEnum::NvimResizeRequest(req_columns, req_rows) => {
|
||||
if req_columns == columns && req_rows == rows {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ResizeStateEnum::Wait => (),
|
||||
}
|
||||
|
||||
|
||||
let resize_state_ref = self.resize_state.clone();
|
||||
let nvim = self.nvim.clone();
|
||||
|
||||
|
||||
if self.model.rows == rows && self.model.columns == columns {
|
||||
return;
|
||||
}
|
||||
|
||||
resize_state.state = ResizeStateEnum::NvimResizeTimer(columns, rows);
|
||||
resize_state.timer = Some(gtk::timeout_add(250, move || {
|
||||
let mut resize_state = resize_state_ref.borrow_mut();
|
||||
resize_state.state = ResizeStateEnum::NvimResizeRequest(columns, rows);
|
||||
resize_state.timer = None;
|
||||
let resize_timer = self.resize_timer.take();
|
||||
if let Some(resize_timer) = resize_timer {
|
||||
glib::source_remove(resize_timer);
|
||||
}
|
||||
|
||||
if let Some(mut nvim) = nvim.nvim().and_then(NeovimRef::non_blocked) {
|
||||
if let Err(err) = nvim.ui_try_resize(columns as u64, rows as u64) {
|
||||
error!("Error trying resize nvim {}", err);
|
||||
}
|
||||
let nvim = self.nvim.clone();
|
||||
let resize_timer = self.resize_timer.clone();
|
||||
|
||||
let resize_id = gtk::timeout_add(200, move || {
|
||||
resize_timer.set(None);
|
||||
|
||||
if let Some(mut nvim) = nvim.nvim() {
|
||||
debug!("ui_try_resize {}/{}", columns, rows);
|
||||
nvim.ui_try_resize_async(columns as u64, rows as u64)
|
||||
.cb(|r| r.report_err())
|
||||
.call();
|
||||
}
|
||||
Continue(false)
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
fn get_window(&self) -> gtk::Window {
|
||||
self.drawing_area
|
||||
.get_toplevel()
|
||||
.unwrap()
|
||||
.downcast()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn resize_main_window(&mut self) {
|
||||
let &CellMetrics {
|
||||
line_height,
|
||||
char_width,
|
||||
..
|
||||
} = self.render_state.borrow().font_ctx.cell_metrics();
|
||||
|
||||
let width = self.drawing_area.get_allocated_width();
|
||||
let height = self.drawing_area.get_allocated_height();
|
||||
let request_height = (self.model.rows as f64 * line_height) as i32;
|
||||
let request_width = (self.model.columns as f64 * char_width) as i32;
|
||||
|
||||
if width != request_width || height != request_height {
|
||||
let window = self.get_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);
|
||||
}
|
||||
self.resize_timer.set(Some(resize_id));
|
||||
}
|
||||
|
||||
fn edit_paste(&self, clipboard: &str) {
|
||||
@@ -383,12 +332,13 @@ impl State {
|
||||
render_state.mode.is(&mode::NvimMode::Normal)
|
||||
{
|
||||
let paste_code = format!("normal! \"{}P", clipboard);
|
||||
nvim.command(&paste_code).report_err();
|
||||
nvim.command_async(&paste_code)
|
||||
.cb(|r| r.report_err())
|
||||
.call();
|
||||
} else {
|
||||
let paste_code = format!("<C-r>{}", clipboard);
|
||||
nvim.input(&paste_code).report_err();
|
||||
nvim.input_async(&paste_code).cb(|r| r.report_err()).call();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,12 +427,14 @@ impl Shell {
|
||||
|
||||
self.widget.pack_start(&state.stack, true, true, 0);
|
||||
|
||||
state.drawing_area.set_events(
|
||||
(gdk_sys::GDK_BUTTON_RELEASE_MASK | gdk_sys::GDK_BUTTON_PRESS_MASK |
|
||||
gdk_sys::GDK_BUTTON_MOTION_MASK | gdk_sys::GDK_SCROLL_MASK |
|
||||
gdk_sys::GDK_SMOOTH_SCROLL_MASK)
|
||||
.bits() as i32,
|
||||
);
|
||||
state
|
||||
.drawing_area
|
||||
.set_events(
|
||||
(gdk_sys::GDK_BUTTON_RELEASE_MASK | gdk_sys::GDK_BUTTON_PRESS_MASK
|
||||
| gdk_sys::GDK_BUTTON_MOTION_MASK | gdk_sys::GDK_SCROLL_MASK
|
||||
| gdk_sys::GDK_SMOOTH_SCROLL_MASK)
|
||||
.bits() as i32,
|
||||
);
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
let ref_ui_state = self.ui_state.clone();
|
||||
@@ -496,33 +448,32 @@ impl Shell {
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
let ref_ui_state = self.ui_state.clone();
|
||||
state.drawing_area.connect_button_release_event(
|
||||
move |_, ev| {
|
||||
state
|
||||
.drawing_area
|
||||
.connect_button_release_event(move |_, ev| {
|
||||
gtk_button_release(
|
||||
&mut *ref_state.borrow_mut(),
|
||||
&mut *ref_ui_state.borrow_mut(),
|
||||
ev,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
let ref_ui_state = self.ui_state.clone();
|
||||
state.drawing_area.connect_motion_notify_event(
|
||||
move |_, ev| {
|
||||
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(&ref_state, ctx),
|
||||
);
|
||||
state
|
||||
.drawing_area
|
||||
.connect_draw(move |_, ctx| gtk_draw(&ref_state, ctx));
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.drawing_area.connect_key_press_event(move |_, ev| {
|
||||
@@ -562,45 +513,43 @@ impl Shell {
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.drawing_area.connect_focus_in_event(move |_, _| {
|
||||
gtk_focus_in(&mut *ref_state.borrow_mut())
|
||||
});
|
||||
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())
|
||||
});
|
||||
state
|
||||
.drawing_area
|
||||
.connect_focus_out_event(move |_, _| gtk_focus_out(&mut *ref_state.borrow_mut()));
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.drawing_area.connect_realize(move |w| {
|
||||
let ref_state = ref_state.clone();
|
||||
let w = w.clone();
|
||||
// sometime set_client_window does not work without idle_add
|
||||
// and looks like not enabled im_context
|
||||
gtk::idle_add(move || {
|
||||
gtk::idle_add(clone!(ref_state, w => move || {
|
||||
ref_state.borrow().im_context.set_client_window(
|
||||
w.get_window().as_ref(),
|
||||
);
|
||||
Continue(false)
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.im_context.connect_commit(move |_, ch| {
|
||||
ref_state.borrow().im_commit(ch)
|
||||
});
|
||||
state
|
||||
.im_context
|
||||
.connect_commit(move |_, ch| ref_state.borrow().im_commit(ch));
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.drawing_area.connect_configure_event(move |_, _| {
|
||||
ref_state.borrow().try_nvim_resize();
|
||||
state.drawing_area.connect_configure_event(move |_, ev| {
|
||||
debug!("configure_event {:?}", ev.get_size());
|
||||
ref_state.borrow_mut().try_nvim_resize();
|
||||
false
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state.drawing_area.connect_size_allocate(
|
||||
move |_, _| init_nvim(&ref_state),
|
||||
);
|
||||
|
||||
state.drawing_area.connect_size_allocate(move |_, _| {
|
||||
init_nvim(&ref_state);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -668,9 +617,10 @@ impl Deref for Shell {
|
||||
}
|
||||
|
||||
fn gtk_focus_in(state: &mut State) -> Inhibit {
|
||||
if let Some(mut nvim) = state.nvim_non_blocked() {
|
||||
nvim.command("if exists('#FocusGained') | doautocmd FocusGained | endif")
|
||||
.report_err();
|
||||
if let Some(mut nvim) = state.nvim() {
|
||||
nvim.command_async("if exists('#FocusGained') | doautocmd FocusGained | endif")
|
||||
.cb(|r| r.report_err())
|
||||
.call();
|
||||
}
|
||||
|
||||
state.im_context.focus_in();
|
||||
@@ -681,9 +631,10 @@ fn gtk_focus_in(state: &mut State) -> Inhibit {
|
||||
}
|
||||
|
||||
fn gtk_focus_out(state: &mut State) -> Inhibit {
|
||||
if let Some(mut nvim) = state.nvim_non_blocked() {
|
||||
nvim.command("if exists('#FocusLost') | doautocmd FocusLost | endif")
|
||||
.report_err();
|
||||
if let Some(mut nvim) = state.nvim() {
|
||||
nvim.command_async("if exists('#FocusLost') | doautocmd FocusLost | endif")
|
||||
.cb(|r| r.report_err())
|
||||
.call();
|
||||
}
|
||||
|
||||
state.im_context.focus_out();
|
||||
@@ -774,9 +725,8 @@ fn mouse_input(shell: &mut State, input: &str, state: ModifierType, position: (f
|
||||
|
||||
let nvim = shell.nvim();
|
||||
if let Some(mut nvim) = nvim {
|
||||
nvim.input(&input_str).expect(
|
||||
"Can't send mouse input event",
|
||||
);
|
||||
nvim.input(&input_str)
|
||||
.expect("Can't send mouse input event");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,7 +753,6 @@ fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion
|
||||
}
|
||||
|
||||
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
|
||||
|
||||
let state = state_arc.borrow();
|
||||
if state.nvim.is_initialized() {
|
||||
let render_state = state.render_state.borrow();
|
||||
@@ -888,12 +837,8 @@ fn init_nvim_async(
|
||||
});
|
||||
|
||||
// attach ui
|
||||
if let Err(err) = nvim::post_start_init(
|
||||
nvim,
|
||||
options.open_path.as_ref(),
|
||||
cols as u64,
|
||||
rows as u64,
|
||||
)
|
||||
if let Err(err) =
|
||||
nvim::post_start_init(nvim, options.open_path.as_ref(), cols as u64, rows as u64)
|
||||
{
|
||||
show_nvim_init_error(&err, state_arc.clone());
|
||||
} else {
|
||||
@@ -932,12 +877,14 @@ fn set_nvim_initialized(state_arc: Arc<UiMutex<State>>) {
|
||||
let mut state = state_arc.borrow_mut();
|
||||
state.nvim.async_to_sync();
|
||||
state.nvim.set_initialized();
|
||||
// in some case resize can happens while initilization in progress
|
||||
// so force resize here
|
||||
state.try_nvim_resize();
|
||||
state.cursor.as_mut().unwrap().start();
|
||||
|
||||
Continue(false)
|
||||
}));
|
||||
|
||||
|
||||
idle_cb_call!(state_arc.nvim_started_cb());
|
||||
}
|
||||
|
||||
@@ -945,7 +892,6 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
||||
let render_state = state.render_state.borrow();
|
||||
let color_model = &render_state.color_model;
|
||||
let layout = pangocairo::functions::create_layout(ctx).unwrap();
|
||||
let desc = render_state.font_ctx.font_description();
|
||||
let alloc = state.drawing_area.get_allocation();
|
||||
|
||||
ctx.set_source_rgb(
|
||||
@@ -955,7 +901,6 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
||||
);
|
||||
ctx.paint();
|
||||
|
||||
layout.set_font_description(desc);
|
||||
layout.set_text("Loading->");
|
||||
let (width, height) = layout.get_pixel_size();
|
||||
|
||||
@@ -971,7 +916,6 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
||||
pangocairo::functions::update_layout(ctx, &layout);
|
||||
pangocairo::functions::show_layout(ctx, &layout);
|
||||
|
||||
|
||||
ctx.move_to(x + width as f64, y);
|
||||
state.cursor.as_ref().unwrap().draw(
|
||||
ctx,
|
||||
@@ -987,8 +931,10 @@ fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
|
||||
let mut state = state_ref.borrow_mut();
|
||||
if state.start_nvim_initialization() {
|
||||
let (cols, rows) = state.calc_nvim_size();
|
||||
|
||||
debug!("Init nvim {}/{}", cols, rows);
|
||||
|
||||
state.model = UiModel::new(rows as u64, cols as u64);
|
||||
state.resize_state.borrow_mut().state = ResizeStateEnum::NvimResizeRequest(cols, rows);
|
||||
|
||||
let state_arc = state_ref.clone();
|
||||
let options = state.options.clone();
|
||||
@@ -996,7 +942,6 @@ fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl RedrawEvents for State {
|
||||
fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
|
||||
let repaint_area = self.model.set_cursor(row as usize, col as usize);
|
||||
@@ -1020,21 +965,10 @@ impl RedrawEvents for State {
|
||||
}
|
||||
|
||||
fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode {
|
||||
let state = self.resize_state.borrow().state.clone();
|
||||
match state {
|
||||
ResizeStateEnum::NvimResizeTimer(..) => {
|
||||
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
||||
self.model = UiModel::new(rows, columns);
|
||||
}
|
||||
}
|
||||
ResizeStateEnum::Wait |
|
||||
ResizeStateEnum::NvimResizeRequest(..) => {
|
||||
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
||||
self.resize_state.borrow_mut().state = ResizeStateEnum::Wait;
|
||||
self.model = UiModel::new(rows, columns);
|
||||
self.resize_main_window();
|
||||
}
|
||||
}
|
||||
debug!("on_resize {}/{}", columns, rows);
|
||||
|
||||
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
||||
self.model = UiModel::new(rows, columns);
|
||||
}
|
||||
|
||||
if let Some(mut nvim) = self.nvim.nvim() {
|
||||
@@ -1161,7 +1095,6 @@ impl RedrawEvents for State {
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
|
||||
|
||||
fn tabline_update(
|
||||
&mut self,
|
||||
selected: Tabpage,
|
||||
|
||||
225
src/ui.rs
225
src/ui.rs
@@ -1,17 +1,19 @@
|
||||
use std::cell::{RefCell, Ref, RefMut};
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::{env, thread};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gdk;
|
||||
use gtk;
|
||||
use gtk_sys;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{ApplicationWindow, HeaderBar, ToolButton, Image, AboutDialog, SettingsExt};
|
||||
use gtk::{AboutDialog, ApplicationWindow, HeaderBar, Image, SettingsExt, ToolButton};
|
||||
use gio::prelude::*;
|
||||
use gio::{Menu, MenuExt, MenuItem, SimpleAction};
|
||||
use toml;
|
||||
|
||||
use settings::Settings;
|
||||
use shell::{Shell, ShellOptions};
|
||||
use settings::{Settings, SettingsLoader};
|
||||
use shell::{self, Shell, ShellOptions};
|
||||
use shell_dlg;
|
||||
use project::Projects;
|
||||
use plug_manager;
|
||||
@@ -33,6 +35,9 @@ macro_rules! clone {
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_WIDTH: i32 = 800;
|
||||
const DEFAULT_HEIGHT: i32 = 600;
|
||||
|
||||
pub struct Ui {
|
||||
initialized: bool,
|
||||
comps: Arc<UiMutex<Components>>,
|
||||
@@ -44,6 +49,7 @@ pub struct Ui {
|
||||
|
||||
pub struct Components {
|
||||
window: Option<ApplicationWindow>,
|
||||
window_state: WindowState,
|
||||
open_btn: ToolButton,
|
||||
}
|
||||
|
||||
@@ -55,6 +61,7 @@ impl Components {
|
||||
Components {
|
||||
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
||||
window: None,
|
||||
window_state: WindowState::load(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +96,7 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self, app: >k::Application) {
|
||||
pub fn init(&mut self, app: >k::Application, restore_win_state: bool) {
|
||||
if self.initialized {
|
||||
return;
|
||||
}
|
||||
@@ -98,65 +105,98 @@ impl Ui {
|
||||
let mut settings = self.settings.borrow_mut();
|
||||
settings.init();
|
||||
|
||||
let mut comps = self.comps.borrow_mut();
|
||||
let window = ApplicationWindow::new(app);
|
||||
|
||||
self.shell.borrow_mut().init();
|
||||
{
|
||||
// initialize window from comps
|
||||
// borrowing of comps must be leaved
|
||||
// for event processing
|
||||
let mut comps = self.comps.borrow_mut();
|
||||
|
||||
comps.window = Some(ApplicationWindow::new(app));
|
||||
let window = comps.window.as_ref().unwrap();
|
||||
self.shell.borrow_mut().init();
|
||||
|
||||
let prefer_dark_theme = env::var("NVIM_GTK_PREFER_DARK_THEME")
|
||||
.map(|opt| opt.trim() == "1")
|
||||
.unwrap_or(false);
|
||||
if prefer_dark_theme {
|
||||
if let Some(settings) = window.get_settings() {
|
||||
settings.set_property_gtk_application_prefer_dark_theme(true);
|
||||
comps.window = Some(window.clone());
|
||||
|
||||
let prefer_dark_theme = env::var("NVIM_GTK_PREFER_DARK_THEME")
|
||||
.map(|opt| opt.trim() == "1")
|
||||
.unwrap_or(false);
|
||||
if prefer_dark_theme {
|
||||
if let Some(settings) = window.get_settings() {
|
||||
settings.set_property_gtk_application_prefer_dark_theme(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1
|
||||
let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR")
|
||||
.map(|opt| opt.trim() != "1")
|
||||
.unwrap_or(true);
|
||||
|
||||
if app.prefers_app_menu() || use_header_bar {
|
||||
self.create_main_menu(app, &window);
|
||||
}
|
||||
|
||||
if use_header_bar {
|
||||
let header_bar = HeaderBar::new();
|
||||
|
||||
let projects = self.projects.clone();
|
||||
header_bar.pack_start(&comps.open_btn);
|
||||
comps
|
||||
.open_btn
|
||||
.connect_clicked(move |_| projects.borrow_mut().show());
|
||||
|
||||
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), "Save");
|
||||
|
||||
let shell = self.shell.clone();
|
||||
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
||||
header_bar.pack_start(&save_btn);
|
||||
|
||||
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), "Paste");
|
||||
let shell = self.shell.clone();
|
||||
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
||||
header_bar.pack_start(&paste_btn);
|
||||
|
||||
header_bar.set_show_close_button(true);
|
||||
|
||||
window.set_titlebar(Some(&header_bar));
|
||||
}
|
||||
|
||||
if restore_win_state {
|
||||
if comps.window_state.is_maximized {
|
||||
window.maximize();
|
||||
}
|
||||
|
||||
window.set_default_size(
|
||||
comps.window_state.current_width,
|
||||
comps.window_state.current_height,
|
||||
);
|
||||
} else {
|
||||
window.set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1
|
||||
let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR")
|
||||
.map(|opt| opt.trim() != "1")
|
||||
.unwrap_or(true);
|
||||
let comps_ref = self.comps.clone();
|
||||
window.connect_size_allocate(move |window, _| {
|
||||
gtk_window_size_allocate(window, &mut *comps_ref.borrow_mut())
|
||||
});
|
||||
|
||||
if app.prefers_app_menu() || use_header_bar {
|
||||
self.create_main_menu(app);
|
||||
}
|
||||
let comps_ref = self.comps.clone();
|
||||
window.connect_window_state_event(move |_, event| {
|
||||
gtk_window_state_event(event, &mut *comps_ref.borrow_mut());
|
||||
Inhibit(false)
|
||||
});
|
||||
|
||||
if use_header_bar {
|
||||
let header_bar = HeaderBar::new();
|
||||
|
||||
let projects = self.projects.clone();
|
||||
header_bar.pack_start(&comps.open_btn);
|
||||
comps.open_btn.connect_clicked(
|
||||
move |_| projects.borrow_mut().show(),
|
||||
);
|
||||
|
||||
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), "Save");
|
||||
|
||||
let shell = self.shell.clone();
|
||||
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
||||
header_bar.pack_start(&save_btn);
|
||||
|
||||
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), "Paste");
|
||||
let shell = self.shell.clone();
|
||||
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
||||
header_bar.pack_start(&paste_btn);
|
||||
|
||||
header_bar.set_show_close_button(true);
|
||||
|
||||
window.set_titlebar(Some(&header_bar));
|
||||
}
|
||||
|
||||
window.set_default_size(800, 600);
|
||||
let comps_ref = self.comps.clone();
|
||||
window.connect_destroy(move |_| {
|
||||
comps_ref.borrow().window_state.save();
|
||||
});
|
||||
|
||||
let shell = self.shell.borrow();
|
||||
window.add(&**shell);
|
||||
@@ -182,14 +222,13 @@ impl Ui {
|
||||
let state_ref = self.shell.borrow().state.clone();
|
||||
let plug_manager_ref = self.plug_manager.clone();
|
||||
shell.set_nvim_started_cb(Some(move || {
|
||||
plug_manager_ref.borrow_mut().init_nvim_client(
|
||||
state_ref.borrow().nvim_clone(),
|
||||
);
|
||||
plug_manager_ref
|
||||
.borrow_mut()
|
||||
.init_nvim_client(state_ref.borrow().nvim_clone());
|
||||
}));
|
||||
}
|
||||
|
||||
fn create_main_menu(&self, app: >k::Application) {
|
||||
let comps = self.comps.clone();
|
||||
fn create_main_menu(&self, app: >k::Application, window: >k::ApplicationWindow) {
|
||||
let plug_manager = self.plug_manager.clone();
|
||||
|
||||
let menu = Menu::new();
|
||||
@@ -208,17 +247,11 @@ impl Ui {
|
||||
|
||||
let plugs_action = SimpleAction::new("Plugins", None);
|
||||
plugs_action.connect_activate(
|
||||
clone!(comps => move |_, _| plug_manager::Ui::new(&plug_manager).show(
|
||||
comps
|
||||
.borrow()
|
||||
.window
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
)),
|
||||
clone!(window => move |_, _| plug_manager::Ui::new(&plug_manager).show(&window)),
|
||||
);
|
||||
|
||||
let about_action = SimpleAction::new("HelpAbout", None);
|
||||
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
|
||||
about_action.connect_activate(clone!(window => move |_, _| on_help_about(&window)));
|
||||
about_action.set_enabled(true);
|
||||
|
||||
app.add_action(&about_action);
|
||||
@@ -226,13 +259,20 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_help_about(comps: &Components) {
|
||||
fn on_help_about(window: >k::ApplicationWindow) {
|
||||
let about = AboutDialog::new();
|
||||
about.set_transient_for(comps.window.as_ref());
|
||||
about.set_transient_for(window);
|
||||
about.set_program_name("NeovimGtk");
|
||||
about.set_version(env!("CARGO_PKG_VERSION"));
|
||||
about.set_logo_icon_name("org.daa.NeovimGtk");
|
||||
about.set_authors(&[env!("CARGO_PKG_AUTHORS")]);
|
||||
about.set_comments(
|
||||
format!(
|
||||
"Build on top of neovim\n\
|
||||
Minimum supported neovim version: {}",
|
||||
shell::MINIMUM_SUPPORTED_NVIM_VERSION
|
||||
).as_str(),
|
||||
);
|
||||
|
||||
about.connect_response(|about, _| about.destroy());
|
||||
about.show();
|
||||
@@ -253,6 +293,49 @@ fn gtk_delete(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> Inhibit {
|
||||
})
|
||||
}
|
||||
|
||||
fn gtk_window_size_allocate(app_window: >k::ApplicationWindow, comps: &mut Components) {
|
||||
if !app_window.is_maximized() {
|
||||
let (current_width, current_height) = app_window.get_size();
|
||||
comps.window_state.current_width = current_width;
|
||||
comps.window_state.current_height = current_height;
|
||||
}
|
||||
}
|
||||
|
||||
fn gtk_window_state_event(event: &gdk::EventWindowState, comps: &mut Components) {
|
||||
comps.window_state.is_maximized = event
|
||||
.get_new_window_state()
|
||||
.contains(gdk::WindowState::MAXIMIZED);
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct WindowState {
|
||||
current_width: i32,
|
||||
current_height: i32,
|
||||
is_maximized: bool,
|
||||
}
|
||||
|
||||
impl WindowState {
|
||||
pub fn new() -> Self {
|
||||
WindowState {
|
||||
current_width: DEFAULT_WIDTH,
|
||||
current_height: DEFAULT_HEIGHT,
|
||||
is_maximized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsLoader for WindowState {
|
||||
const SETTINGS_FILE: &'static str = "window.toml";
|
||||
|
||||
fn empty() -> WindowState {
|
||||
WindowState::new()
|
||||
}
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
toml::from_str(&s).map_err(|e| format!("{}", e))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiMutex<T: ?Sized> {
|
||||
thread: thread::ThreadId,
|
||||
data: RefCell<T>,
|
||||
|
||||
Reference in New Issue
Block a user