Close some popups automatically (#1285)

* Add Event::Used to use event callback without consuming

* Close popup if contents ignored event

* collect event results before executing callbacks

* don't add new result variant, use Ignored(..) instead

* break in match cases

* Make auto_close configurable

* fix merge

* auto close hover popups

* fix formatting
imgbot
Bram 2 years ago committed by GitHub
parent e1a92fd399
commit 40eb1268c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2921,7 +2921,7 @@ pub mod cmd {
let call: job::Callback = let call: job::Callback =
Box::new(move |editor: &mut Editor, compositor: &mut Compositor| { Box::new(move |editor: &mut Editor, compositor: &mut Compositor| {
let contents = ui::Markdown::new(contents, editor.syn_loader.clone()); let contents = ui::Markdown::new(contents, editor.syn_loader.clone());
let popup = Popup::new("hover", contents); let popup = Popup::new("hover", contents).auto_close(true);
compositor.replace_or_push("hover", Box::new(popup)); compositor.replace_or_push("hover", Box::new(popup));
}); });
Ok(call) Ok(call)

@ -633,7 +633,7 @@ pub fn hover(cx: &mut Context) {
// skip if contents empty // skip if contents empty
let contents = ui::Markdown::new(contents, editor.syn_loader.clone()); let contents = ui::Markdown::new(contents, editor.syn_loader.clone());
let popup = Popup::new("hover", contents); let popup = Popup::new("hover", contents).auto_close(true);
compositor.replace_or_push("hover", Box::new(popup)); compositor.replace_or_push("hover", Box::new(popup));
} }
}, },

@ -19,7 +19,7 @@ pub type Callback = Box<dyn FnOnce(&mut Compositor, &mut Context)>;
// Cursive-inspired // Cursive-inspired
pub enum EventResult { pub enum EventResult {
Ignored, Ignored(Option<Callback>),
Consumed(Option<Callback>), Consumed(Option<Callback>),
} }
@ -36,7 +36,7 @@ pub struct Context<'a> {
pub trait Component: Any + AnyComponent { pub trait Component: Any + AnyComponent {
/// Process input events, return true if handled. /// Process input events, return true if handled.
fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult { fn handle_event(&mut self, _event: Event, _ctx: &mut Context) -> EventResult {
EventResult::Ignored EventResult::Ignored(None)
} }
// , args: () // , args: ()
@ -146,19 +146,34 @@ impl Compositor {
keys.push(key.into()); keys.push(key.into());
} }
let mut callbacks = Vec::new();
let mut consumed = false;
// propagate events through the layers until we either find a layer that consumes it or we // propagate events through the layers until we either find a layer that consumes it or we
// run out of layers (event bubbling) // run out of layers (event bubbling)
for layer in self.layers.iter_mut().rev() { for layer in self.layers.iter_mut().rev() {
match layer.handle_event(event, cx) { match layer.handle_event(event, cx) {
EventResult::Consumed(Some(callback)) => { EventResult::Consumed(Some(callback)) => {
callback(self, cx); callbacks.push(callback);
return true; consumed = true;
break;
}
EventResult::Consumed(None) => {
consumed = true;
break;
}
EventResult::Ignored(Some(callback)) => {
callbacks.push(callback);
} }
EventResult::Consumed(None) => return true, EventResult::Ignored(None) => {}
EventResult::Ignored => false,
}; };
} }
false
for callback in callbacks {
callback(self, cx)
}
consumed
} }
pub fn render(&mut self, cx: &mut Context) { pub fn render(&mut self, cx: &mut Context) {

@ -276,7 +276,7 @@ impl Component for Completion {
code: KeyCode::Esc, .. code: KeyCode::Esc, ..
}) = event }) = event
{ {
return EventResult::Ignored; return EventResult::Ignored(None);
} }
self.popup.handle_event(event, cx) self.popup.handle_event(event, cx)
} }

@ -872,9 +872,7 @@ impl EditorView {
let path = match doc.path() { let path = match doc.path() {
Some(path) => path.clone(), Some(path) => path.clone(),
None => { None => return EventResult::Ignored(None),
return EventResult::Ignored;
}
}; };
let line = coords.row + view.offset.row; let line = coords.row + view.offset.row;
@ -884,7 +882,7 @@ impl EditorView {
} }
} }
EventResult::Ignored EventResult::Ignored(None)
} }
MouseEvent { MouseEvent {
@ -897,7 +895,7 @@ impl EditorView {
let pos = match view.pos_at_screen_coords(doc, row, column) { let pos = match view.pos_at_screen_coords(doc, row, column) {
Some(pos) => pos, Some(pos) => pos,
None => return EventResult::Ignored, None => return EventResult::Ignored(None),
}; };
let mut selection = doc.selection(view.id).clone(); let mut selection = doc.selection(view.id).clone();
@ -928,7 +926,7 @@ impl EditorView {
match result { match result {
Some(view_id) => cxt.editor.tree.focus = view_id, Some(view_id) => cxt.editor.tree.focus = view_id,
None => return EventResult::Ignored, None => return EventResult::Ignored(None),
} }
let offset = cxt.editor.config.scroll_lines.abs() as usize; let offset = cxt.editor.config.scroll_lines.abs() as usize;
@ -944,14 +942,14 @@ impl EditorView {
.. ..
} => { } => {
if !cxt.editor.config.middle_click_paste { if !cxt.editor.config.middle_click_paste {
return EventResult::Ignored; return EventResult::Ignored(None);
} }
let (view, doc) = current!(cxt.editor); let (view, doc) = current!(cxt.editor);
let range = doc.selection(view.id).primary(); let range = doc.selection(view.id).primary();
if range.to() - range.from() <= 1 { if range.to() - range.from() <= 1 {
return EventResult::Ignored; return EventResult::Ignored(None);
} }
commands::MappableCommand::yank_main_selection_to_primary_clipboard.execute(cxt); commands::MappableCommand::yank_main_selection_to_primary_clipboard.execute(cxt);
@ -988,7 +986,7 @@ impl EditorView {
return EventResult::Consumed(None); return EventResult::Consumed(None);
} }
} }
EventResult::Ignored EventResult::Ignored(None)
} }
MouseEvent { MouseEvent {
@ -1000,7 +998,7 @@ impl EditorView {
} => { } => {
let editor = &mut cxt.editor; let editor = &mut cxt.editor;
if !editor.config.middle_click_paste { if !editor.config.middle_click_paste {
return EventResult::Ignored; return EventResult::Ignored(None);
} }
if modifiers == crossterm::event::KeyModifiers::ALT { if modifiers == crossterm::event::KeyModifiers::ALT {
@ -1023,10 +1021,10 @@ impl EditorView {
return EventResult::Consumed(None); return EventResult::Consumed(None);
} }
EventResult::Ignored EventResult::Ignored(None)
} }
_ => EventResult::Ignored, _ => EventResult::Ignored(None),
} }
} }
} }
@ -1117,7 +1115,7 @@ impl Component for EditorView {
// if the command consumed the last view, skip the render. // if the command consumed the last view, skip the render.
// on the next loop cycle the Application will then terminate. // on the next loop cycle the Application will then terminate.
if cx.editor.should_close() { if cx.editor.should_close() {
return EventResult::Ignored; return EventResult::Ignored(None);
} }
let (view, doc) = current!(cx.editor); let (view, doc) = current!(cx.editor);

@ -202,7 +202,7 @@ impl<T: Item + 'static> Component for Menu<T> {
fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult { fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
let event = match event { let event = match event {
Event::Key(event) => event, Event::Key(event) => event,
_ => return EventResult::Ignored, _ => return EventResult::Ignored(None),
}; };
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| { let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {
@ -252,7 +252,7 @@ impl<T: Item + 'static> Component for Menu<T> {
// for some events, we want to process them but send ignore, specifically all input except // for some events, we want to process them but send ignore, specifically all input except
// tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll. // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll.
// EventResult::Consumed(None) // EventResult::Consumed(None)
EventResult::Ignored EventResult::Ignored(None)
} }
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> { fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {

@ -419,7 +419,7 @@ impl<T: 'static> Component for Picker<T> {
let key_event = match event { let key_event = match event {
Event::Key(event) => event, Event::Key(event) => event,
Event::Resize(..) => return EventResult::Consumed(None), Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored, _ => return EventResult::Ignored(None),
}; };
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| { let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {

@ -1,5 +1,5 @@
use crate::{ use crate::{
compositor::{Component, Compositor, Context, EventResult}, compositor::{Callback, Component, Context, EventResult},
ctrl, key, ctrl, key,
}; };
use crossterm::event::Event; use crossterm::event::Event;
@ -18,6 +18,7 @@ pub struct Popup<T: Component> {
size: (u16, u16), size: (u16, u16),
child_size: (u16, u16), child_size: (u16, u16),
scroll: usize, scroll: usize,
auto_close: bool,
id: &'static str, id: &'static str,
} }
@ -33,6 +34,7 @@ impl<T: Component> Popup<T> {
size: (0, 0), size: (0, 0),
child_size: (0, 0), child_size: (0, 0),
scroll: 0, scroll: 0,
auto_close: false,
id, id,
} }
} }
@ -46,6 +48,11 @@ impl<T: Component> Popup<T> {
self self
} }
pub fn auto_close(mut self, auto_close: bool) -> Self {
self.auto_close = auto_close;
self
}
pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) { pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) {
let position = self let position = self
.position .position
@ -105,19 +112,19 @@ impl<T: Component> Component for Popup<T> {
Event::Key(event) => event, Event::Key(event) => event,
Event::Resize(_, _) => { Event::Resize(_, _) => {
// TODO: calculate inner area, call component's handle_event with that area // TODO: calculate inner area, call component's handle_event with that area
return EventResult::Ignored; return EventResult::Ignored(None);
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored(None),
}; };
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| { let close_fn: Callback = Box::new(|compositor, _| {
// remove the layer // remove the layer
compositor.pop(); compositor.pop();
}))); });
match key.into() { match key.into() {
// esc or ctrl-c aborts the completion and closes the menu // esc or ctrl-c aborts the completion and closes the menu
key!(Esc) | ctrl!('c') => close_fn, key!(Esc) | ctrl!('c') => EventResult::Consumed(Some(close_fn)),
ctrl!('d') => { ctrl!('d') => {
self.scroll(self.size.1 as usize / 2, true); self.scroll(self.size.1 as usize / 2, true);
EventResult::Consumed(None) EventResult::Consumed(None)
@ -126,7 +133,17 @@ impl<T: Component> Component for Popup<T> {
self.scroll(self.size.1 as usize / 2, false); self.scroll(self.size.1 as usize / 2, false);
EventResult::Consumed(None) EventResult::Consumed(None)
} }
_ => self.contents.handle_event(event, cx), _ => {
let contents_event_result = self.contents.handle_event(event, cx);
if self.auto_close {
if let EventResult::Ignored(None) = contents_event_result {
return EventResult::Ignored(Some(close_fn));
}
}
contents_event_result
}
} }
// for some events, we want to process them but send ignore, specifically all input except // for some events, we want to process them but send ignore, specifically all input except
// tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll. // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll.

@ -438,7 +438,7 @@ impl Component for Prompt {
let event = match event { let event = match event {
Event::Key(event) => event, Event::Key(event) => event,
Event::Resize(..) => return EventResult::Consumed(None), Event::Resize(..) => return EventResult::Consumed(None),
_ => return EventResult::Ignored, _ => return EventResult::Ignored(None),
}; };
let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| { let close_fn = EventResult::Consumed(Some(Box::new(|compositor: &mut Compositor, _| {

Loading…
Cancel
Save