mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 09:25:01 +00:00
Enable multi-rows views in ListView
This commit is contained in:
parent
63b438188f
commit
56008db796
@ -85,13 +85,13 @@ impl Orientation {
|
|||||||
///
|
///
|
||||||
/// * For an horizontal view, returns `(Sum(x), Max(y))`.
|
/// * For an horizontal view, returns `(Sum(x), Max(y))`.
|
||||||
/// * For a vertical view, returns `(Max(x), Sum(y))`.
|
/// * For a vertical view, returns `(Max(x), Sum(y))`.
|
||||||
pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(self, iter: T) -> Vec2 {
|
pub fn stack<'a, T: Iterator<Item = Vec2>>(self, iter: T) -> Vec2 {
|
||||||
match self {
|
match self {
|
||||||
Orientation::Horizontal => {
|
Orientation::Horizontal => {
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
|
iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(&b))
|
||||||
}
|
}
|
||||||
Orientation::Vertical => {
|
Orientation::Vertical => {
|
||||||
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
|
iter.fold(Vec2::zero(), |a, b| a.stack_vertical(&b))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,7 +503,7 @@ impl View for LinearLayout {
|
|||||||
.map(|c| c.required_size(req))
|
.map(|c| c.required_size(req))
|
||||||
.collect();
|
.collect();
|
||||||
debug!("Ideal sizes: {:?}", ideal_sizes);
|
debug!("Ideal sizes: {:?}", ideal_sizes);
|
||||||
let ideal = self.orientation.stack(ideal_sizes.iter());
|
let ideal = self.orientation.stack(ideal_sizes.iter().copied());
|
||||||
debug!("Ideal result: {:?}", ideal);
|
debug!("Ideal result: {:?}", ideal);
|
||||||
|
|
||||||
// Does it fit?
|
// Does it fit?
|
||||||
@ -527,7 +527,7 @@ impl View for LinearLayout {
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|c| c.required_size(budget_req))
|
.map(|c| c.required_size(budget_req))
|
||||||
.collect();
|
.collect();
|
||||||
let desperate = self.orientation.stack(min_sizes.iter());
|
let desperate = self.orientation.stack(min_sizes.iter().copied());
|
||||||
debug!("Min sizes: {:?}", min_sizes);
|
debug!("Min sizes: {:?}", min_sizes);
|
||||||
debug!("Desperate: {:?}", desperate);
|
debug!("Desperate: {:?}", desperate);
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ impl View for LinearLayout {
|
|||||||
debug!("Final sizes2: {:?}", final_sizes);
|
debug!("Final sizes2: {:?}", final_sizes);
|
||||||
|
|
||||||
// Let's stack everything to see what it looks like.
|
// Let's stack everything to see what it looks like.
|
||||||
let compromise = self.orientation.stack(final_sizes.iter());
|
let compromise = self.orientation.stack(final_sizes.iter().copied());
|
||||||
|
|
||||||
// Phew, that was a lot of work! I'm not doing it again.
|
// Phew, that was a lot of work! I'm not doing it again.
|
||||||
self.cache = Some(SizeCache::build(compromise, req));
|
self.cache = Some(SizeCache::build(compromise, req));
|
||||||
|
@ -37,6 +37,7 @@ impl ListChild {
|
|||||||
/// Displays a list of elements.
|
/// Displays a list of elements.
|
||||||
pub struct ListView {
|
pub struct ListView {
|
||||||
children: Vec<ListChild>,
|
children: Vec<ListChild>,
|
||||||
|
children_heights: Vec<usize>,
|
||||||
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<dyn Fn(&mut Cursive, &String)>>,
|
on_select: Option<Rc<dyn Fn(&mut Cursive, &String)>>,
|
||||||
@ -50,6 +51,7 @@ impl ListView {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
ListView {
|
ListView {
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
children_heights: Vec::new(),
|
||||||
focus: 0,
|
focus: 0,
|
||||||
on_select: None,
|
on_select: None,
|
||||||
last_size: Vec2::zero(),
|
last_size: Vec2::zero(),
|
||||||
@ -96,11 +98,13 @@ impl ListView {
|
|||||||
let mut view = view.as_boxed_view();
|
let mut view = view.as_boxed_view();
|
||||||
view.take_focus(direction::Direction::none());
|
view.take_focus(direction::Direction::none());
|
||||||
self.children.push(ListChild::Row(label.to_string(), view));
|
self.children.push(ListChild::Row(label.to_string(), view));
|
||||||
|
self.children_heights.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes all children from this view.
|
/// Removes all children from this view.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.children.clear();
|
self.children.clear();
|
||||||
|
self.children_heights.clear();
|
||||||
self.focus = 0;
|
self.focus = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +122,7 @@ impl ListView {
|
|||||||
/// Adds a delimiter to the end of the list.
|
/// Adds a delimiter to the end of the list.
|
||||||
pub fn add_delimiter(&mut self) {
|
pub fn add_delimiter(&mut self) {
|
||||||
self.children.push(ListChild::Delimiter);
|
self.children.push(ListChild::Delimiter);
|
||||||
|
self.children_heights.push(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a delimiter to the end of the list.
|
/// Adds a delimiter to the end of the list.
|
||||||
@ -133,6 +138,7 @@ impl ListView {
|
|||||||
///
|
///
|
||||||
/// If `index >= self.len()`.
|
/// If `index >= self.len()`.
|
||||||
pub fn remove_child(&mut self, index: usize) -> ListChild {
|
pub fn remove_child(&mut self, index: usize) -> ListChild {
|
||||||
|
self.children_heights.remove(index);
|
||||||
self.children.remove(index)
|
self.children.remove(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +238,7 @@ impl ListView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let position = match position.checked_sub(offset) {
|
let mut position = match position.checked_sub(offset) {
|
||||||
None => return,
|
None => return,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
@ -240,14 +246,21 @@ impl ListView {
|
|||||||
// eprintln!("Rel pos: {:?}", position);
|
// eprintln!("Rel pos: {:?}", position);
|
||||||
|
|
||||||
// Now that we have a relative position, checks for buttons?
|
// Now that we have a relative position, checks for buttons?
|
||||||
let focus = position.y;
|
for (i, (child, height)) in self
|
||||||
if focus >= self.children.len() {
|
.children
|
||||||
return;
|
.iter_mut()
|
||||||
}
|
.zip(&self.children_heights)
|
||||||
|
.enumerate()
|
||||||
if let ListChild::Row(_, ref mut view) = self.children[focus] {
|
{
|
||||||
|
if position.y < *height {
|
||||||
|
if let ListChild::Row(_, ref mut view) = child {
|
||||||
if view.take_focus(direction::Direction::none()) {
|
if view.take_focus(direction::Direction::none()) {
|
||||||
self.focus = focus;
|
self.focus = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
position.y -= height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,18 +290,25 @@ impl View for ListView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let offset = self.labels_width() + 1;
|
let offset = self.labels_width() + 1;
|
||||||
|
let mut y = 0;
|
||||||
|
|
||||||
debug!("Offset: {}", offset);
|
debug!("Offset: {}", offset);
|
||||||
for (i, child) in self.children.iter().enumerate() {
|
for (i, (child, &height)) in
|
||||||
|
self.children.iter().zip(&self.children_heights).enumerate()
|
||||||
|
{
|
||||||
match child {
|
match child {
|
||||||
ListChild::Row(ref label, ref view) => {
|
ListChild::Row(ref label, ref view) => {
|
||||||
printer.print((0, i), label);
|
printer.print((0, y), label);
|
||||||
view.draw(
|
view.draw(
|
||||||
&printer.offset((offset, i)).focused(i == self.focus),
|
&printer
|
||||||
|
.offset((offset, y))
|
||||||
|
.cropped((printer.size.x, height))
|
||||||
|
.focused(i == self.focus),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ListChild::Delimiter => (),
|
ListChild::Delimiter => (), // TODO: draw delimiters?
|
||||||
}
|
}
|
||||||
|
y += height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,15 +322,14 @@ impl View for ListView {
|
|||||||
.max()
|
.max()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
let view_size = self
|
let view_size = direction::Orientation::Vertical.stack(
|
||||||
.children
|
self.children.iter_mut().map(|c| match c {
|
||||||
.iter_mut()
|
ListChild::Delimiter => Vec2::new(0, 1),
|
||||||
.filter_map(ListChild::view)
|
ListChild::Row(_, ref mut view) => view.required_size(req),
|
||||||
.map(|v| v.required_size(req).x)
|
}),
|
||||||
.max()
|
);
|
||||||
.unwrap_or(0);
|
|
||||||
|
|
||||||
Vec2::new(label_width + 1 + view_size, self.children.len())
|
view_size + (1 + label_width, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
@ -331,8 +350,16 @@ impl View for ListView {
|
|||||||
|
|
||||||
debug!("Available: {}", available);
|
debug!("Available: {}", available);
|
||||||
|
|
||||||
for child in self.children.iter_mut().filter_map(ListChild::view) {
|
self.children_heights.resize(self.children.len(), 0);
|
||||||
child.layout(Vec2::new(available, 1));
|
for (child, height) in self
|
||||||
|
.children
|
||||||
|
.iter_mut()
|
||||||
|
.filter_map(ListChild::view)
|
||||||
|
.zip(&mut self.children_heights)
|
||||||
|
{
|
||||||
|
// TODO: Find the child height?
|
||||||
|
*height = child.required_size(size).y;
|
||||||
|
child.layout(Vec2::new(available, *height));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,9 +373,8 @@ impl View for ListView {
|
|||||||
// Send the event to the focused child.
|
// Send the event to the focused child.
|
||||||
let labels_width = self.labels_width();
|
let labels_width = self.labels_width();
|
||||||
if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
|
if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
|
||||||
// If self.focus < self.scrollbase.start_line, it means the focus is not
|
let y = self.children_heights[..self.focus].iter().sum();
|
||||||
// in view. Something's fishy, so don't send the event.
|
let offset = (labels_width + 1, y);
|
||||||
let offset = (labels_width + 1, self.focus);
|
|
||||||
let result = view.on_event(event.relativized(offset));
|
let result = view.on_event(event.relativized(offset));
|
||||||
if result.is_consumed() {
|
if result.is_consumed() {
|
||||||
return result;
|
return result;
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use cursive::traits::*;
|
use cursive::{
|
||||||
use cursive::views::{
|
traits::*,
|
||||||
Checkbox, Dialog, EditView, LinearLayout, ListView, SelectView, TextView,
|
views::{
|
||||||
|
Checkbox, Dialog, EditView, LinearLayout, ListView, SelectView,
|
||||||
|
TextArea, TextView,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// This example uses a ListView.
|
// This example uses a ListView.
|
||||||
@ -18,6 +21,7 @@ fn main() {
|
|||||||
ListView::new()
|
ListView::new()
|
||||||
// Each child is a single-line view with a label
|
// Each child is a single-line view with a label
|
||||||
.child("Name", EditView::new().fixed_width(10))
|
.child("Name", EditView::new().fixed_width(10))
|
||||||
|
.child("Presentation", TextArea::new().min_height(4))
|
||||||
.child(
|
.child(
|
||||||
"Receive spam?",
|
"Receive spam?",
|
||||||
Checkbox::new().on_change(|s, checked| {
|
Checkbox::new().on_change(|s, checked| {
|
||||||
|
Loading…
Reference in New Issue
Block a user