diff --git a/src/popup_menu.rs b/src/popup_menu.rs index 5d0e5a6..6f54fc3 100644 --- a/src/popup_menu.rs +++ b/src/popup_menu.rs @@ -1,5 +1,5 @@ use std::rc::Rc; -use std::cell::{RefCell, Cell}; +use std::cell::RefCell; use gtk; use gtk::prelude::*; @@ -16,54 +16,74 @@ use input; const MIN_CONTENT_HEIGHT: i32 = 250; struct State { - nvim: Rc>, - tree: gtk::TreeView, + nvim: Option>>, renderer: gtk::CellRendererText, + tree: gtk::TreeView, } impl State { - pub fn new(popover: >k::Popover, - nvim: Rc>, - font_desc: &FontDescription, - menu_items: &Vec>, - selected: i64, - x: i32, - y: i32, - width: i32, - height: i32) - -> Self { - let tree = create_list(menu_items, font_desc); - tree.set_can_focus(false); - - let nvim_ref = nvim.clone(); - tree.connect_button_press_event(move |tree, ev| { - tree_button_press(tree, ev, &mut *nvim_ref.borrow_mut()) - }); - - let scroll = gtk::ScrolledWindow::new(None, None); - scroll.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic); - scroll.set_min_content_height(MIN_CONTENT_HEIGHT); - - scroll.add(&tree); - scroll.show_all(); - popover.add(&scroll); - popover.set_pointing_to(>k::Rectangle { - x, - y, - width, - height, - }); - - - let state = State { - nvim, - tree, + pub fn new() -> Self { + State { + nvim: None, renderer: gtk::CellRendererText::new(), - }; + tree: gtk::TreeView::new(), + } + } - state.select(selected); + fn before_show(&mut self, + nvim: &Rc>, + font_desc: &FontDescription, + menu_items: &Vec>, + selected: i64) { + if self.nvim.is_none() { + self.nvim = Some(nvim.clone()); + } - state + self.update_tree(menu_items, font_desc); + self.select(selected); + } + + fn update_tree(&self, menu: &Vec>, font_desc: &FontDescription) { + if menu.is_empty() { + return; + } + + self.renderer + .set_property_font(Some(&font_desc.to_string())); + + let col_count = menu.get(0).unwrap().len(); + let columns = self.tree.get_columns(); + + if columns.len() != col_count { + for col in columns { + self.tree.remove_column(&col); + } + + for i in 0..col_count { + self.append_column(i as i32); + } + } + + let list_store = gtk::ListStore::new(&vec![gtk::Type::String; col_count]); + let all_column_ids: Vec = (0..col_count).map(|i| i as u32).collect(); + + for line in menu { + let line_array: Vec<&glib::ToValue> = + line.iter().map(|v| v as &glib::ToValue).collect(); + list_store.insert_with_values(None, &all_column_ids, &line_array[..]); + } + + self.tree.set_model(Some(&list_store)); + self.tree.set_headers_visible(false); + } + + fn append_column(&self, id: i32) { + let renderer = &self.renderer; + + let column = gtk::TreeViewColumn::new(); + column.pack_start(renderer, true); + column.add_attribute(renderer, "text", id); + self.tree.append_column(&column); } fn select(&self, selected: i64) { @@ -80,30 +100,56 @@ impl State { pub struct PopupMenu { popover: gtk::Popover, - state: Rc>>, + open: bool, + + state: Rc>, } impl PopupMenu { pub fn new(drawing: >k::DrawingArea) -> PopupMenu { - let state = Rc::new(Cell::new(None)); + let state = State::new(); let popover = gtk::Popover::new(Some(drawing)); popover.set_modal(false); + state.tree.set_headers_visible(false); + state.tree.set_can_focus(false); + + let scroll = gtk::ScrolledWindow::new(None, None); + scroll.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic); + scroll.set_min_content_height(MIN_CONTENT_HEIGHT); + + scroll.add(&state.tree); + scroll.show_all(); + popover.add(&scroll); + + let state = Rc::new(RefCell::new(state)); + let state_ref = state.clone(); + state.borrow().tree.connect_button_press_event(move |tree, ev| { + let state = state_ref.borrow(); + let mut nvim = state.nvim.as_ref().unwrap().borrow_mut(); + tree_button_press(tree, ev, &mut *nvim) + }); let state_ref = state.clone(); popover.connect_key_press_event(move |_, ev| { - let mut state: &mut State = state_ref.get_mut().as_mut().unwrap(); - input::gtk_key_press(&mut *state.nvim.borrow_mut(), ev) - }); + let state = state_ref.borrow(); + let mut nvim = state.nvim.as_ref().unwrap().borrow_mut(); + input::gtk_key_press(&mut *nvim, ev) + }); - let popup = PopupMenu { popover, state }; - - - popup + PopupMenu { + popover, + state, + open: false, + } } - pub fn show(&self, - nvim: Rc>, + pub fn is_open(&self) -> bool { + self.open + } + + pub fn show(&mut self, + nvim: &Rc>, font_desc: &FontDescription, menu_items: &Vec>, selected: i64, @@ -111,66 +157,32 @@ impl PopupMenu { y: i32, width: i32, height: i32) { + + self.open = true; + + self.popover + .set_pointing_to(>k::Rectangle { + x, + y, + width, + height, + }); self.state - .replace(Some(State::new(&self.popover, - nvim, - font_desc, - menu_items, - selected, - x, - y, - width, - height))); + .borrow_mut() + .before_show(&nvim, font_desc, menu_items, selected); self.popover.popup(); } - pub fn hide(self) { + pub fn hide(&mut self) { + self.open = false; self.popover.popdown(); - let tree = self.state.get_mut().take().unwrap().tree; - self.popover.remove(&tree); - tree.destroy(); } pub fn select(&self, selected: i64) { + self.state.borrow().select(selected); } } -fn create_list(menu: &Vec>, font_desc: &FontDescription) -> gtk::TreeView { - let tree = gtk::TreeView::new(); - - if menu.is_empty() { - return tree; - } - let columns = menu.get(0).unwrap().len(); - - let font_str = font_desc.to_string(); - for i in 0..columns { - append_column(&tree, i as i32, &font_str); - } - - let list_store = gtk::ListStore::new(&vec![gtk::Type::String; columns]); - let all_column_ids: Vec = (0..columns).map(|i| i as u32).collect(); - - for line in menu { - let line_array: Vec<&glib::ToValue> = line.iter().map(|v| v as &glib::ToValue).collect(); - list_store.insert_with_values(None, &all_column_ids, &line_array[..]); - } - - tree.set_model(Some(&list_store)); - tree.set_headers_visible(false); - - tree -} - -fn append_column(tree: >k::TreeView, id: i32, font_str: &str) { - let renderer = gtk::CellRendererText::new(); - renderer.set_property_font(Some(font_str)); - - let column = gtk::TreeViewColumn::new(); - column.pack_start(&renderer, true); - column.add_attribute(&renderer, "text", id); - tree.append_column(&column); -} fn tree_button_press(tree: >k::TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit { if ev.get_event_type() != EventType::ButtonPress { diff --git a/src/shell.rs b/src/shell.rs index cc41c24..05ae541 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -138,7 +138,7 @@ impl State { } fn close_popup_menu(&self) { - if self.popup_menu.is_some() { + if self.popup_menu.is_open() { let mut nvim = self.nvim(); nvim.input("").report_err(&mut *nvim); } @@ -825,7 +825,7 @@ impl RedrawEvents for State { let point = ModelRect::point(col as usize, row as usize); let (x, y, width, height) = point.to_area(line_height, char_width); - self.popup_menu.show(self.nvim.as_ref().unwrap().clone(), + self.popup_menu.show(self.nvim.as_ref().unwrap(), &self.font_desc, menu, selected,