2017-04-19 08:40:53 +00:00
|
|
|
use std::rc::Rc;
|
2017-05-23 15:17:45 +00:00
|
|
|
use std::cell::RefCell;
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-17 15:25:50 +00:00
|
|
|
use gtk;
|
2017-04-19 08:40:53 +00:00
|
|
|
use gtk::prelude::*;
|
|
|
|
use glib;
|
|
|
|
use pango::FontDescription;
|
|
|
|
use gdk::{EventButton, EventType};
|
|
|
|
|
|
|
|
use neovim_lib::{Neovim, NeovimApi};
|
|
|
|
|
|
|
|
use nvim::ErrorReport;
|
|
|
|
|
|
|
|
use input;
|
|
|
|
|
|
|
|
const MIN_CONTENT_HEIGHT: i32 = 250;
|
|
|
|
|
2017-05-22 15:38:36 +00:00
|
|
|
struct State {
|
2017-05-23 15:17:45 +00:00
|
|
|
nvim: Option<Rc<RefCell<Neovim>>>,
|
2017-05-22 15:38:36 +00:00
|
|
|
renderer: gtk::CellRendererText,
|
2017-05-23 15:17:45 +00:00
|
|
|
tree: gtk::TreeView,
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
|
2017-05-22 15:38:36 +00:00
|
|
|
impl State {
|
2017-05-23 15:17:45 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
State {
|
|
|
|
nvim: None,
|
|
|
|
renderer: gtk::CellRendererText::new(),
|
|
|
|
tree: gtk::TreeView::new(),
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
fn before_show(&mut self,
|
|
|
|
nvim: &Rc<RefCell<Neovim>>,
|
|
|
|
font_desc: &FontDescription,
|
|
|
|
menu_items: &Vec<Vec<&str>>,
|
|
|
|
selected: i64) {
|
|
|
|
if self.nvim.is_none() {
|
|
|
|
self.nvim = Some(nvim.clone());
|
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
self.update_tree(menu_items, font_desc);
|
|
|
|
self.select(selected);
|
|
|
|
}
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
fn update_tree(&self, menu: &Vec<Vec<&str>>, font_desc: &FontDescription) {
|
|
|
|
if menu.is_empty() {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
self.renderer
|
|
|
|
.set_property_font(Some(&font_desc.to_string()));
|
|
|
|
|
|
|
|
let col_count = menu.get(0).unwrap().len();
|
|
|
|
let columns = self.tree.get_columns();
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
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<u32> = (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[..]);
|
|
|
|
}
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
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);
|
2017-05-22 15:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn select(&self, selected: i64) {
|
|
|
|
if selected >= 0 {
|
|
|
|
let selected_path = gtk::TreePath::new_from_string(&format!("{}", selected));
|
|
|
|
self.tree.get_selection().select_path(&selected_path);
|
|
|
|
self.tree
|
|
|
|
.scroll_to_cell(Some(&selected_path), None, false, 0.0, 0.0);
|
|
|
|
} else {
|
|
|
|
self.tree.get_selection().unselect_all();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PopupMenu {
|
|
|
|
popover: gtk::Popover,
|
2017-05-23 15:17:45 +00:00
|
|
|
open: bool,
|
|
|
|
|
|
|
|
state: Rc<RefCell<State>>,
|
2017-05-22 15:38:36 +00:00
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-22 15:38:36 +00:00
|
|
|
impl PopupMenu {
|
|
|
|
pub fn new(drawing: >k::DrawingArea) -> PopupMenu {
|
2017-05-23 15:17:45 +00:00
|
|
|
let state = State::new();
|
2017-05-22 15:38:36 +00:00
|
|
|
let popover = gtk::Popover::new(Some(drawing));
|
|
|
|
popover.set_modal(false);
|
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
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);
|
2017-05-22 15:38:36 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
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)
|
|
|
|
});
|
2017-05-22 15:38:36 +00:00
|
|
|
let state_ref = state.clone();
|
2017-05-17 15:25:50 +00:00
|
|
|
popover.connect_key_press_event(move |_, ev| {
|
2017-05-23 15:17:45 +00:00
|
|
|
let state = state_ref.borrow();
|
|
|
|
let mut nvim = state.nvim.as_ref().unwrap().borrow_mut();
|
|
|
|
input::gtk_key_press(&mut *nvim, ev)
|
|
|
|
});
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
PopupMenu {
|
|
|
|
popover,
|
|
|
|
state,
|
|
|
|
open: false,
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 08:40:53 +00:00
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
pub fn is_open(&self) -> bool {
|
|
|
|
self.open
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
pub fn show(&mut self,
|
|
|
|
nvim: &Rc<RefCell<Neovim>>,
|
2017-05-22 15:38:36 +00:00
|
|
|
font_desc: &FontDescription,
|
|
|
|
menu_items: &Vec<Vec<&str>>,
|
|
|
|
selected: i64,
|
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
width: i32,
|
|
|
|
height: i32) {
|
2017-05-23 15:17:45 +00:00
|
|
|
|
|
|
|
self.open = true;
|
|
|
|
|
|
|
|
self.popover
|
|
|
|
.set_pointing_to(>k::Rectangle {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
});
|
2017-05-22 15:38:36 +00:00
|
|
|
self.state
|
2017-05-23 15:17:45 +00:00
|
|
|
.borrow_mut()
|
|
|
|
.before_show(&nvim, font_desc, menu_items, selected);
|
2017-05-21 13:52:49 +00:00
|
|
|
self.popover.popup();
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
|
2017-05-23 15:17:45 +00:00
|
|
|
pub fn hide(&mut self) {
|
|
|
|
self.open = false;
|
2017-05-22 15:38:36 +00:00
|
|
|
self.popover.popdown();
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn select(&self, selected: i64) {
|
2017-05-23 15:17:45 +00:00
|
|
|
self.state.borrow().select(selected);
|
2017-04-19 08:40:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-17 15:25:50 +00:00
|
|
|
fn tree_button_press(tree: >k::TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit {
|
2017-04-19 08:40:53 +00:00
|
|
|
if ev.get_event_type() != EventType::ButtonPress {
|
|
|
|
return Inhibit(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
let (paths, ..) = tree.get_selection().get_selected_rows();
|
|
|
|
let selected_idx = if !paths.is_empty() {
|
|
|
|
let ids = paths[0].get_indices();
|
2017-05-17 15:25:50 +00:00
|
|
|
if !ids.is_empty() { ids[0] } else { -1 }
|
2017-04-19 08:40:53 +00:00
|
|
|
} else {
|
|
|
|
-1
|
|
|
|
};
|
|
|
|
|
|
|
|
let (x, y) = ev.get_position();
|
|
|
|
if let Some((Some(tree_path), ..)) = tree.get_path_at_pos(x as i32, y as i32) {
|
|
|
|
let target_idx = tree_path.get_indices()[0];
|
|
|
|
|
|
|
|
let scroll_count = find_scroll_count(selected_idx, target_idx);
|
|
|
|
|
|
|
|
let mut apply_command = String::new();
|
|
|
|
|
|
|
|
for _ in 0..scroll_count {
|
|
|
|
if target_idx > selected_idx {
|
|
|
|
apply_command.push_str("<C-n>");
|
|
|
|
} else {
|
|
|
|
apply_command.push_str("<C-p>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
apply_command.push_str("<C-y>");
|
|
|
|
|
|
|
|
nvim.input(&apply_command).report_err(nvim);
|
|
|
|
}
|
|
|
|
|
|
|
|
Inhibit(false)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find_scroll_count(selected_idx: i32, target_idx: i32) -> i32 {
|
|
|
|
if selected_idx < 0 {
|
|
|
|
target_idx + 1
|
|
|
|
} else if target_idx > selected_idx {
|
|
|
|
target_idx - selected_idx
|
|
|
|
} else {
|
|
|
|
selected_idx - target_idx
|
|
|
|
}
|
|
|
|
}
|