mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Fix mouse support for ListView
This commit is contained in:
parent
850e0b2cd1
commit
294a4102b4
@ -207,6 +207,11 @@ impl ScrollBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if we are in the process of dragging the scroll thumb.
|
||||||
|
pub fn is_dragging(&self) -> bool {
|
||||||
|
self.thumb_grab.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Stops grabbing the scrollbar.
|
/// Stops grabbing the scrollbar.
|
||||||
pub fn release_grab(&mut self) {
|
pub fn release_grab(&mut self) {
|
||||||
self.thumb_grab = None;
|
self.thumb_grab = None;
|
||||||
|
@ -2,7 +2,7 @@ use Cursive;
|
|||||||
use Printer;
|
use Printer;
|
||||||
use With;
|
use With;
|
||||||
use direction;
|
use direction;
|
||||||
use event::{Callback, Event, EventResult, Key};
|
use event::{Callback, Event, EventResult, Key, MouseEvent, MouseButton};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
@ -44,6 +44,7 @@ pub struct ListView {
|
|||||||
focus: usize,
|
focus: usize,
|
||||||
// This callback is called when the selection is changed.
|
// This callback is called when the selection is changed.
|
||||||
on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
|
on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
|
||||||
|
last_size: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
new_default!(ListView);
|
new_default!(ListView);
|
||||||
@ -56,6 +57,7 @@ impl ListView {
|
|||||||
scrollbase: ScrollBase::new(),
|
scrollbase: ScrollBase::new(),
|
||||||
focus: 0,
|
focus: 0,
|
||||||
on_select: None,
|
on_select: None,
|
||||||
|
last_size: Vec2::zero(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +96,10 @@ impl ListView {
|
|||||||
/// Adds a view to the end of the list.
|
/// Adds a view to the end of the list.
|
||||||
pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
|
pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
|
||||||
view.take_focus(direction::Direction::none());
|
view.take_focus(direction::Direction::none());
|
||||||
self.children
|
self.children.push(ListChild::Row(
|
||||||
.push(ListChild::Row(label.to_string(), Box::new(view)));
|
label.to_string(),
|
||||||
|
Box::new(view),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all children from this view.
|
/// Removes all children from this view.
|
||||||
@ -148,9 +152,8 @@ impl ListView {
|
|||||||
self.focus
|
self.focus
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter_mut<'a>(
|
fn iter_mut<'a>(&'a mut self, from_focus: bool, source: direction::Relative)
|
||||||
&'a mut self, from_focus: bool, source: direction::Relative
|
-> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
|
||||||
) -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {
|
|
||||||
match source {
|
match source {
|
||||||
direction::Relative::Front => {
|
direction::Relative::Front => {
|
||||||
let start = if from_focus { self.focus } else { 0 };
|
let start = if from_focus { self.focus } else { 0 };
|
||||||
@ -168,9 +171,8 @@ impl ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_focus(
|
fn move_focus(&mut self, n: usize, source: direction::Direction)
|
||||||
&mut self, n: usize, source: direction::Direction
|
-> EventResult {
|
||||||
) -> EventResult {
|
|
||||||
let i = if let Some(i) = source
|
let i = if let Some(i) = source
|
||||||
.relative(direction::Orientation::Vertical)
|
.relative(direction::Orientation::Vertical)
|
||||||
.and_then(|rel| {
|
.and_then(|rel| {
|
||||||
@ -181,7 +183,8 @@ impl ListView {
|
|||||||
.filter_map(|p| try_focus(p, source))
|
.filter_map(|p| try_focus(p, source))
|
||||||
.take(n)
|
.take(n)
|
||||||
.last()
|
.last()
|
||||||
}) {
|
})
|
||||||
|
{
|
||||||
i
|
i
|
||||||
} else {
|
} else {
|
||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
@ -238,16 +241,17 @@ impl ListView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_focus(
|
fn try_focus((i, child): (usize, &mut ListChild), source: direction::Direction)
|
||||||
(i, child): (usize, &mut ListChild), source: direction::Direction
|
-> Option<usize> {
|
||||||
) -> Option<usize> {
|
|
||||||
match *child {
|
match *child {
|
||||||
ListChild::Delimiter => None,
|
ListChild::Delimiter => None,
|
||||||
ListChild::Row(_, ref mut view) => if view.take_focus(source) {
|
ListChild::Row(_, ref mut view) => {
|
||||||
Some(i)
|
if view.take_focus(source) {
|
||||||
} else {
|
Some(i)
|
||||||
None
|
} else {
|
||||||
},
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,14 +264,16 @@ impl View for ListView {
|
|||||||
let offset = self.labels_width() + 1;
|
let offset = self.labels_width() + 1;
|
||||||
|
|
||||||
debug!("Offset: {}", offset);
|
debug!("Offset: {}", offset);
|
||||||
self.scrollbase
|
self.scrollbase.draw(
|
||||||
.draw(printer, |printer, i| match self.children[i] {
|
printer,
|
||||||
|
|printer, i| match self.children[i] {
|
||||||
ListChild::Row(ref label, ref view) => {
|
ListChild::Row(ref label, ref view) => {
|
||||||
printer.print((0, 0), label);
|
printer.print((0, 0), label);
|
||||||
view.draw(&printer.offset((offset, 0), i == self.focus));
|
view.draw(&printer.offset((offset, 0), i == self.focus));
|
||||||
}
|
}
|
||||||
ListChild::Delimiter => (),
|
ListChild::Delimiter => (),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
fn required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
@ -294,6 +300,7 @@ impl View for ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
|
self.last_size = size;
|
||||||
self.scrollbase.set_heights(size.y, self.children.len());
|
self.scrollbase.set_heights(size.y, self.children.len());
|
||||||
|
|
||||||
// We'll show 2 columns: the labels, and the views.
|
// We'll show 2 columns: the labels, and the views.
|
||||||
@ -307,8 +314,9 @@ impl View for ListView {
|
|||||||
let spacing = 1;
|
let spacing = 1;
|
||||||
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };
|
||||||
|
|
||||||
let available = size.x
|
let available = size.x.saturating_sub(
|
||||||
.saturating_sub(label_width + spacing + scrollbar_width);
|
label_width + spacing + scrollbar_width,
|
||||||
|
);
|
||||||
|
|
||||||
debug!("Available: {}", available);
|
debug!("Available: {}", available);
|
||||||
|
|
||||||
@ -322,7 +330,43 @@ impl View for ListView {
|
|||||||
return EventResult::Ignored;
|
return EventResult::Ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First: some events can move the focus around.
|
// First: some events can directly affect the ListView
|
||||||
|
match event {
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Press(MouseButton::Left),
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
}
|
||||||
|
if position
|
||||||
|
.checked_sub(offset)
|
||||||
|
.map(|position| {
|
||||||
|
self.scrollbase.start_drag(position, self.last_size.x)
|
||||||
|
})
|
||||||
|
.unwrap_or(false) => {
|
||||||
|
return EventResult::Consumed(None);
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Hold(MouseButton::Left),
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
} if self.scrollbase.is_dragging() => {
|
||||||
|
position.checked_sub(offset).map(|position| {
|
||||||
|
self.scrollbase.drag(position)
|
||||||
|
});
|
||||||
|
return EventResult::Consumed(None);
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Release(MouseButton::Left), ..
|
||||||
|
} if self.scrollbase.is_dragging() => {
|
||||||
|
self.scrollbase.release_grab();
|
||||||
|
return EventResult::Consumed(None);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Then: some events can move the focus around.
|
||||||
self.check_focus_grab(&event);
|
self.check_focus_grab(&event);
|
||||||
|
|
||||||
// Send the event to the focused child.
|
// Send the event to the focused child.
|
||||||
@ -350,13 +394,15 @@ impl View for ListView {
|
|||||||
Event::Key(Key::PageDown) => {
|
Event::Key(Key::PageDown) => {
|
||||||
self.move_focus(10, direction::Direction::up())
|
self.move_focus(10, direction::Direction::up())
|
||||||
}
|
}
|
||||||
Event::Key(Key::Home) | Event::Ctrl(Key::Home) => {
|
Event::Key(Key::Home) |
|
||||||
|
Event::Ctrl(Key::Home) => {
|
||||||
self.move_focus(
|
self.move_focus(
|
||||||
usize::max_value(),
|
usize::max_value(),
|
||||||
direction::Direction::back(),
|
direction::Direction::back(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Event::Key(Key::End) | Event::Ctrl(Key::End) => {
|
Event::Key(Key::End) |
|
||||||
|
Event::Ctrl(Key::End) => {
|
||||||
self.move_focus(
|
self.move_focus(
|
||||||
usize::max_value(),
|
usize::max_value(),
|
||||||
direction::Direction::front(),
|
direction::Direction::front(),
|
||||||
@ -368,32 +414,40 @@ impl View for ListView {
|
|||||||
Event::Shift(Key::Tab) => {
|
Event::Shift(Key::Tab) => {
|
||||||
self.move_focus(1, direction::Direction::back())
|
self.move_focus(1, direction::Direction::back())
|
||||||
}
|
}
|
||||||
|
Event::Mouse { event: MouseEvent::WheelDown, .. }
|
||||||
|
if self.scrollbase.can_scroll_down() => {
|
||||||
|
self.scrollbase.scroll_down(5);
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
Event::Mouse { event: MouseEvent::WheelUp, .. }
|
||||||
|
if self.scrollbase.can_scroll_up() => {
|
||||||
|
self.scrollbase.scroll_up(5);
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
fn take_focus(&mut self, source: direction::Direction) -> bool {
|
||||||
let rel = source.relative(direction::Orientation::Vertical);
|
let rel = source.relative(direction::Orientation::Vertical);
|
||||||
let i = if let Some(i) = self.iter_mut(
|
let i =
|
||||||
rel.is_none(),
|
if let Some(i) = self.iter_mut(
|
||||||
rel.unwrap_or(direction::Relative::Front),
|
rel.is_none(),
|
||||||
).filter_map(|p| try_focus(p, source))
|
rel.unwrap_or(direction::Relative::Front),
|
||||||
.next()
|
).filter_map(|p| try_focus(p, source))
|
||||||
{
|
.next()
|
||||||
i
|
{
|
||||||
} else {
|
i
|
||||||
// No one wants to be in focus
|
} else {
|
||||||
return false;
|
// No one wants to be in focus
|
||||||
};
|
return false;
|
||||||
|
};
|
||||||
self.focus = i;
|
self.focus = i;
|
||||||
self.scrollbase.scroll_to(self.focus);
|
self.scrollbase.scroll_to(self.focus);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call_on_any<'a>(
|
fn call_on_any<'a>(&mut self, selector: &Selector, mut callback: Box<FnMut(&mut Any) + 'a>) {
|
||||||
&mut self, selector: &Selector,
|
|
||||||
mut callback: Box<FnMut(&mut Any) + 'a>,
|
|
||||||
) {
|
|
||||||
for view in self.children.iter_mut().filter_map(ListChild::view) {
|
for view in self.children.iter_mut().filter_map(ListChild::view) {
|
||||||
view.call_on_any(selector, Box::new(|any| callback(any)));
|
view.call_on_any(selector, Box::new(|any| callback(any)));
|
||||||
}
|
}
|
||||||
|
@ -339,19 +339,13 @@ impl<T: 'static> SelectView<T> {
|
|||||||
Event::Key(Key::End) => {
|
Event::Key(Key::End) => {
|
||||||
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::WheelDown,
|
if self.scrollbase.can_scroll_down() => {
|
||||||
..
|
|
||||||
} if self.scrollbase.can_scroll_down() =>
|
|
||||||
{
|
|
||||||
fix_scroll = false;
|
fix_scroll = false;
|
||||||
self.scrollbase.scroll_down(5);
|
self.scrollbase.scroll_down(5);
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse { event: MouseEvent::WheelUp, .. }
|
||||||
event: MouseEvent::WheelUp,
|
if self.scrollbase.can_scroll_up() => {
|
||||||
..
|
|
||||||
} if self.scrollbase.can_scroll_up() =>
|
|
||||||
{
|
|
||||||
fix_scroll = false;
|
fix_scroll = false;
|
||||||
self.scrollbase.scroll_up(5);
|
self.scrollbase.scroll_up(5);
|
||||||
}
|
}
|
||||||
@ -359,13 +353,13 @@ impl<T: 'static> SelectView<T> {
|
|||||||
event: MouseEvent::Press(MouseButton::Left),
|
event: MouseEvent::Press(MouseButton::Left),
|
||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
} if position
|
}
|
||||||
.checked_sub(offset)
|
if position
|
||||||
.map(|position| {
|
.checked_sub(offset)
|
||||||
|
.map(|position| {
|
||||||
self.scrollbase.start_drag(position, self.last_size.x)
|
self.scrollbase.start_drag(position, self.last_size.x)
|
||||||
})
|
})
|
||||||
.unwrap_or(false) =>
|
.unwrap_or(false) => {
|
||||||
{
|
|
||||||
fix_scroll = false;
|
fix_scroll = false;
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
@ -375,26 +369,31 @@ impl<T: 'static> SelectView<T> {
|
|||||||
} => {
|
} => {
|
||||||
// If the mouse is dragged, we always consume the event.
|
// If the mouse is dragged, we always consume the event.
|
||||||
fix_scroll = false;
|
fix_scroll = false;
|
||||||
position
|
position.checked_sub(offset).map(|position| {
|
||||||
.checked_sub(offset)
|
self.scrollbase.drag(position)
|
||||||
.map(|position| self.scrollbase.drag(position));
|
});
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::Press(_),
|
event: MouseEvent::Press(_),
|
||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
} => if let Some(position) = position.checked_sub(offset) {
|
} => {
|
||||||
let scrollbar_size = if self.scrollbase.scrollable() {
|
if let Some(position) = position.checked_sub(offset) {
|
||||||
(2, 0)
|
let scrollbar_size = if self.scrollbase.scrollable() {
|
||||||
} else {
|
(2, 0)
|
||||||
(0, 0)
|
} else {
|
||||||
};
|
(0, 0)
|
||||||
let clickable_size = self.last_size.saturating_sub(scrollbar_size);
|
};
|
||||||
if position < clickable_size {
|
let clickable_size =
|
||||||
fix_scroll = false;
|
self.last_size.saturating_sub(scrollbar_size);
|
||||||
self.focus.set(position.y + self.scrollbase.start_line);
|
if position < clickable_size {
|
||||||
|
fix_scroll = false;
|
||||||
|
self.focus.set(
|
||||||
|
position.y + self.scrollbase.start_line,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::Release(MouseButton::Left),
|
event: MouseEvent::Release(MouseButton::Left),
|
||||||
position,
|
position,
|
||||||
@ -409,10 +408,11 @@ impl<T: 'static> SelectView<T> {
|
|||||||
} else {
|
} else {
|
||||||
(0, 0)
|
(0, 0)
|
||||||
};
|
};
|
||||||
let clickable_size = self.last_size.saturating_sub(scrollbar_size);
|
let clickable_size =
|
||||||
if position < clickable_size
|
self.last_size.saturating_sub(scrollbar_size);
|
||||||
&& (position.y + self.scrollbase.start_line)
|
if position < clickable_size &&
|
||||||
== self.focus()
|
(position.y + self.scrollbase.start_line) ==
|
||||||
|
self.focus()
|
||||||
{
|
{
|
||||||
return self.submit();
|
return self.submit();
|
||||||
}
|
}
|
||||||
@ -429,9 +429,12 @@ impl<T: 'static> SelectView<T> {
|
|||||||
// the list when we reach the end.
|
// the list when we reach the end.
|
||||||
// This is achieved by chaining twice the iterator
|
// This is achieved by chaining twice the iterator
|
||||||
let iter = self.items.iter().chain(self.items.iter());
|
let iter = self.items.iter().chain(self.items.iter());
|
||||||
if let Some((i, _)) = iter.enumerate()
|
if let Some((i, _)) =
|
||||||
.skip(self.focus() + 1)
|
iter.enumerate().skip(self.focus() + 1).find(
|
||||||
.find(|&(_, item)| item.label.starts_with(c))
|
|&(_, item)| {
|
||||||
|
item.label.starts_with(c)
|
||||||
|
},
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Apply modulo in case we have a hit
|
// Apply modulo in case we have a hit
|
||||||
// from the chained iterator
|
// from the chained iterator
|
||||||
@ -513,8 +516,7 @@ impl<T: 'static> SelectView<T> {
|
|||||||
event: MouseEvent::Release(MouseButton::Left),
|
event: MouseEvent::Release(MouseButton::Left),
|
||||||
position,
|
position,
|
||||||
offset,
|
offset,
|
||||||
} if position.fits_in_rect(offset, self.last_size) =>
|
} if position.fits_in_rect(offset, self.last_size) => {
|
||||||
{
|
|
||||||
self.open_popup()
|
self.open_popup()
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored,
|
_ => EventResult::Ignored,
|
||||||
@ -603,15 +605,16 @@ impl<T: 'static> View for SelectView<T> {
|
|||||||
&printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
&printer.sub_printer(Vec2::new(0, offset), printer.size, true);
|
||||||
|
|
||||||
self.scrollbase.draw(printer, |printer, i| {
|
self.scrollbase.draw(printer, |printer, i| {
|
||||||
printer.with_selection(i == self.focus(), |printer| {
|
printer.with_selection(
|
||||||
if i != self.focus() && !self.enabled {
|
i == self.focus(),
|
||||||
|
|printer| if i != self.focus() && !self.enabled {
|
||||||
printer.with_color(ColorStyle::Secondary, |printer| {
|
printer.with_color(ColorStyle::Secondary, |printer| {
|
||||||
self.draw_item(printer, i)
|
self.draw_item(printer, i)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.draw_item(printer, i);
|
self.draw_item(printer, i);
|
||||||
}
|
},
|
||||||
});
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user