Remove internal scrolling from SelectView

This commit is contained in:
Alexandre Bury 2018-07-24 19:38:24 -07:00
parent 49e1d1d15e
commit 840fd627b7
2 changed files with 36 additions and 115 deletions

View File

@ -38,7 +38,7 @@ fn main() {
// Let's add a BoxView to keep the list at a reasonable size // Let's add a BoxView to keep the list at a reasonable size
// (it can scroll anyway). // (it can scroll anyway).
siv.add_layer( siv.add_layer(
Dialog::around(select.fixed_size((20, 10))) Dialog::around(select.scrollable().fixed_size((20, 10)))
.title("Where are you from?"), .title("Where are you from?"),
); );

View File

@ -10,7 +10,7 @@ use std::rc::Rc;
use theme::ColorStyle; use theme::ColorStyle;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use vec::Vec2; use vec::Vec2;
use view::{Position, ScrollBase, View}; use view::{Position, View};
use views::MenuPopup; use views::MenuPopup;
use Cursive; use Cursive;
use Printer; use Printer;
@ -51,7 +51,6 @@ pub struct SelectView<T = String> {
enabled: bool, enabled: bool,
// the focus needs to be manipulable from callbacks // the focus needs to be manipulable from callbacks
focus: Rc<Cell<usize>>, focus: Rc<Cell<usize>>,
scrollbase: ScrollBase,
// This is a custom callback to include a &T. // This is a custom callback to include a &T.
// It will be called whenever "Enter" is pressed. // It will be called whenever "Enter" is pressed.
on_submit: Option<Rc<Fn(&mut Cursive, &T)>>, on_submit: Option<Rc<Fn(&mut Cursive, &T)>>,
@ -79,7 +78,6 @@ impl<T: 'static> SelectView<T> {
items: Vec::new(), items: Vec::new(),
enabled: true, enabled: true,
focus: Rc::new(Cell::new(0)), focus: Rc::new(Cell::new(0)),
scrollbase: ScrollBase::new(),
on_select: None, on_select: None,
on_submit: None, on_submit: None,
align: Align::top_left(), align: Align::top_left(),
@ -353,7 +351,6 @@ impl<T: 'static> SelectView<T> {
min(i, self.len() - 1) min(i, self.len() - 1)
}; };
self.focus.set(i); self.focus.set(i);
self.scrollbase.scroll_to(i);
self.make_select_cb().unwrap_or_else(Callback::dummy) self.make_select_cb().unwrap_or_else(Callback::dummy)
} }
@ -386,9 +383,6 @@ impl<T: 'static> SelectView<T> {
/// ``` /// ```
pub fn select_up(&mut self, n: usize) -> Callback { pub fn select_up(&mut self, n: usize) -> Callback {
self.focus_up(n); self.focus_up(n);
let focus = self.focus();
self.scrollbase.scroll_to(focus);
self.make_select_cb().unwrap_or_else(Callback::dummy) self.make_select_cb().unwrap_or_else(Callback::dummy)
} }
@ -399,19 +393,14 @@ impl<T: 'static> SelectView<T> {
/// You should run this callback with a `&mut Cursive`. /// You should run this callback with a `&mut Cursive`.
pub fn select_down(&mut self, n: usize) -> Callback { pub fn select_down(&mut self, n: usize) -> Callback {
self.focus_down(n); self.focus_down(n);
let focus = self.focus();
self.scrollbase.scroll_to(focus);
self.make_select_cb().unwrap_or_else(Callback::dummy) self.make_select_cb().unwrap_or_else(Callback::dummy)
} }
// Low-level focus change. Does not fix scrollbase.
fn focus_up(&mut self, n: usize) { fn focus_up(&mut self, n: usize) {
let focus = self.focus().saturating_sub(n); let focus = self.focus().saturating_sub(n);
self.focus.set(focus); self.focus.set(focus);
} }
// Low-level focus change. Does not fix scrollbase.
fn focus_down(&mut self, n: usize) { fn focus_down(&mut self, n: usize) {
let focus = min(self.focus() + n, self.items.len().saturating_sub(1)); let focus = min(self.focus() + n, self.items.len().saturating_sub(1));
self.focus.set(focus); self.focus.set(focus);
@ -427,7 +416,6 @@ impl<T: 'static> SelectView<T> {
} }
fn on_event_regular(&mut self, event: Event) -> EventResult { fn on_event_regular(&mut self, event: Event) -> EventResult {
let mut fix_scroll = true;
match event { match event {
Event::Key(Key::Up) if self.focus() > 0 => self.focus_up(1), Event::Key(Key::Up) if self.focus() > 0 => self.focus_up(1),
Event::Key(Key::Down) if self.focus() + 1 < self.items.len() => { Event::Key(Key::Down) if self.focus() + 1 < self.items.len() => {
@ -440,93 +428,35 @@ impl<T: 'static> SelectView<T> {
self.focus.set(self.items.len().saturating_sub(1)) self.focus.set(self.items.len().saturating_sub(1))
} }
Event::Mouse { Event::Mouse {
event: MouseEvent::WheelDown, event: MouseEvent::Press(_),
..
}
if self.scrollbase.can_scroll_down() =>
{
fix_scroll = false;
self.scrollbase.scroll_down(5);
}
Event::Mouse {
event: MouseEvent::WheelUp,
..
}
if self.scrollbase.can_scroll_up() =>
{
fix_scroll = false;
self.scrollbase.scroll_up(5);
}
Event::Mouse {
event: MouseEvent::Press(MouseButton::Left),
position, position,
offset, offset,
} }
if position if position
.checked_sub(offset) .checked_sub(offset)
.map(|position| { .map(|position| {
self.scrollbase.start_drag(position, self.last_size.x) position < self.last_size && position.y < self.len()
}) })
.unwrap_or(false) => .unwrap_or(false) =>
{ {
fix_scroll = false; self.focus.set(position.y - offset.y)
} }
Event::Mouse {
event: MouseEvent::Hold(MouseButton::Left),
position,
offset,
} => {
// If the mouse is dragged, we always consume the event.
fix_scroll = false;
let position = position.saturating_sub(offset);
self.scrollbase.drag(position);
}
Event::Mouse {
event: MouseEvent::Press(_),
position,
offset,
} => if let Some(position) = position.checked_sub(offset) {
let scrollbar_size = if self.scrollbase.scrollable() {
(2, 0)
} else {
(0, 0)
};
let clickable_size =
self.last_size.saturating_sub(scrollbar_size);
if position < clickable_size {
fix_scroll = false;
let focus = position.y + self.scrollbase.start_line;
if focus < self.len() {
// Only select actual items
self.focus.set(focus);
}
}
},
Event::Mouse { Event::Mouse {
event: MouseEvent::Release(MouseButton::Left), event: MouseEvent::Release(MouseButton::Left),
position, position,
offset, offset,
} => { }
fix_scroll = false; if self.on_submit.is_some()
self.scrollbase.release_grab(); && position
if self.on_submit.is_some() { .checked_sub(offset)
if let Some(position) = position.checked_sub(offset) { .map(|position| {
let scrollbar_size = if self.scrollbase.scrollable() { position < self.last_size
(2, 0) && position.y == self.focus()
} else { })
(0, 0) .unwrap_or(false) =>
};
let clickable_size =
self.last_size.saturating_sub(scrollbar_size);
if position < clickable_size
&& (position.y + self.scrollbase.start_line)
== self.focus()
{ {
return self.submit(); return self.submit();
} }
}
}
}
Event::Key(Key::Enter) if self.on_submit.is_some() => { Event::Key(Key::Enter) if self.on_submit.is_some() => {
return self.submit(); return self.submit();
} }
@ -551,10 +481,6 @@ impl<T: 'static> SelectView<T> {
} }
_ => return EventResult::Ignored, _ => return EventResult::Ignored,
} }
if fix_scroll {
let focus = self.focus();
self.scrollbase.scroll_to(focus);
}
EventResult::Consumed(self.make_select_cb()) EventResult::Consumed(self.make_select_cb())
} }
@ -697,6 +623,8 @@ impl<T: 'static> View for SelectView<T> {
self.last_offset.set(printer.offset); self.last_offset.set(printer.offset);
if self.popup { if self.popup {
// Popup-select only draw the active element.
// We'll draw the full list in a popup if needed.
let style = if !self.enabled { let style = if !self.enabled {
ColorStyle::secondary() ColorStyle::secondary()
} else if !printer.focused { } else if !printer.focused {
@ -724,26 +652,30 @@ impl<T: 'static> View for SelectView<T> {
printer.print((offset, 0), label); printer.print((offset, 0), label);
}); });
} else { } else {
// Non-popup mode: we always print the entire list.
let h = self.items.len(); let h = self.items.len();
let offset = self.align.v.get_offset(h, printer.size.y); let offset = self.align.v.get_offset(h, printer.size.y);
let printer = &printer.offset((0, offset)); let printer = &printer.offset((0, offset));
self.scrollbase.draw(printer, |printer, i| { for i in 0..self.len() {
printer.with_selection(i == self.focus(), |printer| { printer.offset((0, i)).with_selection(
i == self.focus(),
|printer| {
if i != self.focus() && !self.enabled { if i != self.focus() && !self.enabled {
printer printer.with_color(
.with_color(ColorStyle::secondary(), |printer| { ColorStyle::secondary(),
self.draw_item(printer, i) |printer| self.draw_item(printer, i),
}); );
} else { } else {
self.draw_item(printer, i); self.draw_item(printer, i);
} }
}); },
}); );
}
} }
} }
fn required_size(&mut self, req: Vec2) -> Vec2 { fn required_size(&mut self, _: Vec2) -> Vec2 {
// Items here are not compressible. // Items here are not compressible.
// So no matter what the horizontal requirements are, // So no matter what the horizontal requirements are,
// we'll still return our longest item. // we'll still return our longest item.
@ -758,14 +690,7 @@ impl<T: 'static> View for SelectView<T> {
} else { } else {
let h = self.items.len(); let h = self.items.len();
let scrolling = req.y < h; Vec2::new(w, h)
// Add 2 spaces for the scrollbar if we need
let w = if scrolling { w + 2 } else { w };
// Don't request more than we're offered - we can scroll,
// after all
Vec2::new(w, min(h, req.y))
} }
} }
@ -783,10 +708,6 @@ impl<T: 'static> View for SelectView<T> {
fn layout(&mut self, size: Vec2) { fn layout(&mut self, size: Vec2) {
self.last_size = size; self.last_size = size;
if !self.popup {
self.scrollbase.set_heights(size.y, self.items.len());
}
} }
fn important_area(&self, size: Vec2) -> Rect { fn important_area(&self, size: Vec2) -> Rect {