Implement external popup menu
As solution have some restrictions - it disabled by default
This commit is contained in:
parent
b2ee054d6d
commit
1c4e43a26c
@ -15,11 +15,14 @@ call rpcnotify(1, 'Gui', 'Font', 'DejaVu Sans Mono 12')
|
||||
```
|
||||
|
||||
# Command line
|
||||
As this project uses gtk-rs, custom option by GtkApplication not supported yet.
|
||||
There is workaround to pass nvim execution path.
|
||||
* pass nvim custom execution path (by default used `nvim` command)
|
||||
```
|
||||
cargo run -- --nvim-bin-path=E:\Neovim\bin\nvim.exe
|
||||
```
|
||||
* enable external popup menu autocompletion menu (this function a bit limited, so disabled by default)
|
||||
```
|
||||
cargo run -- --enable-external-popup
|
||||
```
|
||||
|
||||
# Build
|
||||
## Linux
|
||||
|
13
src/input.rs
13
src/input.rs
@ -1,7 +1,9 @@
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gdk;
|
||||
use gdk::EventKey;
|
||||
use phf;
|
||||
use neovim_lib::{Neovim, NeovimApi};
|
||||
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
@ -74,3 +76,14 @@ pub fn convert_key(ev: &EventKey) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gtk_key_press(nvim: &mut Neovim, ev: &EventKey) -> Inhibit {
|
||||
if let Some(input) = convert_key(ev) {
|
||||
debug!("nvim_input -> {}", input);
|
||||
nvim.input(&input)
|
||||
.expect("Error run input command to nvim");
|
||||
Inhibit(true)
|
||||
} else {
|
||||
Inhibit(false)
|
||||
}
|
||||
}
|
||||
|
21
src/main.rs
21
src/main.rs
@ -22,6 +22,7 @@ mod input;
|
||||
mod settings;
|
||||
mod cursor;
|
||||
mod shell_dlg;
|
||||
mod popup_menu;
|
||||
|
||||
use std::env;
|
||||
use gio::ApplicationExt;
|
||||
@ -29,6 +30,7 @@ use gio::ApplicationExt;
|
||||
use ui::Ui;
|
||||
|
||||
const BIN_PATH_ARG: &'static str = "--nvim-bin-path";
|
||||
const ENABLE_EXTERNAL_POPUP: &'static str = "--enable-external-popup";
|
||||
|
||||
fn main() {
|
||||
env_logger::init().expect("Can't initialize env_logger");
|
||||
@ -41,6 +43,7 @@ fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let mut argv: Vec<&str> = args.iter()
|
||||
.filter(|a| !a.starts_with(BIN_PATH_ARG))
|
||||
.filter(|a| !a.starts_with(ENABLE_EXTERNAL_POPUP))
|
||||
.map(String::as_str)
|
||||
.collect();
|
||||
if open_arg().is_some() {
|
||||
@ -54,6 +57,7 @@ fn activate(app: >k::Application) {
|
||||
|
||||
ui.init(app,
|
||||
nvim_bin_path(std::env::args()).as_ref(),
|
||||
external_popup(std::env::args()),
|
||||
open_arg().as_ref());
|
||||
}
|
||||
|
||||
@ -66,6 +70,15 @@ fn nvim_bin_path<I>(args: I) -> Option<String>
|
||||
.unwrap_or(None)
|
||||
}
|
||||
|
||||
fn external_popup<I>(args: I) -> bool
|
||||
where I: Iterator<Item = String>
|
||||
{
|
||||
args.filter(|a| a.starts_with(ENABLE_EXTERNAL_POPUP))
|
||||
.map(|_| true)
|
||||
.nth(0)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn open_arg() -> Option<String> {
|
||||
open_arg_impl(std::env::args())
|
||||
}
|
||||
@ -87,6 +100,14 @@ fn open_arg_impl<I>(args: I) -> Option<String>
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_external_menu() {
|
||||
assert_eq!(true,
|
||||
nvim_bin_path(vec!["neovim-gtk", "--enable-external-popup"]
|
||||
.iter()
|
||||
.map(|s| s.to_string())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bin_path_arg() {
|
||||
assert_eq!(Some("/test_path".to_string()),
|
||||
|
123
src/nvim.rs
123
src/nvim.rs
@ -38,6 +38,17 @@ pub trait RedrawEvents {
|
||||
fn on_mouse(&mut self, on: bool) -> RepaintMode;
|
||||
|
||||
fn on_busy(&mut self, busy: bool) -> RepaintMode;
|
||||
|
||||
fn popupmenu_show(&mut self,
|
||||
menu: &Vec<Vec<&str>>,
|
||||
selected: i64,
|
||||
row: u64,
|
||||
col: u64)
|
||||
-> RepaintMode;
|
||||
|
||||
fn popupmenu_hide(&mut self) -> RepaintMode;
|
||||
|
||||
fn popupmenu_select(&mut self, selected: i64) -> RepaintMode;
|
||||
}
|
||||
|
||||
pub trait GuiApi {
|
||||
@ -45,28 +56,20 @@ pub trait GuiApi {
|
||||
}
|
||||
|
||||
macro_rules! try_str {
|
||||
($exp:expr) => (match $exp.as_str() {
|
||||
Some(val) => val,
|
||||
_ => return Err("Can't convert argument to string".to_owned())
|
||||
})
|
||||
($exp:expr) => ($exp.as_str().ok_or("Can't convert argument to string".to_owned())?)
|
||||
}
|
||||
|
||||
macro_rules! try_int {
|
||||
($expr:expr) => (match $expr.as_i64() {
|
||||
Some(val) => val,
|
||||
_ => return Err("Can't convert argument to int".to_owned())
|
||||
})
|
||||
($expr:expr) => ($expr.as_i64().ok_or("Can't convert argument to int".to_owned())?)
|
||||
}
|
||||
|
||||
macro_rules! try_uint {
|
||||
($exp:expr) => (match $exp.as_u64() {
|
||||
Some(val) => val,
|
||||
_ => return Err("Can't convert argument to u64".to_owned())
|
||||
})
|
||||
($exp:expr) => ($exp.as_u64().ok_or("Can't convert argument to u64".to_owned())?)
|
||||
}
|
||||
|
||||
pub fn initialize(shell: Arc<UiMutex<shell::State>>,
|
||||
nvim_bin_path: Option<&String>)
|
||||
nvim_bin_path: Option<&String>,
|
||||
external_popup: bool)
|
||||
-> Result<Neovim> {
|
||||
let session = if let Some(path) = nvim_bin_path {
|
||||
Session::new_child_path(path)?
|
||||
@ -78,7 +81,9 @@ pub fn initialize(shell: Arc<UiMutex<shell::State>>,
|
||||
|
||||
nvim.session
|
||||
.start_event_loop_handler(NvimHandler::new(shell));
|
||||
nvim.ui_attach(80, 24, UiAttachOptions::new())
|
||||
let mut opts = UiAttachOptions::new();
|
||||
opts.set_popupmenu_external(external_popup);
|
||||
nvim.ui_attach(80, 24, opts)
|
||||
.map_err(|e| Error::new(ErrorKind::Other, e))?;
|
||||
nvim.command("runtime! ginit.vim")
|
||||
.map_err(|e| Error::new(ErrorKind::Other, e))?;
|
||||
@ -181,41 +186,63 @@ fn call(ui: &mut shell::State,
|
||||
method: &str,
|
||||
args: &Vec<Value>)
|
||||
-> result::Result<RepaintMode, String> {
|
||||
Ok(match method {
|
||||
"cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])),
|
||||
"put" => ui.on_put(try_str!(args[0])),
|
||||
"clear" => ui.on_clear(),
|
||||
"resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])),
|
||||
"highlight_set" => {
|
||||
if let Value::Map(ref attrs) = args[0] {
|
||||
ui.on_highlight_set(attrs);
|
||||
} else {
|
||||
panic!("Supports only map value as argument");
|
||||
let repaint_mode = match method {
|
||||
"cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])),
|
||||
"put" => ui.on_put(try_str!(args[0])),
|
||||
"clear" => ui.on_clear(),
|
||||
"resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])),
|
||||
"highlight_set" => {
|
||||
if let Value::Map(ref attrs) = args[0] {
|
||||
ui.on_highlight_set(attrs);
|
||||
} else {
|
||||
panic!("Supports only map value as argument");
|
||||
}
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
"eol_clear" => ui.on_eol_clear(),
|
||||
"set_scroll_region" => {
|
||||
ui.on_set_scroll_region(try_uint!(args[0]),
|
||||
try_uint!(args[1]),
|
||||
try_uint!(args[2]),
|
||||
try_uint!(args[3]));
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
"scroll" => ui.on_scroll(try_int!(args[0])),
|
||||
"update_bg" => ui.on_update_bg(try_int!(args[0])),
|
||||
"update_fg" => ui.on_update_fg(try_int!(args[0])),
|
||||
"update_sp" => ui.on_update_sp(try_int!(args[0])),
|
||||
"mode_change" => ui.on_mode_change(try_str!(args[0])),
|
||||
"mouse_on" => ui.on_mouse(true),
|
||||
"mouse_off" => ui.on_mouse(false),
|
||||
"busy_start" => ui.on_busy(true),
|
||||
"busy_stop" => ui.on_busy(false),
|
||||
_ => {
|
||||
println!("Event {}({:?})", method, args);
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
})
|
||||
"eol_clear" => ui.on_eol_clear(),
|
||||
"set_scroll_region" => {
|
||||
ui.on_set_scroll_region(try_uint!(args[0]),
|
||||
try_uint!(args[1]),
|
||||
try_uint!(args[2]),
|
||||
try_uint!(args[3]));
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
"scroll" => ui.on_scroll(try_int!(args[0])),
|
||||
"update_bg" => ui.on_update_bg(try_int!(args[0])),
|
||||
"update_fg" => ui.on_update_fg(try_int!(args[0])),
|
||||
"update_sp" => ui.on_update_sp(try_int!(args[0])),
|
||||
"mode_change" => ui.on_mode_change(try_str!(args[0])),
|
||||
"mouse_on" => ui.on_mouse(true),
|
||||
"mouse_off" => ui.on_mouse(false),
|
||||
"busy_start" => ui.on_busy(true),
|
||||
"busy_stop" => ui.on_busy(false),
|
||||
"popupmenu_show" => {
|
||||
let mut menu_items = Vec::new();
|
||||
|
||||
let items = args[0].as_array().ok_or("Error get menu list array")?;
|
||||
for item in items {
|
||||
let item_line: result::Result<Vec<_>, &str> = item.as_array()
|
||||
.ok_or("Error get menu item array")?
|
||||
.iter()
|
||||
.map(|col| col.as_str().ok_or("Error get menu column"))
|
||||
.collect();
|
||||
menu_items.push(item_line?);
|
||||
}
|
||||
|
||||
ui.popupmenu_show(&menu_items,
|
||||
try_int!(args[1]),
|
||||
try_uint!(args[2]),
|
||||
try_uint!(args[3]))
|
||||
}
|
||||
"popupmenu_hide" => ui.popupmenu_hide(),
|
||||
"popupmenu_select" => ui.popupmenu_select(try_int!(args[0])),
|
||||
_ => {
|
||||
println!("Event {}({:?})", method, args);
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
};
|
||||
|
||||
Ok(repaint_mode)
|
||||
}
|
||||
|
||||
pub trait ErrorReport {
|
||||
|
171
src/popup_menu.rs
Normal file
171
src/popup_menu.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Window, WindowType, TreeView, TreeViewColumn, TreePath, CellRendererText, ListStore,
|
||||
Type, ScrolledWindow, PolicyType};
|
||||
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;
|
||||
|
||||
pub struct PopupMenu {
|
||||
menu: Window,
|
||||
list: TreeView,
|
||||
}
|
||||
|
||||
impl PopupMenu {
|
||||
pub fn new(nvim: Rc<RefCell<Neovim>>,
|
||||
font_desc: &FontDescription,
|
||||
menu: &Vec<Vec<&str>>,
|
||||
selected: i64,
|
||||
x: i32,
|
||||
y: i32,
|
||||
grow_up: bool)
|
||||
-> PopupMenu {
|
||||
let win = Window::new(WindowType::Popup);
|
||||
|
||||
let tree = create_list(menu, 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 = ScrolledWindow::new(None, None);
|
||||
scroll.set_policy(PolicyType::Never, PolicyType::Automatic);
|
||||
scroll.set_min_content_height(MIN_CONTENT_HEIGHT);
|
||||
|
||||
scroll.add(&tree);
|
||||
win.add(&scroll);
|
||||
if grow_up {
|
||||
win.move_(x, y - MIN_CONTENT_HEIGHT);
|
||||
} else {
|
||||
win.move_(x, y);
|
||||
}
|
||||
|
||||
win.connect_key_press_event(move |_, ev| input::gtk_key_press(&mut *nvim.borrow_mut(), ev));
|
||||
|
||||
let popup = PopupMenu {
|
||||
menu: win,
|
||||
list: tree,
|
||||
};
|
||||
|
||||
popup.select(selected);
|
||||
|
||||
popup
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
self.menu.show_all();
|
||||
}
|
||||
|
||||
pub fn hide(self) {
|
||||
self.menu.destroy();
|
||||
}
|
||||
|
||||
pub fn select(&self, selected: i64) {
|
||||
if selected >= 0 {
|
||||
let selected_path = TreePath::new_from_string(&format!("{}", selected));
|
||||
self.list
|
||||
.get_selection()
|
||||
.select_path(&selected_path);
|
||||
self.list.scroll_to_cell(Some(&selected_path), None, false, 0.0, 0.0);
|
||||
} else {
|
||||
self.list.get_selection().unselect_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_list(menu: &Vec<Vec<&str>>, font_desc: &FontDescription) -> TreeView {
|
||||
let tree = 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 = ListStore::new(&vec![Type::String; columns]);
|
||||
let all_column_ids: Vec<u32> = (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: &TreeView, id: i32, font_str: &str) {
|
||||
let renderer = CellRendererText::new();
|
||||
renderer.set_property_font(Some(font_str));
|
||||
|
||||
let column = TreeViewColumn::new();
|
||||
column.pack_start(&renderer, true);
|
||||
column.add_attribute(&renderer, "text", id);
|
||||
tree.append_column(&column);
|
||||
}
|
||||
|
||||
fn tree_button_press(tree: &TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit {
|
||||
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();
|
||||
if !ids.is_empty() {
|
||||
ids[0]
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
154
src/shell.rs
154
src/shell.rs
@ -1,14 +1,14 @@
|
||||
use std::string::String;
|
||||
use std::cell::{Ref, RefMut, RefCell};
|
||||
use std::rc::Rc;
|
||||
use std::sync;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cairo;
|
||||
use pangocairo as pc;
|
||||
use pango;
|
||||
use pango::FontDescription;
|
||||
use gdk::{ModifierType, EventKey, EventConfigure, EventButton, EventMotion, EventType,
|
||||
EventScroll, ScrollDirection};
|
||||
use gdk::{ModifierType, EventConfigure, EventButton, EventMotion, EventType, EventScroll,
|
||||
ScrollDirection};
|
||||
use gdk_sys;
|
||||
use glib;
|
||||
use gtk::prelude::*;
|
||||
@ -20,10 +20,12 @@ use settings::{Settings, FontSource};
|
||||
use ui_model::{UiModel, Cell, Attrs, Color, ModelRect, COLOR_BLACK, COLOR_WHITE, COLOR_RED};
|
||||
use nvim;
|
||||
use nvim::{RedrawEvents, GuiApi, RepaintMode, ErrorReport};
|
||||
use input::{convert_key, keyval_to_input_string};
|
||||
use input;
|
||||
use input::keyval_to_input_string;
|
||||
use cursor::Cursor;
|
||||
use ui;
|
||||
use ui::UiMutex;
|
||||
use popup_menu::PopupMenu;
|
||||
|
||||
const DEFAULT_FONT_NAME: &'static str = "DejaVu Sans Mono 12";
|
||||
|
||||
@ -44,19 +46,22 @@ pub struct State {
|
||||
pub mode: NvimMode,
|
||||
mouse_enabled: bool,
|
||||
drawing_area: DrawingArea,
|
||||
nvim: Option<Neovim>,
|
||||
nvim: Option<Rc<RefCell<Neovim>>>,
|
||||
font_desc: FontDescription,
|
||||
cursor: Option<Cursor>,
|
||||
popup_menu: Option<PopupMenu>,
|
||||
settings: Rc<RefCell<Settings>>,
|
||||
|
||||
line_height: Option<f64>,
|
||||
char_width: Option<f64>,
|
||||
request_width: bool,
|
||||
resize_timer: Option<glib::SourceId>,
|
||||
|
||||
parent: sync::Weak<UiMutex<ui::Components>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(settings: Rc<RefCell<Settings>>) -> State {
|
||||
pub fn new(settings: Rc<RefCell<Settings>>, parent: &Arc<UiMutex<ui::Components>>) -> State {
|
||||
State {
|
||||
model: UiModel::new(24, 80),
|
||||
drawing_area: DrawingArea::new(),
|
||||
@ -69,17 +74,20 @@ impl State {
|
||||
mouse_enabled: true,
|
||||
font_desc: FontDescription::from_string(DEFAULT_FONT_NAME),
|
||||
cursor: None,
|
||||
popup_menu: None,
|
||||
settings: settings,
|
||||
|
||||
line_height: None,
|
||||
char_width: None,
|
||||
resize_timer: None,
|
||||
request_width: true,
|
||||
|
||||
parent: Arc::downgrade(parent),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nvim(&mut self) -> &mut Neovim {
|
||||
self.nvim.as_mut().unwrap()
|
||||
pub fn nvim(&self) -> RefMut<Neovim> {
|
||||
self.nvim.as_ref().unwrap().borrow_mut()
|
||||
}
|
||||
|
||||
fn create_pango_font(&self) -> FontDescription {
|
||||
@ -114,6 +122,13 @@ impl State {
|
||||
fn request_width(&mut self) {
|
||||
self.request_width = true;
|
||||
}
|
||||
|
||||
fn close_popup_menu(&self) {
|
||||
if self.popup_menu.is_some() {
|
||||
let mut nvim = self.nvim();
|
||||
nvim.input("<Esc>").report_err(&mut *nvim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UiState {
|
||||
@ -127,14 +142,14 @@ impl UiState {
|
||||
}
|
||||
|
||||
pub struct Shell {
|
||||
state: Arc<UiMutex<State>>,
|
||||
pub state: Arc<UiMutex<State>>,
|
||||
ui_state: Rc<RefCell<UiState>>,
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
pub fn new(settings: Rc<RefCell<Settings>>) -> Shell {
|
||||
pub fn new(settings: Rc<RefCell<Settings>>, parent: &Arc<UiMutex<ui::Components>>) -> Shell {
|
||||
let shell = Shell {
|
||||
state: Arc::new(UiMutex::new(State::new(settings))),
|
||||
state: Arc::new(UiMutex::new(State::new(settings, parent))),
|
||||
ui_state: Rc::new(RefCell::new(UiState::new())),
|
||||
};
|
||||
|
||||
@ -144,7 +159,7 @@ impl Shell {
|
||||
shell
|
||||
}
|
||||
|
||||
pub fn init(&mut self, parent: Arc<UiMutex<ui::Components>>) {
|
||||
pub fn init(&mut self) {
|
||||
let state = self.state.borrow_mut();
|
||||
state.drawing_area.set_size_request(500, 300);
|
||||
state.drawing_area.set_hexpand(true);
|
||||
@ -190,13 +205,21 @@ impl Shell {
|
||||
state
|
||||
.drawing_area
|
||||
.connect_draw(move |_, ctx| {
|
||||
gtk_draw(&*parent.borrow(), &mut *ref_state.borrow_mut(), ctx)
|
||||
let mut state = ref_state.borrow_mut();
|
||||
let ref_parent = sync::Weak::upgrade(&state.parent).unwrap();
|
||||
let parent = ref_parent.borrow();
|
||||
gtk_draw(&*parent, &mut *state, ctx)
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state
|
||||
.drawing_area
|
||||
.connect_key_press_event(move |_, ev| gtk_key_press(&mut *ref_state.borrow_mut(), ev));
|
||||
.connect_key_press_event(move |_, ev| {
|
||||
let mut shell = ref_state.borrow_mut();
|
||||
shell.cursor.as_mut().unwrap().reset_state();
|
||||
let mut nvim = shell.nvim();
|
||||
input::gtk_key_press(&mut *nvim, ev)
|
||||
});
|
||||
|
||||
let ref_state = self.state.clone();
|
||||
state
|
||||
@ -222,10 +245,12 @@ impl Shell {
|
||||
Ref::map(self.state(), |s| &s.drawing_area)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn redraw(&self, mode: &RepaintMode) {
|
||||
self.state.borrow_mut().on_redraw(mode);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn set_font_desc(&self, font_name: &str) {
|
||||
self.state.borrow_mut().set_font_desc(font_name);
|
||||
}
|
||||
@ -241,26 +266,28 @@ impl Shell {
|
||||
state.cursor.as_mut().unwrap().start();
|
||||
}
|
||||
|
||||
pub fn init_nvim(&mut self, nvim_bin_path: Option<&String>) {
|
||||
pub fn init_nvim(&mut self, nvim_bin_path: Option<&String>, external_popup: bool) {
|
||||
let nvim =
|
||||
nvim::initialize(self.state.clone(), nvim_bin_path).expect("Can't start nvim instance");
|
||||
nvim::initialize(self.state.clone(), nvim_bin_path, external_popup).expect("Can't start nvim instance");
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.nvim = Some(nvim);
|
||||
state.nvim = Some(Rc::new(RefCell::new(nvim)));
|
||||
state.request_width();
|
||||
}
|
||||
|
||||
pub fn open_file(&self, path: &str) {
|
||||
let mut nvim = self.nvim();
|
||||
let state = self.state.borrow();
|
||||
let mut nvim = state.nvim();
|
||||
nvim.command(&format!("e {}", path))
|
||||
.report_err(&mut *nvim);
|
||||
}
|
||||
|
||||
pub fn detach_ui(&mut self) {
|
||||
self.nvim().ui_detach().expect("Error in ui_detach");
|
||||
let state = self.state.borrow();
|
||||
state.nvim().ui_detach().expect("Error in ui_detach");
|
||||
}
|
||||
|
||||
pub fn edit_paste(&self) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
let state = self.state.borrow();
|
||||
let paste_command = if state.mode == NvimMode::Normal {
|
||||
"\"*p"
|
||||
} else {
|
||||
@ -268,18 +295,14 @@ impl Shell {
|
||||
};
|
||||
|
||||
let mut nvim = state.nvim();
|
||||
nvim.input(paste_command).report_err(nvim);
|
||||
nvim.input(paste_command).report_err(&mut *nvim);
|
||||
}
|
||||
|
||||
pub fn edit_save_all(&self) {
|
||||
let mut nvim = &mut *self.nvim();
|
||||
let state = self.state.borrow();
|
||||
let mut nvim = &mut *state.nvim();
|
||||
nvim.command(":wa").report_err(nvim);
|
||||
}
|
||||
|
||||
pub fn nvim(&self) -> RefMut<Neovim> {
|
||||
let state = self.state.borrow_mut();
|
||||
RefMut::map(state, |s| s.nvim())
|
||||
}
|
||||
}
|
||||
|
||||
fn gtk_focus_in(state: &mut State) -> Inhibit {
|
||||
@ -293,6 +316,8 @@ fn gtk_focus_out(state: &mut State) -> Inhibit {
|
||||
state.cursor.as_mut().unwrap().leave_focus();
|
||||
let point = state.model.cur_point();
|
||||
state.on_redraw(&RepaintMode::Area(point));
|
||||
|
||||
state.close_popup_menu();
|
||||
Inhibit(false)
|
||||
}
|
||||
|
||||
@ -301,6 +326,8 @@ fn gtk_scroll_event(state: &mut State, ev: &EventScroll) -> Inhibit {
|
||||
return Inhibit(false);
|
||||
}
|
||||
|
||||
state.close_popup_menu();
|
||||
|
||||
match ev.as_ref().direction {
|
||||
ScrollDirection::Right => {
|
||||
mouse_input(state,
|
||||
@ -354,7 +381,7 @@ fn mouse_input(shell: &mut State,
|
||||
if let Some(line_height) = shell.line_height {
|
||||
if let Some(char_width) = shell.char_width {
|
||||
|
||||
let nvim = shell.nvim();
|
||||
let mut nvim = shell.nvim();
|
||||
let (x, y) = position;
|
||||
let col = (x / char_width).trunc() as u64;
|
||||
let row = (y / line_height).trunc() as u64;
|
||||
@ -380,20 +407,6 @@ fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion
|
||||
Inhibit(false)
|
||||
}
|
||||
|
||||
fn gtk_key_press(shell: &mut State, ev: &EventKey) -> Inhibit {
|
||||
if let Some(input) = convert_key(ev) {
|
||||
debug!("nvim_input -> {}", input);
|
||||
shell
|
||||
.nvim()
|
||||
.input(&input)
|
||||
.expect("Error run input command to nvim");
|
||||
shell.cursor.as_mut().unwrap().reset_state();
|
||||
Inhibit(true)
|
||||
} else {
|
||||
Inhibit(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn gtk_draw(parent: &ui::Components, state: &mut State, ctx: &cairo::Context) -> Inhibit {
|
||||
if state.line_height.is_none() {
|
||||
let (width, height) = calc_char_bounds(state, ctx);
|
||||
@ -789,6 +802,63 @@ impl RedrawEvents for State {
|
||||
}
|
||||
RepaintMode::Area(self.model.cur_point())
|
||||
}
|
||||
|
||||
fn popupmenu_show(&mut self,
|
||||
menu: &Vec<Vec<&str>>,
|
||||
selected: i64,
|
||||
row: u64,
|
||||
col: u64)
|
||||
-> RepaintMode {
|
||||
match (&self.line_height, &self.char_width) {
|
||||
(&Some(line_height), &Some(char_width)) => {
|
||||
let parent = sync::Weak::upgrade(&self.parent).unwrap();
|
||||
let comps = parent.borrow();
|
||||
let window = comps.window();
|
||||
let screen = window.get_screen().unwrap();
|
||||
let height = screen.get_height();
|
||||
|
||||
let point = ModelRect::point((col + 1) as usize, (row + 1) as usize);
|
||||
let (x, y, ..) = point.to_area(line_height, char_width);
|
||||
let translated = self.drawing_area.translate_coordinates(window, x, y);
|
||||
let (x, y) = if let Some((x, y)) = translated {
|
||||
(x, y)
|
||||
} else {
|
||||
(x, y)
|
||||
};
|
||||
|
||||
let (win_x, win_y) = window.get_position();
|
||||
let (abs_x, mut abs_y) = (win_x + x, win_y + y);
|
||||
|
||||
let grow_up = abs_y > height / 2;
|
||||
|
||||
if grow_up {
|
||||
abs_y -= line_height as i32;
|
||||
}
|
||||
|
||||
self.popup_menu = Some(PopupMenu::new(self.nvim.as_ref().unwrap().clone(),
|
||||
&self.font_desc,
|
||||
menu,
|
||||
selected,
|
||||
abs_x,
|
||||
abs_y,
|
||||
grow_up));
|
||||
self.popup_menu.as_ref().unwrap().show();
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
|
||||
fn popupmenu_hide(&mut self) -> RepaintMode {
|
||||
self.popup_menu.take().unwrap().hide();
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
|
||||
fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
|
||||
self.popup_menu.as_mut().unwrap().select(selected);
|
||||
RepaintMode::Nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl GuiApi for State {
|
||||
|
@ -51,7 +51,8 @@ fn show_not_saved_dlg(comps: &UiMutex<Components>,
|
||||
|
||||
let res = match dlg.run() {
|
||||
SAVE_ID => {
|
||||
let mut nvim = shell.nvim();
|
||||
let state = shell.state.borrow();
|
||||
let mut nvim = state.nvim();
|
||||
match nvim.command("wa") {
|
||||
Err(ref err) => {
|
||||
println!("Error: {}", err);
|
||||
@ -71,7 +72,8 @@ fn show_not_saved_dlg(comps: &UiMutex<Components>,
|
||||
}
|
||||
|
||||
fn get_changed_buffers(shell: &Shell) -> Result<Vec<String>, CallError> {
|
||||
let mut nvim = shell.nvim();
|
||||
let state = shell.state.borrow();
|
||||
let mut nvim = state.nvim();
|
||||
let buffers = nvim.get_buffers().unwrap();
|
||||
|
||||
Ok(buffers
|
||||
|
13
src/ui.rs
13
src/ui.rs
@ -45,13 +45,14 @@ impl Components {
|
||||
|
||||
impl Ui {
|
||||
pub fn new() -> Ui {
|
||||
let comps = Arc::new(UiMutex::new(Components::new()));
|
||||
let settings = Rc::new(RefCell::new(Settings::new()));
|
||||
let shell = Rc::new(RefCell::new(Shell::new(settings.clone())));
|
||||
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), &comps)));
|
||||
settings.borrow_mut().set_shell(Rc::downgrade(&shell));
|
||||
|
||||
Ui {
|
||||
initialized: false,
|
||||
comps: Arc::new(UiMutex::new(Components::new())),
|
||||
comps: comps,
|
||||
shell: shell.clone(),
|
||||
settings: settings,
|
||||
}
|
||||
@ -60,6 +61,7 @@ impl Ui {
|
||||
pub fn init(&mut self,
|
||||
app: >k::Application,
|
||||
nvim_bin_path: Option<&String>,
|
||||
external_popup: bool,
|
||||
open_path: Option<&String>) {
|
||||
if self.initialized {
|
||||
return;
|
||||
@ -90,7 +92,7 @@ impl Ui {
|
||||
paste_btn.connect_clicked(move |_| { shell.borrow_mut().edit_paste(); });
|
||||
comps.header_bar.pack_start(&paste_btn);
|
||||
|
||||
self.shell.borrow_mut().init(self.comps.clone());
|
||||
self.shell.borrow_mut().init();
|
||||
|
||||
comps.window = Some(ApplicationWindow::new(app));
|
||||
let window = comps.window.as_ref().unwrap();
|
||||
@ -108,7 +110,7 @@ impl Ui {
|
||||
window.connect_delete_event(move |_, _| gtk_delete(&*comps_ref, &*shell_ref));
|
||||
|
||||
shell.add_configure_event();
|
||||
shell.init_nvim(nvim_bin_path);
|
||||
shell.init_nvim(nvim_bin_path, external_popup);
|
||||
|
||||
if open_path.is_some() {
|
||||
shell.open_file(open_path.unwrap());
|
||||
@ -118,7 +120,8 @@ impl Ui {
|
||||
}
|
||||
|
||||
fn guard_dispatch_thread(&self, shell: &mut Shell) {
|
||||
let guard = shell.nvim().session.take_dispatch_guard();
|
||||
let state = shell.state.borrow();
|
||||
let guard = state.nvim().session.take_dispatch_guard();
|
||||
|
||||
let comps = self.comps.clone();
|
||||
thread::spawn(move || {
|
||||
|
@ -135,7 +135,7 @@ impl UiModel {
|
||||
}
|
||||
|
||||
pub fn cur_point(&self) -> ModelRect {
|
||||
ModelRect::point(self.cur_row, self.cur_col)
|
||||
ModelRect::point(self.cur_col, self.cur_row)
|
||||
}
|
||||
|
||||
pub fn set_cursor(&mut self, row: usize, col: usize) -> ModelRect {
|
||||
@ -166,7 +166,7 @@ impl UiModel {
|
||||
self.cur_col -= 1;
|
||||
}
|
||||
|
||||
changed_region.join(&ModelRect::point(self.cur_row, self.cur_col));
|
||||
changed_region.join(&ModelRect::point(self.cur_col, self.cur_row));
|
||||
|
||||
changed_region
|
||||
}
|
||||
@ -250,10 +250,10 @@ impl ModelRect {
|
||||
|
||||
pub fn point(x: usize, y: usize) -> ModelRect {
|
||||
ModelRect {
|
||||
top: x,
|
||||
bot: x,
|
||||
left: y,
|
||||
right: y,
|
||||
top: y,
|
||||
bot: y,
|
||||
left: x,
|
||||
right: x,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user