2017-03-26 11:34:38 +00:00
|
|
|
use std::cell::{RefCell, Ref, RefMut};
|
2017-09-10 18:49:41 +00:00
|
|
|
use std::{env, thread};
|
2017-04-12 10:12:05 +00:00
|
|
|
use std::rc::Rc;
|
|
|
|
use std::sync::Arc;
|
2016-03-31 16:19:08 +00:00
|
|
|
|
2016-03-16 15:25:25 +00:00
|
|
|
use gtk;
|
2017-03-15 15:33:47 +00:00
|
|
|
use gtk_sys;
|
2016-03-16 15:25:25 +00:00
|
|
|
use gtk::prelude::*;
|
2017-12-05 12:38:31 +00:00
|
|
|
use gtk::{ApplicationWindow, HeaderBar, ToolButton, Image, AboutDialog, SettingsExt};
|
2017-06-02 14:54:24 +00:00
|
|
|
use gio::prelude::*;
|
2017-12-15 01:36:16 +00:00
|
|
|
use gio::{Menu, MenuExt, MenuItem, SimpleAction};
|
2017-03-15 15:33:47 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
use settings::Settings;
|
2017-06-04 10:14:09 +00:00
|
|
|
use shell::{Shell, ShellOptions};
|
2017-04-01 10:00:14 +00:00
|
|
|
use shell_dlg;
|
2017-05-13 14:31:19 +00:00
|
|
|
use project::Projects;
|
2017-10-13 15:27:10 +00:00
|
|
|
use plug_manager;
|
2017-03-13 15:03:32 +00:00
|
|
|
|
2017-11-05 18:07:02 +00:00
|
|
|
macro_rules! clone {
|
|
|
|
(@param _) => ( _ );
|
|
|
|
(@param $x:ident) => ( $x );
|
|
|
|
($($n:ident),+ => move || $body:expr) => (
|
|
|
|
{
|
|
|
|
$( let $n = $n.clone(); )+
|
|
|
|
move || $body
|
|
|
|
}
|
|
|
|
);
|
|
|
|
($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
|
|
|
|
{
|
|
|
|
$( let $n = $n.clone(); )+
|
|
|
|
move |$(clone!(@param $p),)+| $body
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub struct Ui {
|
|
|
|
initialized: bool,
|
|
|
|
comps: Arc<UiMutex<Components>>,
|
|
|
|
settings: Rc<RefCell<Settings>>,
|
|
|
|
shell: Rc<RefCell<Shell>>,
|
2017-05-13 14:31:19 +00:00
|
|
|
projects: Rc<RefCell<Projects>>,
|
2017-10-15 19:50:59 +00:00
|
|
|
plug_manager: Arc<UiMutex<plug_manager::Manager>>,
|
2017-03-23 15:04:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub struct Components {
|
|
|
|
window: Option<ApplicationWindow>,
|
2017-05-13 14:31:19 +00:00
|
|
|
open_btn: ToolButton,
|
2016-03-19 10:27:39 +00:00
|
|
|
}
|
2016-03-16 15:25:25 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
impl Components {
|
|
|
|
fn new() -> Components {
|
2017-10-13 15:27:10 +00:00
|
|
|
let save_image =
|
|
|
|
Image::new_from_icon_name("document-open", gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
2017-05-13 14:31:19 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
Components {
|
2017-05-13 14:31:19 +00:00
|
|
|
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
2017-03-06 13:58:10 +00:00
|
|
|
window: None,
|
2016-03-19 10:27:39 +00:00
|
|
|
}
|
2016-03-16 15:25:25 +00:00
|
|
|
}
|
|
|
|
|
2017-03-16 10:18:13 +00:00
|
|
|
pub fn close_window(&self) {
|
2017-03-06 13:58:10 +00:00
|
|
|
self.window.as_ref().unwrap().destroy();
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
pub fn window(&self) -> &ApplicationWindow {
|
|
|
|
self.window.as_ref().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ui {
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn new(options: ShellOptions) -> Ui {
|
2017-10-30 15:25:23 +00:00
|
|
|
let plug_manager = plug_manager::Manager::new();
|
2017-10-26 14:56:53 +00:00
|
|
|
|
|
|
|
let plug_manager = Arc::new(UiMutex::new(plug_manager));
|
2017-04-19 08:40:53 +00:00
|
|
|
let comps = Arc::new(UiMutex::new(Components::new()));
|
2017-04-12 10:12:05 +00:00
|
|
|
let settings = Rc::new(RefCell::new(Settings::new()));
|
2017-11-04 20:04:03 +00:00
|
|
|
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
2017-04-12 10:12:05 +00:00
|
|
|
settings.borrow_mut().set_shell(Rc::downgrade(&shell));
|
|
|
|
|
2017-05-13 14:31:19 +00:00
|
|
|
let projects = Projects::new(&comps.borrow().open_btn, shell.clone());
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
Ui {
|
|
|
|
initialized: false,
|
2017-05-13 14:31:19 +00:00
|
|
|
comps,
|
|
|
|
shell,
|
|
|
|
settings,
|
|
|
|
projects,
|
2017-10-15 19:50:59 +00:00
|
|
|
plug_manager,
|
2017-04-12 10:12:05 +00:00
|
|
|
}
|
2017-03-16 10:18:13 +00:00
|
|
|
}
|
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
pub fn init(&mut self, app: >k::Application) {
|
2017-03-15 15:33:47 +00:00
|
|
|
if self.initialized {
|
|
|
|
return;
|
2017-03-14 20:12:31 +00:00
|
|
|
}
|
|
|
|
self.initialized = true;
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
let mut settings = self.settings.borrow_mut();
|
|
|
|
settings.init();
|
|
|
|
|
|
|
|
let mut comps = self.comps.borrow_mut();
|
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
self.shell.borrow_mut().init();
|
2017-03-12 19:50:03 +00:00
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
comps.window = Some(ApplicationWindow::new(app));
|
|
|
|
let window = comps.window.as_ref().unwrap();
|
2017-05-13 14:31:19 +00:00
|
|
|
|
2017-12-05 12:38:31 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
// Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1
|
2017-09-11 18:44:38 +00:00
|
|
|
let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR")
|
|
|
|
.map(|opt| opt.trim() != "1")
|
|
|
|
.unwrap_or(true);
|
2017-05-13 14:31:19 +00:00
|
|
|
|
2017-11-13 09:01:37 +00:00
|
|
|
if app.prefers_app_menu() || use_header_bar {
|
|
|
|
self.create_main_menu(app);
|
|
|
|
}
|
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
if use_header_bar {
|
|
|
|
let header_bar = HeaderBar::new();
|
2016-03-17 13:58:21 +00:00
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
let projects = self.projects.clone();
|
|
|
|
header_bar.pack_start(&comps.open_btn);
|
2017-10-13 15:27:10 +00:00
|
|
|
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,
|
|
|
|
);
|
2017-09-10 18:49:41 +00:00
|
|
|
let save_btn = ToolButton::new(Some(&save_image), "Save");
|
2016-03-17 13:58:21 +00:00
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
let shell = self.shell.clone();
|
|
|
|
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
|
|
|
header_bar.pack_start(&save_btn);
|
2016-03-16 15:25:25 +00:00
|
|
|
|
2017-10-13 15:27:10 +00:00
|
|
|
let paste_image = Image::new_from_icon_name(
|
|
|
|
"edit-paste",
|
|
|
|
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
|
|
|
);
|
2017-09-10 18:49:41 +00:00
|
|
|
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);
|
2017-10-13 15:27:10 +00:00
|
|
|
|
2017-09-10 18:49:41 +00:00
|
|
|
header_bar.set_show_close_button(true);
|
|
|
|
|
|
|
|
window.set_titlebar(Some(&header_bar));
|
|
|
|
}
|
2017-03-06 13:58:10 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
window.set_default_size(800, 600);
|
2017-03-06 21:05:48 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
let shell = self.shell.borrow();
|
2017-05-27 16:50:25 +00:00
|
|
|
window.add(&**shell);
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
window.show_all();
|
2017-09-22 15:05:12 +00:00
|
|
|
window.set_title("NeovimGtk");
|
2017-04-12 10:12:05 +00:00
|
|
|
|
|
|
|
let comps_ref = self.comps.clone();
|
|
|
|
let shell_ref = self.shell.clone();
|
|
|
|
window.connect_delete_event(move |_, _| gtk_delete(&*comps_ref, &*shell_ref));
|
|
|
|
|
2017-05-27 16:50:25 +00:00
|
|
|
shell.grab_focus();
|
2017-04-12 10:12:05 +00:00
|
|
|
|
2017-06-04 10:14:09 +00:00
|
|
|
let comps_ref = self.comps.clone();
|
2017-10-13 15:27:10 +00:00
|
|
|
shell.set_detach_cb(Some(move || {
|
2017-07-09 11:05:55 +00:00
|
|
|
let comps_ref = comps_ref.clone();
|
|
|
|
gtk::idle_add(move || {
|
|
|
|
comps_ref.borrow().close_window();
|
|
|
|
Continue(false)
|
|
|
|
});
|
|
|
|
}));
|
2017-10-15 19:50:59 +00:00
|
|
|
|
|
|
|
let state_ref = self.shell.borrow().state.clone();
|
|
|
|
let plug_manager_ref = self.plug_manager.clone();
|
|
|
|
shell.set_nvim_started_cb(Some(move || {
|
2017-10-24 15:03:34 +00:00
|
|
|
plug_manager_ref.borrow_mut().init_nvim_client(
|
2017-10-16 15:34:26 +00:00
|
|
|
state_ref.borrow().nvim_clone(),
|
|
|
|
);
|
2017-10-15 19:50:59 +00:00
|
|
|
}));
|
2017-03-06 21:05:48 +00:00
|
|
|
}
|
2017-03-31 20:19:50 +00:00
|
|
|
|
|
|
|
fn create_main_menu(&self, app: >k::Application) {
|
2017-11-05 18:07:02 +00:00
|
|
|
let comps = self.comps.clone();
|
|
|
|
let plug_manager = self.plug_manager.clone();
|
|
|
|
|
2017-03-31 20:19:50 +00:00
|
|
|
let menu = Menu::new();
|
|
|
|
|
2017-12-15 01:36:16 +00:00
|
|
|
let section = Menu::new();
|
|
|
|
section.append_item(&MenuItem::new("New Window", "app.new-window"));
|
|
|
|
menu.append_section(None, §ion);
|
2017-11-05 18:07:02 +00:00
|
|
|
|
2017-12-15 01:36:16 +00:00
|
|
|
let section = Menu::new();
|
|
|
|
section.append_item(&MenuItem::new("Plugins", "app.Plugins"));
|
|
|
|
section.append_item(&MenuItem::new("About", "app.HelpAbout"));
|
|
|
|
menu.append_section(None, §ion);
|
2017-03-31 20:19:50 +00:00
|
|
|
|
2017-12-15 01:36:16 +00:00
|
|
|
menu.freeze();
|
2017-05-31 14:14:58 +00:00
|
|
|
app.set_app_menu(Some(&menu));
|
2017-03-31 20:19:50 +00:00
|
|
|
|
2017-11-05 18:07:02 +00:00
|
|
|
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(),
|
|
|
|
)),
|
|
|
|
);
|
|
|
|
|
2017-03-31 20:19:50 +00:00
|
|
|
let about_action = SimpleAction::new("HelpAbout", None);
|
2017-04-12 10:12:05 +00:00
|
|
|
about_action.connect_activate(move |_, _| on_help_about(&*comps.borrow()));
|
2017-03-31 20:19:50 +00:00
|
|
|
about_action.set_enabled(true);
|
2017-11-05 18:07:02 +00:00
|
|
|
|
2017-03-31 20:19:50 +00:00
|
|
|
app.add_action(&about_action);
|
2017-11-05 18:07:02 +00:00
|
|
|
app.add_action(&plugs_action);
|
2017-03-31 20:19:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
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"));
|
2017-09-22 15:05:12 +00:00
|
|
|
about.set_logo_icon_name("org.daa.NeovimGtk");
|
2017-04-12 10:12:05 +00:00
|
|
|
about.set_authors(&[env!("CARGO_PKG_AUTHORS")]);
|
2017-03-31 20:19:50 +00:00
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
about.connect_response(|about, _| about.destroy());
|
|
|
|
about.show();
|
2016-05-04 14:59:51 +00:00
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
fn gtk_delete(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> Inhibit {
|
2017-07-06 09:41:35 +00:00
|
|
|
if !shell.borrow().is_nvim_initialized() {
|
|
|
|
return Inhibit(false);
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
Inhibit(if shell_dlg::can_close_window(comps, shell) {
|
2017-10-13 15:27:10 +00:00
|
|
|
let comps = comps.borrow();
|
|
|
|
comps.close_window();
|
|
|
|
shell.borrow_mut().detach_ui();
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
true
|
|
|
|
})
|
2016-05-04 08:52:57 +00:00
|
|
|
}
|
|
|
|
|
2017-03-26 11:34:38 +00:00
|
|
|
pub struct UiMutex<T: ?Sized> {
|
2017-09-14 13:29:03 +00:00
|
|
|
thread: thread::ThreadId,
|
2017-03-26 11:34:38 +00:00
|
|
|
data: RefCell<T>,
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
unsafe impl<T: ?Sized> Send for UiMutex<T> {}
|
|
|
|
unsafe impl<T: ?Sized> Sync for UiMutex<T> {}
|
2017-03-26 11:34:38 +00:00
|
|
|
|
|
|
|
impl<T> UiMutex<T> {
|
|
|
|
pub fn new(t: T) -> UiMutex<T> {
|
2017-04-12 10:12:05 +00:00
|
|
|
UiMutex {
|
2017-09-14 13:29:03 +00:00
|
|
|
thread: thread::current().id(),
|
2017-04-12 10:12:05 +00:00
|
|
|
data: RefCell::new(t),
|
|
|
|
}
|
2017-03-26 11:34:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: ?Sized> UiMutex<T> {
|
|
|
|
pub fn borrow(&self) -> Ref<T> {
|
2017-04-12 10:12:05 +00:00
|
|
|
self.assert_ui_thread();
|
2017-03-26 11:34:38 +00:00
|
|
|
self.data.borrow()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn borrow_mut(&self) -> RefMut<T> {
|
2017-04-12 10:12:05 +00:00
|
|
|
self.assert_ui_thread();
|
2017-03-26 11:34:38 +00:00
|
|
|
self.data.borrow_mut()
|
|
|
|
}
|
|
|
|
|
2017-04-12 10:12:05 +00:00
|
|
|
#[inline]
|
|
|
|
fn assert_ui_thread(&self) {
|
2017-09-14 13:29:03 +00:00
|
|
|
if thread::current().id() != self.thread {
|
|
|
|
panic!("Can access to UI only from main thread");
|
2017-03-26 11:34:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|