diff --git a/src/ui.rs b/src/ui.rs index aeda3e7..65bcd2b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,16 +1,18 @@ -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 settings::{Settings, SettingsLoader}; use shell::{self, Shell, ShellOptions}; use shell_dlg; use project::Projects; @@ -44,6 +46,7 @@ pub struct Ui { pub struct Components { window: Option, + window_state: WindowState, open_btn: ToolButton, } @@ -55,6 +58,7 @@ impl Components { Components { open_btn: ToolButton::new(Some(&save_image), "Open"), window: None, + window_state: WindowState::load(), } } @@ -98,65 +102,93 @@ 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); + } + + 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( + comps.window_state.current_width, + comps.window_state.current_height, + ); + if comps.window_state.is_maximized { + window.maximize(); } } - // 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,9 +214,9 @@ 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()); })); } @@ -236,7 +268,7 @@ fn on_help_about(comps: &Components) { about.set_comments( format!( "Build on top of neovim\n\ - Minimum supported neovim version: {}", + Minimum supported neovim version: {}", shell::MINIMUM_SUPPORTED_NVIM_VERSION ).as_str(), ); @@ -260,6 +292,50 @@ fn gtk_delete(comps: &UiMutex, shell: &RefCell) -> 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: 800, + current_height: 600, + 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 { + toml::from_str(&s).map_err(|e| format!("{}", e)) + } +} + + pub struct UiMutex { thread: thread::ThreadId, data: RefCell,