diff --git a/src/main.rs b/src/main.rs index 2cfb3d4..982fcfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,7 @@ mod cursor; mod shell_dlg; mod popup_menu; mod project; +mod tabline; use std::env; use gio::ApplicationExt; diff --git a/src/nvim.rs b/src/nvim.rs index a5ab200..5781f32 100644 --- a/src/nvim.rs +++ b/src/nvim.rs @@ -4,8 +4,10 @@ use std::process::{Stdio, Command}; use std::result; use std::sync::Arc; -use ui::UiMutex; use neovim_lib::{Handler, Neovim, NeovimApi, Session, Value, UiAttachOptions, CallError, UiOption}; +use neovim_lib::neovim_api::Tabpage; + +use ui::UiMutex; use ui_model::{ModelRect, ModelRectVec}; use shell; use glib; @@ -51,6 +53,8 @@ pub trait RedrawEvents { fn popupmenu_hide(&mut self) -> RepaintMode; fn popupmenu_select(&mut self, selected: i64) -> RepaintMode; + + fn tabline_update(&mut self, selected: Tabpage, tabs: Vec<(Tabpage, Option<&str>)>) -> RepaintMode; } pub trait GuiApi { @@ -112,6 +116,7 @@ pub fn initialize(shell: Arc>, .start_event_loop_handler(NvimHandler::new(shell)); let mut opts = UiAttachOptions::new(); opts.set_popupmenu_external(false); + opts.set_tabline_external(true); nvim.ui_attach(80, 24, opts) .map_err(|e| Error::new(ErrorKind::Other, e))?; nvim.command("runtime! ginit.vim") @@ -275,6 +280,28 @@ fn call(ui: &mut shell::State, } "popupmenu_hide" => ui.popupmenu_hide(), "popupmenu_select" => ui.popupmenu_select(try_int!(args[0])), + "tabline_update" => { + let tabs_in = args[1].as_array().ok_or("Error get tabline list")?; + + let mut tabs_out = Vec::new(); + for tab in tabs_in { + let tab_attrs = tab.as_map().ok_or("Error get map for tab")?; + + let mut tab_attr = None; + let mut name_attr = None; + + for attr in tab_attrs { + let key = attr.0.as_str().ok_or("Error get key value")?; + if key == "tab" { + tab_attr = Some(Tabpage::new(attr.1.clone())); + } else if key == "name" { + name_attr = attr.1.as_str(); + } + } + tabs_out.push((tab_attr.unwrap(), name_attr)); + } + ui.tabline_update(Tabpage::new(args[0].clone()), tabs_out) + }, _ => { println!("Event {}({:?})", method, args); RepaintMode::Nothing diff --git a/src/shell.rs b/src/shell.rs index e929d05..47d903e 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,7 +1,8 @@ -use std::cell::{Ref, RefMut, RefCell}; +use std::cell::{RefMut, RefCell}; use std::rc::Rc; use std::sync; use std::sync::Arc; +use std::ops::Deref; use cairo; use pangocairo::CairoContextExt; @@ -10,10 +11,11 @@ use pango::FontDescription; use gdk::{ModifierType, EventConfigure, EventButton, EventMotion, EventType, EventScroll}; use gdk_sys; use glib; +use gtk; use gtk::prelude::*; -use gtk::DrawingArea; use neovim_lib::{Neovim, NeovimApi, Value}; +use neovim_lib::neovim_api::Tabpage; use settings::{Settings, FontSource}; use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED}; @@ -25,6 +27,7 @@ use cursor::Cursor; use ui; use ui::UiMutex; use popup_menu::PopupMenu; +use tabline::Tabline; const DEFAULT_FONT_NAME: &'static str = "DejaVu Sans Mono 12"; @@ -44,13 +47,15 @@ pub struct State { cur_attrs: Option, pub mode: NvimMode, mouse_enabled: bool, - drawing_area: DrawingArea, nvim: Option>>, font_desc: FontDescription, cursor: Option, popup_menu: RefCell, settings: Rc>, + drawing_area: gtk::DrawingArea, + tabs: Tabline, + line_height: Option, char_width: Option, request_resize: bool, @@ -61,12 +66,11 @@ pub struct State { impl State { pub fn new(settings: Rc>, parent: &Arc>) -> State { - let drawing_area = DrawingArea::new(); + let drawing_area = gtk::DrawingArea::new(); let popup_menu = RefCell::new(PopupMenu::new(&drawing_area)); State { model: UiModel::new(24, 80), - drawing_area, nvim: None, cur_attrs: None, bg_color: COLOR_BLACK, @@ -79,6 +83,9 @@ impl State { popup_menu, settings: settings, + drawing_area, + tabs: Tabline::new(), + line_height: None, char_width: None, resize_timer: None, @@ -191,6 +198,8 @@ impl UiState { pub struct Shell { pub state: Arc>, ui_state: Rc>, + + widget: gtk::Box, } impl Shell { @@ -198,6 +207,8 @@ impl Shell { let shell = Shell { state: Arc::new(UiMutex::new(State::new(settings, parent))), ui_state: Rc::new(RefCell::new(UiState::new())), + + widget: gtk::Box::new(gtk::Orientation::Vertical, 0), }; let shell_ref = Arc::downgrade(&shell.state); @@ -213,6 +224,9 @@ impl Shell { state.drawing_area.set_vexpand(true); state.drawing_area.set_can_focus(true); + self.widget.pack_start(&*state.tabs, false, true, 0); + self.widget.pack_start(&state.drawing_area, true, true, 0); + state .drawing_area .set_events((gdk_sys::GDK_BUTTON_RELEASE_MASK | gdk_sys::GDK_BUTTON_PRESS_MASK | @@ -284,14 +298,6 @@ impl Shell { .connect_focus_out_event(move |_, _| gtk_focus_out(&mut *ref_state.borrow_mut())); } - pub fn state(&self) -> Ref { - self.state.borrow() - } - - pub fn drawing_area(&self) -> Ref { - Ref::map(self.state(), |s| &s.drawing_area) - } - #[cfg(unix)] pub fn redraw(&self, mode: &RepaintMode) { self.state.borrow_mut().on_redraw(mode); @@ -320,6 +326,10 @@ impl Shell { state.nvim = Some(Rc::new(RefCell::new(nvim))); } + pub fn grab_focus(&self) { + self.state.borrow().drawing_area.grab_focus(); + } + pub fn open_file(&self, path: &str) { self.state.borrow().open_file(path); } @@ -352,6 +362,14 @@ impl Shell { } } +impl Deref for Shell { + type Target = gtk::Box; + + fn deref(&self) -> >k::Box { + &self.widget + } +} + fn gtk_focus_in(state: &mut State) -> Inhibit { state.cursor.as_mut().unwrap().enter_focus(); let point = state.model.cur_point(); @@ -864,6 +882,13 @@ impl RedrawEvents for State { self.popup_menu.borrow().select(selected); RepaintMode::Nothing } + + + fn tabline_update(&mut self, selected: Tabpage, tabs: Vec<(Tabpage, Option<&str>)>) -> RepaintMode { + self.tabs.update_tabs(&selected, &tabs); + + RepaintMode::Nothing + } } impl GuiApi for State { diff --git a/src/tabline.rs b/src/tabline.rs new file mode 100644 index 0000000..214a721 --- /dev/null +++ b/src/tabline.rs @@ -0,0 +1,55 @@ +use std::ops::Deref; + +use gtk; +use gtk::prelude::*; + +use neovim_lib::neovim_api::Tabpage; + +pub struct Tabline { + tabs: gtk::Notebook, +} + +impl Tabline { + pub fn new() -> Self { + let tabs = gtk::Notebook::new(); + + tabs.set_can_focus(false); + tabs.set_scrollable(true); + tabs.set_show_border(false); + tabs.set_border_width(0); + + Tabline { + tabs, + } + } + + pub fn update_tabs(&self, selected: &Tabpage, tabs: &Vec<(Tabpage, Option<&str>)>) { + let count = self.tabs.get_n_pages() as usize; + if count < tabs.len() { + for _ in count..tabs.len() { + let empty = gtk::Box::new(gtk::Orientation::Vertical, 0); + empty.show_all(); + self.tabs.append_page(&empty, Some(>k::Label::new("AA"))); + } + } + else if count > tabs.len() { + for _ in tabs.len()..count { + self.tabs.remove_page(None); + } + } + + // TODO: current page + for (idx, tab) in tabs.iter().enumerate() { + let tab_child = self.tabs.get_nth_page(Some(idx as u32)); + self.tabs.set_tab_label_text(&tab_child.unwrap(), &tab.1.unwrap_or("??")); + } + } +} + +impl Deref for Tabline { + type Target = gtk::Notebook; + + fn deref(&self) -> >k::Notebook { + &self.tabs + } +} diff --git a/src/ui.rs b/src/ui.rs index 02d29c1..8e4ce53 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -114,7 +114,7 @@ impl Ui { window.set_titlebar(Some(&comps.header_bar)); let mut shell = self.shell.borrow_mut(); - window.add(&*shell.drawing_area()); + window.add(&**shell); window.show_all(); window.set_title("Neovim-gtk"); @@ -125,6 +125,7 @@ impl Ui { shell.add_configure_event(); shell.init_nvim(nvim_bin_path); + shell.grab_focus(); if open_path.is_some() { shell.open_file(open_path.unwrap());