2016-07-13 08:19:05 +00:00
|
|
|
use XY;
|
2016-07-02 07:47:38 +00:00
|
|
|
use view::View;
|
2016-07-13 08:19:05 +00:00
|
|
|
use view::SizeCache;
|
2015-06-08 03:58:10 +00:00
|
|
|
use vec::Vec2;
|
|
|
|
use printer::Printer;
|
2015-06-08 19:23:36 +00:00
|
|
|
use orientation::Orientation;
|
2016-03-15 22:37:57 +00:00
|
|
|
use event::{Event, EventResult, Key};
|
2015-06-08 03:58:10 +00:00
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
use std::cmp::min;
|
|
|
|
|
2015-06-08 22:38:10 +00:00
|
|
|
/// Arranges its children linearly according to its orientation.
|
2015-06-08 03:58:10 +00:00
|
|
|
pub struct LinearLayout {
|
|
|
|
children: Vec<Child>,
|
|
|
|
orientation: Orientation,
|
2015-06-08 22:11:44 +00:00
|
|
|
focus: usize,
|
2016-07-11 00:41:49 +00:00
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
cache: Option<XY<SizeCache>>,
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-08 22:38:10 +00:00
|
|
|
struct Child {
|
|
|
|
view: Box<View>,
|
|
|
|
size: Vec2,
|
|
|
|
weight: usize,
|
|
|
|
}
|
2015-06-08 03:58:10 +00:00
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
impl Child {
|
|
|
|
fn get_min_size(&mut self, req: Vec2) -> Vec2 {
|
|
|
|
self.size = self.view.get_min_size(req);
|
|
|
|
self.size
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-08 03:58:10 +00:00
|
|
|
impl LinearLayout {
|
2015-06-08 22:38:10 +00:00
|
|
|
/// Creates a new layout with the given orientation.
|
2015-06-08 03:58:10 +00:00
|
|
|
pub fn new(orientation: Orientation) -> Self {
|
|
|
|
LinearLayout {
|
|
|
|
children: Vec::new(),
|
|
|
|
orientation: orientation,
|
2015-06-08 22:11:44 +00:00
|
|
|
focus: 0,
|
2016-07-13 08:19:05 +00:00
|
|
|
cache: None,
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-08 22:38:10 +00:00
|
|
|
/// Modifies the weight of the last child added.
|
|
|
|
///
|
|
|
|
/// It is an error to call this before adding a child (and it will panic).
|
2015-06-08 03:58:10 +00:00
|
|
|
pub fn weight(mut self, weight: usize) -> Self {
|
|
|
|
self.children.last_mut().unwrap().weight = weight;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2015-06-08 22:38:10 +00:00
|
|
|
/// Adds a child to the layout.
|
2015-06-08 03:58:10 +00:00
|
|
|
pub fn child<V: View + 'static>(mut self, view: V) -> Self {
|
|
|
|
self.children.push(Child {
|
|
|
|
view: Box::new(view),
|
|
|
|
size: Vec2::zero(),
|
|
|
|
weight: 0,
|
|
|
|
});
|
2016-07-13 08:19:05 +00:00
|
|
|
self.invalidate();
|
2015-06-08 03:58:10 +00:00
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
// Invalidate the view, to request a layout next time
|
|
|
|
fn invalidate(&mut self) {
|
|
|
|
self.cache = None;
|
|
|
|
}
|
|
|
|
|
2015-06-08 22:38:10 +00:00
|
|
|
/// Creates a new vertical layout.
|
2015-06-08 03:58:10 +00:00
|
|
|
pub fn vertical() -> Self {
|
|
|
|
LinearLayout::new(Orientation::Vertical)
|
|
|
|
}
|
2015-06-08 22:38:10 +00:00
|
|
|
|
|
|
|
/// Creates a new horizontal layout.
|
2015-06-08 03:58:10 +00:00
|
|
|
pub fn horizontal() -> Self {
|
|
|
|
LinearLayout::new(Orientation::Horizontal)
|
|
|
|
}
|
2016-07-13 08:19:05 +00:00
|
|
|
|
|
|
|
// If the cache can be used, return the cached size.
|
|
|
|
// Otherwise, return None.
|
|
|
|
fn get_cache(&self, req: Vec2) -> Option<Vec2> {
|
|
|
|
match self.cache {
|
|
|
|
None => None,
|
|
|
|
Some(ref cache) => {
|
|
|
|
// Is our cache even valid?
|
|
|
|
// Also, is any child invalidating the layout?
|
|
|
|
if cache.x.accept(req.x) && cache.y.accept(req.y) &&
|
|
|
|
self.children_are_sleeping() {
|
|
|
|
Some(cache.map(|s| s.value))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn children_are_sleeping(&self) -> bool {
|
|
|
|
!self.children
|
|
|
|
.iter()
|
|
|
|
.map(|c| &*c.view)
|
|
|
|
.any(View::needs_relayout)
|
|
|
|
}
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl View for LinearLayout {
|
|
|
|
fn draw(&mut self, printer: &Printer) {
|
|
|
|
// Use pre-computed sizes
|
|
|
|
let mut offset = Vec2::zero();
|
2016-03-15 22:37:57 +00:00
|
|
|
for (i, child) in self.children.iter_mut().enumerate() {
|
2016-07-11 02:11:21 +00:00
|
|
|
let printer =
|
|
|
|
&printer.sub_printer(offset, child.size, i == self.focus);
|
|
|
|
child.view.draw(printer);
|
2015-06-08 03:58:10 +00:00
|
|
|
|
2016-07-02 07:47:38 +00:00
|
|
|
// On the axis given by the orientation,
|
|
|
|
// add the child size to the offset.
|
2016-06-28 05:10:59 +00:00
|
|
|
*self.orientation.get_ref(&mut offset) += self.orientation
|
2016-07-10 02:05:51 +00:00
|
|
|
.get(&child.size);
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-11 00:41:49 +00:00
|
|
|
fn needs_relayout(&self) -> bool {
|
2016-07-13 08:19:05 +00:00
|
|
|
if self.cache.is_none() {
|
2016-07-11 00:41:49 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
!self.children_are_sleeping()
|
2016-07-11 00:41:49 +00:00
|
|
|
}
|
|
|
|
|
2015-06-08 03:58:10 +00:00
|
|
|
fn layout(&mut self, size: Vec2) {
|
2016-07-13 08:19:05 +00:00
|
|
|
// If we can get away without breaking a sweat, you can bet we will.
|
|
|
|
if self.get_cache(size).is_none() {
|
|
|
|
self.get_min_size(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
for child in &mut self.children {
|
|
|
|
child.view.layout(child.size);
|
|
|
|
}
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
|
|
|
|
2016-07-10 01:23:58 +00:00
|
|
|
fn get_min_size(&mut self, req: Vec2) -> Vec2 {
|
2016-07-13 08:19:05 +00:00
|
|
|
// Did anything change since last time?
|
|
|
|
if let Some(size) = self.get_cache(req) {
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2015-06-08 03:58:10 +00:00
|
|
|
// First, make a naive scenario: everything will work fine.
|
2016-03-15 22:37:57 +00:00
|
|
|
let sizes: Vec<Vec2> = self.children
|
2016-07-10 02:05:51 +00:00
|
|
|
.iter_mut()
|
2016-07-13 08:19:05 +00:00
|
|
|
.map(|c| c.get_min_size(req))
|
2016-07-10 02:05:51 +00:00
|
|
|
.collect();
|
2016-07-13 08:19:05 +00:00
|
|
|
// println_stderr!("Ideal sizes: {:?}", sizes);
|
|
|
|
let ideal = self.orientation.stack(sizes.iter());
|
|
|
|
// println_stderr!("Ideal result: {:?}", ideal);
|
2015-06-08 03:58:10 +00:00
|
|
|
|
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
// Does it fit?
|
|
|
|
if ideal.fits_in(req) {
|
|
|
|
// Champagne!
|
|
|
|
self.cache = Some(SizeCache::build(ideal, req));
|
|
|
|
return ideal;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ok, so maybe it didn't.
|
|
|
|
// Budget cuts, everyone.
|
|
|
|
let budget_req = req.with(self.orientation, 1);
|
|
|
|
// println_stderr!("Budget req: {:?}", budget_req);
|
|
|
|
|
|
|
|
let min_sizes: Vec<Vec2> = self.children
|
|
|
|
.iter_mut()
|
|
|
|
.map(|c| c.get_min_size(budget_req))
|
|
|
|
.collect();
|
|
|
|
let desperate = self.orientation.stack(min_sizes.iter());
|
|
|
|
// println_stderr!("Min sizes: {:?}", min_sizes);
|
|
|
|
// println_stderr!("Desperate: {:?}", desperate);
|
|
|
|
|
|
|
|
// I really hope it fits this time...
|
|
|
|
if !desperate.fits_in(req) {
|
|
|
|
// Just give up...
|
|
|
|
// println_stderr!("Seriously? {:?} > {:?}???", desperate, req);
|
|
|
|
self.cache = Some(SizeCache::build(desperate, req));
|
|
|
|
return desperate;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This here is how much we're generously offered
|
|
|
|
let mut available = self.orientation.get(&(req - desperate));
|
|
|
|
// println_stderr!("Available: {:?}", available);
|
|
|
|
|
|
|
|
// Here, we have to make a compromise between the ideal
|
|
|
|
// and the desperate solutions.
|
|
|
|
let mut overweight: Vec<(usize, usize)> = sizes.iter()
|
|
|
|
.map(|v| self.orientation.get(v))
|
|
|
|
.zip(min_sizes.iter().map(|v| self.orientation.get(v)))
|
|
|
|
.map(|(a, b)| a - b)
|
|
|
|
.enumerate()
|
|
|
|
.collect();
|
|
|
|
// println_stderr!("Overweight: {:?}", overweight);
|
|
|
|
|
|
|
|
// So... distribute `available` to reduce the overweight...
|
|
|
|
// TODO: use child weight in the distribution...
|
|
|
|
overweight.sort_by_key(|&(_, weight)| weight);
|
|
|
|
let mut allocations = vec![0; overweight.len()];
|
|
|
|
|
|
|
|
for (i, &(j, weight)) in overweight.iter().enumerate() {
|
|
|
|
let remaining = overweight.len() - i;
|
|
|
|
let budget = available / remaining;
|
|
|
|
let spent = min(budget, weight);
|
|
|
|
allocations[j] = spent;
|
|
|
|
available -= spent;
|
|
|
|
}
|
|
|
|
// println_stderr!("Allocations: {:?}", allocations);
|
|
|
|
|
|
|
|
// Final lengths are the minimum ones + allocations
|
|
|
|
let final_lengths: Vec<Vec2> = min_sizes.iter()
|
|
|
|
.map(|v| self.orientation.get(v))
|
|
|
|
.zip(allocations.iter())
|
|
|
|
.map(|(a, b)| a + b)
|
|
|
|
.map(|l| req.with(self.orientation, l))
|
|
|
|
.collect();
|
|
|
|
// println_stderr!("Final sizes: {:?}", final_lengths);
|
|
|
|
|
|
|
|
let final_sizes: Vec<Vec2> = self.children
|
|
|
|
.iter_mut()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, c)| {
|
|
|
|
c.get_min_size(final_lengths[i])
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
// println_stderr!("Final sizes2: {:?}", final_sizes);
|
2015-06-08 03:58:10 +00:00
|
|
|
|
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
let compromise = self.orientation.stack(final_sizes.iter());
|
|
|
|
self.cache = Some(SizeCache::build(compromise, req));
|
2015-06-08 03:58:10 +00:00
|
|
|
|
2016-07-13 08:19:05 +00:00
|
|
|
compromise
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|
2015-06-08 22:11:44 +00:00
|
|
|
|
|
|
|
fn on_event(&mut self, event: Event) -> EventResult {
|
|
|
|
match self.children[self.focus].view.on_event(event) {
|
2016-06-25 23:36:22 +00:00
|
|
|
EventResult::Ignored => {
|
|
|
|
match event {
|
2016-06-28 05:40:11 +00:00
|
|
|
Event::Key(Key::Tab) if self.focus > 0 => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus -= 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-11 01:27:26 +00:00
|
|
|
Event::Shift(Key::Tab) if self.focus + 1 <
|
2016-07-11 02:11:21 +00:00
|
|
|
self.children.len() => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus += 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-10 02:05:51 +00:00
|
|
|
Event::Key(Key::Left) if self.orientation ==
|
|
|
|
Orientation::Horizontal &&
|
2016-06-30 00:36:20 +00:00
|
|
|
self.focus > 0 => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus -= 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-10 02:05:51 +00:00
|
|
|
Event::Key(Key::Up) if self.orientation ==
|
|
|
|
Orientation::Vertical &&
|
2016-06-30 00:36:20 +00:00
|
|
|
self.focus > 0 => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus -= 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-10 02:05:51 +00:00
|
|
|
Event::Key(Key::Right) if self.orientation ==
|
|
|
|
Orientation::Horizontal &&
|
|
|
|
self.focus + 1 <
|
|
|
|
self.children.len() => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus += 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
2016-07-10 02:05:51 +00:00
|
|
|
Event::Key(Key::Down) if self.orientation ==
|
|
|
|
Orientation::Vertical &&
|
|
|
|
self.focus + 1 <
|
|
|
|
self.children.len() => {
|
2016-06-25 23:36:22 +00:00
|
|
|
self.focus += 1;
|
|
|
|
EventResult::Consumed(None)
|
|
|
|
}
|
|
|
|
_ => EventResult::Ignored,
|
2016-03-15 22:37:57 +00:00
|
|
|
}
|
2016-06-25 23:36:22 +00:00
|
|
|
}
|
2015-06-08 22:11:44 +00:00
|
|
|
res => res,
|
|
|
|
}
|
|
|
|
}
|
2015-06-08 03:58:10 +00:00
|
|
|
}
|