Use timeout to hide popup menu
this prevent "blinking animation" of menu in case of popup population take some time.
This commit is contained in:
parent
82c7f0818a
commit
e5994b79d4
@ -36,9 +36,7 @@ impl<'a> NeovimRef<'a> {
|
||||
pub fn non_blocked(mut self) -> Option<Self> {
|
||||
self.get_mode().ok_and_report().and_then(|mode| {
|
||||
mode.iter()
|
||||
.find(|kv| {
|
||||
kv.0.as_str().map(|key| key == "blocking").unwrap_or(false)
|
||||
})
|
||||
.find(|kv| kv.0.as_str().map(|key| key == "blocking").unwrap_or(false))
|
||||
.map(|kv| kv.1.as_bool().unwrap_or(false))
|
||||
.and_then(|block| if block { None } else { Some(self) })
|
||||
})
|
||||
@ -71,7 +69,9 @@ pub struct NeovimClientAsync {
|
||||
|
||||
impl NeovimClientAsync {
|
||||
fn new() -> Self {
|
||||
NeovimClientAsync { nvim: Arc::new(Mutex::new(None)) }
|
||||
NeovimClientAsync {
|
||||
nvim: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn borrow(&self) -> Option<NeovimRef> {
|
||||
@ -81,7 +81,9 @@ impl NeovimClientAsync {
|
||||
|
||||
impl Clone for NeovimClientAsync {
|
||||
fn clone(&self) -> Self {
|
||||
NeovimClientAsync { nvim: self.nvim.clone() }
|
||||
NeovimClientAsync {
|
||||
nvim: self.nvim.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,9 +149,9 @@ impl NeovimClient {
|
||||
pub fn nvim(&self) -> Option<NeovimRef> {
|
||||
let nvim = self.nvim.borrow_mut();
|
||||
if nvim.is_some() {
|
||||
Some(NeovimRef::from_nvim(
|
||||
RefMut::map(nvim, |n| n.as_mut().unwrap()),
|
||||
))
|
||||
Some(NeovimRef::from_nvim(RefMut::map(nvim, |n| {
|
||||
n.as_mut().unwrap()
|
||||
})))
|
||||
} else {
|
||||
self.nvim_async.borrow()
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
use std::result;
|
||||
|
||||
use neovim_lib::CallError;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::result;
|
||||
use std::sync::{Arc, mpsc};
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
use neovim_lib::{Handler, Value};
|
||||
|
||||
@ -12,51 +12,60 @@ use super::redraw_handler;
|
||||
|
||||
pub struct NvimHandler {
|
||||
shell: Arc<UiMutex<shell::State>>,
|
||||
|
||||
delayed_redraw_event_id: Arc<UiMutex<Option<glib::SourceId>>>,
|
||||
}
|
||||
|
||||
impl NvimHandler {
|
||||
pub fn new(shell: Arc<UiMutex<shell::State>>) -> NvimHandler {
|
||||
NvimHandler { shell: shell }
|
||||
NvimHandler {
|
||||
shell,
|
||||
delayed_redraw_event_id: Arc::new(UiMutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule_redraw_event(&self, event: Value) {
|
||||
let shell = self.shell.clone();
|
||||
let delayed_redraw_event_id = self.delayed_redraw_event_id.clone();
|
||||
|
||||
glib::idle_add(move || {
|
||||
let id = Some(glib::timeout_add(
|
||||
250,
|
||||
clone!(shell, event, delayed_redraw_event_id => move || {
|
||||
delayed_redraw_event_id.replace(None);
|
||||
|
||||
if let Err(msg) = call_redraw_handler(vec![event.clone()], &shell) {
|
||||
error!("Error call function: {}", msg);
|
||||
}
|
||||
|
||||
glib::Continue(false)
|
||||
}),
|
||||
));
|
||||
|
||||
delayed_redraw_event_id.replace(id);
|
||||
|
||||
glib::Continue(false)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn remove_scheduled_redraw_event(&self) {
|
||||
let delayed_redraw_event_id = self.delayed_redraw_event_id.clone();
|
||||
glib::idle_add(move || {
|
||||
let id = delayed_redraw_event_id.replace(None);
|
||||
if let Some(ev_id) = id {
|
||||
glib::source_remove(ev_id);
|
||||
}
|
||||
|
||||
glib::Continue(false)
|
||||
});
|
||||
}
|
||||
|
||||
fn nvim_cb(&self, method: &str, mut params: Vec<Value>) {
|
||||
match method {
|
||||
"redraw" => {
|
||||
redraw_handler::remove_uneeded_events(&mut params);
|
||||
redraw_handler::remove_or_delay_uneeded_events(self, &mut params);
|
||||
|
||||
self.safe_call(move |ui| {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
let mut repaint_mode = RepaintMode::Nothing;
|
||||
|
||||
for ev in params {
|
||||
if let Value::Array(ev_args) = ev {
|
||||
let mut args_iter = ev_args.into_iter();
|
||||
let ev_name = args_iter.next();
|
||||
if let Some(ev_name) = ev_name {
|
||||
if let Some(ev_name) = ev_name.as_str() {
|
||||
for local_args in args_iter {
|
||||
let args = match local_args {
|
||||
Value::Array(ar) => ar,
|
||||
_ => vec![],
|
||||
};
|
||||
let call_reapint_mode =
|
||||
redraw_handler::call(ui, &ev_name, args)?;
|
||||
repaint_mode = repaint_mode.join(call_reapint_mode);
|
||||
}
|
||||
} else {
|
||||
error!("Unsupported event");
|
||||
}
|
||||
} else {
|
||||
error!("Event name does not exists");
|
||||
}
|
||||
} else {
|
||||
error!("Unsupported event type {:?}", ev);
|
||||
}
|
||||
}
|
||||
|
||||
ui.on_redraw(&repaint_mode);
|
||||
Ok(())
|
||||
});
|
||||
self.safe_call(move |ui| call_redraw_handler(params, ui));
|
||||
}
|
||||
"Gui" => {
|
||||
if !params.is_empty() {
|
||||
@ -68,7 +77,9 @@ impl NvimHandler {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
redraw_handler::call_gui_event(
|
||||
ui,
|
||||
ev_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
||||
ev_name
|
||||
.as_str()
|
||||
.ok_or_else(|| "Event name does not exists")?,
|
||||
args,
|
||||
)?;
|
||||
ui.on_redraw(&RepaintMode::All);
|
||||
@ -96,7 +107,7 @@ impl NvimHandler {
|
||||
}
|
||||
}
|
||||
|
||||
fn nvim_cb_req (&self, method: &str, params: Vec<Value>) -> result::Result<Value, Value> {
|
||||
fn nvim_cb_req(&self, method: &str, params: Vec<Value>) -> result::Result<Value, Value> {
|
||||
match method {
|
||||
"Gui" => {
|
||||
if !params.is_empty() {
|
||||
@ -106,11 +117,15 @@ impl NvimHandler {
|
||||
let args = params_iter.collect();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
self.safe_call(move |ui| {
|
||||
sender.send(redraw_handler::call_gui_request(
|
||||
&ui.clone(),
|
||||
req_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
||||
&args,
|
||||
)).unwrap();
|
||||
sender
|
||||
.send(redraw_handler::call_gui_request(
|
||||
&ui.clone(),
|
||||
req_name
|
||||
.as_str()
|
||||
.ok_or_else(|| "Event name does not exists")?,
|
||||
&args,
|
||||
))
|
||||
.unwrap();
|
||||
{
|
||||
let ui = &mut ui.borrow_mut();
|
||||
ui.on_redraw(&RepaintMode::All);
|
||||
@ -130,7 +145,7 @@ impl NvimHandler {
|
||||
error!("Unsupported request {:?}", params);
|
||||
Err(Value::Nil)
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
error!("Request {}({:?})", method, params);
|
||||
Err(Value::Nil)
|
||||
@ -142,17 +157,59 @@ impl NvimHandler {
|
||||
where
|
||||
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
||||
{
|
||||
let mut cb = Some(cb);
|
||||
let shell = self.shell.clone();
|
||||
glib::idle_add(move || {
|
||||
if let Err(msg) = cb.take().unwrap()(&shell) {
|
||||
error!("Error call function: {}", msg);
|
||||
}
|
||||
glib::Continue(false)
|
||||
});
|
||||
safe_call(self.shell.clone(), cb);
|
||||
}
|
||||
}
|
||||
|
||||
fn call_redraw_handler(
|
||||
params: Vec<Value>,
|
||||
ui: &Arc<UiMutex<shell::State>>,
|
||||
) -> result::Result<(), String> {
|
||||
let ui = &mut ui.borrow_mut();
|
||||
let mut repaint_mode = RepaintMode::Nothing;
|
||||
|
||||
for ev in params {
|
||||
if let Value::Array(ev_args) = ev {
|
||||
let mut args_iter = ev_args.into_iter();
|
||||
let ev_name = args_iter.next();
|
||||
if let Some(ev_name) = ev_name {
|
||||
if let Some(ev_name) = ev_name.as_str() {
|
||||
for local_args in args_iter {
|
||||
let args = match local_args {
|
||||
Value::Array(ar) => ar,
|
||||
_ => vec![],
|
||||
};
|
||||
let call_reapint_mode = redraw_handler::call(ui, &ev_name, args)?;
|
||||
repaint_mode = repaint_mode.join(call_reapint_mode);
|
||||
}
|
||||
} else {
|
||||
error!("Unsupported event");
|
||||
}
|
||||
} else {
|
||||
error!("Event name does not exists");
|
||||
}
|
||||
} else {
|
||||
error!("Unsupported event type {:?}", ev);
|
||||
}
|
||||
}
|
||||
|
||||
ui.on_redraw(&repaint_mode);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn safe_call<F>(shell: Arc<UiMutex<shell::State>>, cb: F)
|
||||
where
|
||||
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
||||
{
|
||||
let mut cb = Some(cb);
|
||||
glib::idle_add(move || {
|
||||
if let Err(msg) = cb.take().unwrap()(&shell) {
|
||||
error!("Error call function: {}", msg);
|
||||
}
|
||||
glib::Continue(false)
|
||||
});
|
||||
}
|
||||
|
||||
impl Handler for NvimHandler {
|
||||
fn handle_notify(&mut self, name: &str, args: Vec<Value>) {
|
||||
self.nvim_cb(name, args);
|
||||
|
@ -10,20 +10,18 @@ pub use self::repaint_mode::RepaintMode;
|
||||
pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef};
|
||||
pub use self::mode_info::{CursorShape, ModeInfo};
|
||||
pub use self::ext::ErrorReport;
|
||||
pub use self::handler::NvimHandler;
|
||||
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Session, UiAttachOptions};
|
||||
|
||||
use misc::escape_filename;
|
||||
use ui::UiMutex;
|
||||
use shell;
|
||||
use nvim_config::NvimConfig;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -85,7 +83,7 @@ fn set_windows_creation_flags(cmd: &mut Command) {
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
shell: Arc<UiMutex<shell::State>>,
|
||||
handler: NvimHandler,
|
||||
nvim_bin_path: Option<&String>,
|
||||
timeout: Option<Duration>,
|
||||
) -> result::Result<Neovim, NvimInitError> {
|
||||
@ -135,8 +133,7 @@ pub fn start(
|
||||
|
||||
let mut nvim = Neovim::new(session);
|
||||
|
||||
nvim.session
|
||||
.start_event_loop_handler(handler::NvimHandler::new(shell));
|
||||
nvim.session.start_event_loop_handler(handler);
|
||||
|
||||
Ok(nvim)
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ pub enum CursorShape {
|
||||
|
||||
impl CursorShape {
|
||||
fn new(shape_code: &Value) -> Result<CursorShape, String> {
|
||||
let str_code = shape_code.as_str().ok_or_else(|| {
|
||||
"Can't convert cursor shape to string".to_owned()
|
||||
})?;
|
||||
let str_code = shape_code
|
||||
.as_str()
|
||||
.ok_or_else(|| "Can't convert cursor shape to string".to_owned())?;
|
||||
|
||||
Ok(match str_code {
|
||||
"block" => CursorShape::Block,
|
||||
|
@ -13,6 +13,7 @@ use rmpv;
|
||||
|
||||
use super::repaint_mode::RepaintMode;
|
||||
use super::mode_info::ModeInfo;
|
||||
use super::handler::NvimHandler;
|
||||
|
||||
macro_rules! try_str {
|
||||
($exp:expr) => ($exp.as_str().ok_or_else(|| "Can't convert argument to string".to_owned())?)
|
||||
@ -247,18 +248,32 @@ pub fn call(
|
||||
Ok(repaint_mode)
|
||||
}
|
||||
|
||||
// menu content update call popupmenu_hide followed by popupmenu_show
|
||||
// Here two cases processed:
|
||||
//
|
||||
// 1. menu content update call popupmenu_hide followed by popupmenu_show in same batch
|
||||
// this generates unneded hide event
|
||||
// so in case we get both events, just romove one
|
||||
pub fn remove_uneeded_events(params: &mut Vec<Value>) {
|
||||
//
|
||||
// 2. hide event postpone in case show event come bit later
|
||||
// but in new event batch
|
||||
pub fn remove_or_delay_uneeded_events(handler: &NvimHandler, params: &mut Vec<Value>) {
|
||||
let mut show_popup_finded = false;
|
||||
let mut to_remove = Vec::new();
|
||||
let mut delayed_hide_event = None;
|
||||
|
||||
for (idx, val) in params.iter().enumerate().rev() {
|
||||
if let Some(args) = val.as_array() {
|
||||
match args[0].as_str() {
|
||||
Some("popupmenu_show") => show_popup_finded = true,
|
||||
Some("popupmenu_hide") if show_popup_finded => {
|
||||
Some("popupmenu_show") => {
|
||||
show_popup_finded = true;
|
||||
handler.remove_scheduled_redraw_event();
|
||||
}
|
||||
Some("popupmenu_hide") if !show_popup_finded && delayed_hide_event.is_none() => {
|
||||
to_remove.push(idx);
|
||||
delayed_hide_event = Some(idx);
|
||||
handler.remove_scheduled_redraw_event();
|
||||
}
|
||||
Some("popupmenu_hide") => {
|
||||
to_remove.push(idx);
|
||||
}
|
||||
_ => (),
|
||||
@ -267,7 +282,12 @@ pub fn remove_uneeded_events(params: &mut Vec<Value>) {
|
||||
}
|
||||
|
||||
to_remove.iter().for_each(|&idx| {
|
||||
params.remove(idx);
|
||||
let ev = params.remove(idx);
|
||||
if let Some(delayed_hide_event_idx) = delayed_hide_event {
|
||||
if delayed_hide_event_idx == idx {
|
||||
handler.schedule_redraw_event(ev);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -290,22 +310,3 @@ impl<'a> CompleteItem<'a> {
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_remove_popup_menu_hide() {
|
||||
// remove only first hide
|
||||
let mut params = vec![
|
||||
Value::from(vec![Value::from("popupmenu_hide")]),
|
||||
Value::from(vec![Value::from("popupmenu_show")]),
|
||||
Value::from(vec![Value::from("popupmenu_hide")]),
|
||||
];
|
||||
|
||||
remove_uneeded_events(&mut params);
|
||||
|
||||
assert_eq!(2, params.len());
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ impl RepaintMode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -32,6 +32,7 @@ struct State {
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
let tree = gtk::TreeView::new();
|
||||
tree.get_selection().set_mode(gtk::SelectionMode::Single);
|
||||
let css_provider = gtk::CssProvider::new();
|
||||
|
||||
let style_context = tree.get_style_context().unwrap();
|
||||
@ -140,9 +141,6 @@ impl State {
|
||||
self.renderer.set_property_foreground_rgba(
|
||||
Some(&color_model.pmenu_fg().into()),
|
||||
);
|
||||
self.renderer.set_property_background_rgba(
|
||||
Some(&color_model.pmenu_bg().into()),
|
||||
);
|
||||
|
||||
self.update_css(color_model);
|
||||
|
||||
@ -164,9 +162,11 @@ impl State {
|
||||
match gtk::CssProviderExt::load_from_data(
|
||||
&self.css_provider,
|
||||
&format!(
|
||||
".view {{ color: {}; background-color: {};}}",
|
||||
".view :selected {{ color: {}; background-color: {};}}\n
|
||||
.view {{ background-color: {}; }}",
|
||||
fg.to_hex(),
|
||||
bg.to_hex()
|
||||
bg.to_hex(),
|
||||
color_model.pmenu_bg().to_hex(),
|
||||
).as_bytes(),
|
||||
) {
|
||||
Err(e) => error!("Can't update css {}", e),
|
||||
|
@ -24,7 +24,7 @@ use settings::{FontSource, Settings};
|
||||
use ui_model::{Attrs, ModelRect, UiModel};
|
||||
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
||||
use nvim::{self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef,
|
||||
RepaintMode};
|
||||
RepaintMode, NvimHandler};
|
||||
use input;
|
||||
use input::keyval_to_input_string;
|
||||
use cursor::{BlinkCursor, Cursor, CursorRedrawCb};
|
||||
@ -919,13 +919,14 @@ fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>
|
||||
|
||||
fn init_nvim_async(
|
||||
state_arc: Arc<UiMutex<State>>,
|
||||
nvim_handler: NvimHandler,
|
||||
options: ShellOptions,
|
||||
cols: usize,
|
||||
rows: usize,
|
||||
) {
|
||||
// execute nvim
|
||||
let nvim = match nvim::start(
|
||||
state_arc.clone(),
|
||||
nvim_handler,
|
||||
options.nvim_bin_path.as_ref(),
|
||||
options.timeout,
|
||||
) {
|
||||
@ -1053,8 +1054,9 @@ fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
|
||||
state.model = UiModel::new(rows as u64, cols as u64);
|
||||
|
||||
let state_arc = state_ref.clone();
|
||||
let nvim_handler = NvimHandler::new(state_ref.clone());
|
||||
let options = state.options.clone();
|
||||
thread::spawn(move || init_nvim_async(state_arc, options, cols, rows));
|
||||
thread::spawn(move || init_nvim_async(state_arc, nvim_handler, options, cols, rows));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user