Fix focus in LinearLayout

Focus now at least works. #42 is still open though.
This commit is contained in:
Alexandre Bury 2016-07-13 22:11:03 -07:00
parent 9390504290
commit 5059e21cb1
4 changed files with 95 additions and 47 deletions

View File

@ -9,6 +9,7 @@ use std::cmp::{Ordering, max, min};
pub type Vec2 = XY<usize>; pub type Vec2 = XY<usize>;
impl PartialOrd for Vec2 { impl PartialOrd for Vec2 {
/// `a < b` <=> `a.x < b.x && a.y < b.y`
fn partial_cmp(&self, other: &Vec2) -> Option<Ordering> { fn partial_cmp(&self, other: &Vec2) -> Option<Ordering> {
if self == other { if self == other {
Some(Ordering::Equal) Some(Ordering::Equal)
@ -27,14 +28,14 @@ impl Vec2 {
pub fn max<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self { pub fn max<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
Vec2::new(max(a.x, b.x), max(a.y, b.y)) a.zip_map(b, max)
} }
/// Returns a new Vec2 that is no larger than any input in both dimensions. /// Returns a new Vec2 that is no larger than any input in both dimensions.
pub fn min<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self { pub fn min<A: Into<Vec2>, B: Into<Vec2>>(a: A, b: B) -> Self {
let a = a.into(); let a = a.into();
let b = b.into(); let b = b.into();
Vec2::new(min(a.x, b.x), min(a.y, b.y)) a.zip_map(b, min)
} }
/// Returns the minimum of `self` and `other`. /// Returns the minimum of `self` and `other`.
@ -105,11 +106,7 @@ impl<T: Into<Vec2>> Add<T> for Vec2 {
type Output = Vec2; type Output = Vec2;
fn add(self, other: T) -> Vec2 { fn add(self, other: T) -> Vec2 {
let ov = other.into(); self.zip_map(other.into(), Add::add)
Vec2 {
x: self.x + ov.x,
y: self.y + ov.y,
}
} }
} }
@ -117,11 +114,7 @@ impl<T: Into<Vec2>> Sub<T> for Vec2 {
type Output = Vec2; type Output = Vec2;
fn sub(self, other: T) -> Vec2 { fn sub(self, other: T) -> Vec2 {
let ov = other.into(); self.zip_map(other.into(), Sub::sub)
Vec2 {
x: self.x - ov.x,
y: self.y - ov.y,
}
} }
} }
@ -129,10 +122,7 @@ impl Div<usize> for Vec2 {
type Output = Vec2; type Output = Vec2;
fn div(self, other: usize) -> Vec2 { fn div(self, other: usize) -> Vec2 {
Vec2 { self.map(|s| s / other)
x: self.x / other,
y: self.y / other,
}
} }
} }
@ -140,10 +130,7 @@ impl Mul<usize> for Vec2 {
type Output = Vec2; type Output = Vec2;
fn mul(self, other: usize) -> Vec2 { fn mul(self, other: usize) -> Vec2 {
Vec2 { self.map(|s| s * other)
x: self.x * other,
y: self.y * other,
}
} }
} }

View File

@ -28,6 +28,14 @@ impl Child {
self.size = self.view.get_min_size(req); self.size = self.view.get_min_size(req);
self.size self.size
} }
fn as_ref(&self) -> &View {
&*self.view
}
fn as_mut(&mut self) -> &mut View {
&mut *self.view
}
} }
impl LinearLayout { impl LinearLayout {
@ -85,7 +93,7 @@ impl LinearLayout {
Some(ref cache) => { Some(ref cache) => {
// Is our cache even valid? // Is our cache even valid?
// Also, is any child invalidating the layout? // Also, is any child invalidating the layout?
if cache.x.accept(req.x) && cache.y.accept(req.y) && if cache.zip_map(req, SizeCache::accept).both() &&
self.children_are_sleeping() { self.children_are_sleeping() {
Some(cache.map(|s| s.value)) Some(cache.map(|s| s.value))
} else { } else {
@ -98,9 +106,36 @@ impl LinearLayout {
fn children_are_sleeping(&self) -> bool { fn children_are_sleeping(&self) -> bool {
!self.children !self.children
.iter() .iter()
.map(|c| &*c.view) .map(Child::as_ref)
.any(View::needs_relayout) .any(View::needs_relayout)
} }
fn focus_prev(&mut self) -> EventResult {
if let Some(i) = self.children[..self.focus]
.iter_mut()
.rev()
.map(Child::as_mut)
.position(View::take_focus) {
self.focus = i;
EventResult::Consumed(None)
} else {
EventResult::Ignored
}
}
fn focus_next(&mut self) -> EventResult {
if let Some(i) = self.children[(self.focus + 1)..]
.iter_mut()
.rev()
.map(Child::as_mut)
.position(View::take_focus) {
self.focus = i;
EventResult::Consumed(None)
} else {
EventResult::Ignored
}
}
} }
impl View for LinearLayout { impl View for LinearLayout {
@ -166,6 +201,7 @@ impl View for LinearLayout {
let budget_req = req.with(self.orientation, 1); let budget_req = req.with(self.orientation, 1);
// println_stderr!("Budget req: {:?}", budget_req); // println_stderr!("Budget req: {:?}", budget_req);
// See how they like it that way
let min_sizes: Vec<Vec2> = self.children let min_sizes: Vec<Vec2> = self.children
.iter_mut() .iter_mut()
.map(|c| c.get_min_size(budget_req)) .map(|c| c.get_min_size(budget_req))
@ -174,9 +210,10 @@ impl View for LinearLayout {
// println_stderr!("Min sizes: {:?}", min_sizes); // println_stderr!("Min sizes: {:?}", min_sizes);
// println_stderr!("Desperate: {:?}", desperate); // println_stderr!("Desperate: {:?}", desperate);
// I really hope it fits this time... // This is the lowest we'll ever go. It better fit at least.
if !desperate.fits_in(req) { if !desperate.fits_in(req) {
// Just give up... // Just give up...
// TODO: print some error message or something
// println_stderr!("Seriously? {:?} > {:?}???", desperate, req); // println_stderr!("Seriously? {:?} > {:?}???", desperate, req);
self.cache = Some(SizeCache::build(desperate, req)); self.cache = Some(SizeCache::build(desperate, req));
return desperate; return desperate;
@ -198,19 +235,25 @@ impl View for LinearLayout {
// So... distribute `available` to reduce the overweight... // So... distribute `available` to reduce the overweight...
// TODO: use child weight in the distribution... // TODO: use child weight in the distribution...
// We'll give everyone his share of what we have left,
// starting with those who ask the least.
overweight.sort_by_key(|&(_, weight)| weight); overweight.sort_by_key(|&(_, weight)| weight);
let mut allocations = vec![0; overweight.len()]; let mut allocations = vec![0; overweight.len()];
for (i, &(j, weight)) in overweight.iter().enumerate() { for (i, &(j, weight)) in overweight.iter().enumerate() {
// This is the number of people we still have to feed.
let remaining = overweight.len() - i; let remaining = overweight.len() - i;
// How much we can spare on each one
let budget = available / remaining; let budget = available / remaining;
// Maybe he doesn't even need that much?
let spent = min(budget, weight); let spent = min(budget, weight);
allocations[j] = spent; allocations[j] = spent;
available -= spent; available -= spent;
} }
// println_stderr!("Allocations: {:?}", allocations); // println_stderr!("Allocations: {:?}", allocations);
// Final lengths are the minimum ones + allocations // Final lengths are the minimum ones + generous allocations
let final_lengths: Vec<Vec2> = min_sizes.iter() let final_lengths: Vec<Vec2> = min_sizes.iter()
.map(|v| self.orientation.get(v)) .map(|v| self.orientation.get(v))
.zip(allocations.iter()) .zip(allocations.iter())
@ -219,60 +262,66 @@ impl View for LinearLayout {
.collect(); .collect();
// println_stderr!("Final sizes: {:?}", final_lengths); // println_stderr!("Final sizes: {:?}", final_lengths);
// Let's ask everyone one last time. Everyone should be happy.
// (But they may ask more on the other axis.)
let final_sizes: Vec<Vec2> = self.children let final_sizes: Vec<Vec2> = self.children
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.map(|(i, c)| { .map(|(i, c)| c.get_min_size(final_lengths[i]))
c.get_min_size(final_lengths[i])
})
.collect(); .collect();
// println_stderr!("Final sizes2: {:?}", final_sizes); // println_stderr!("Final sizes2: {:?}", final_sizes);
// 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());
// 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));
compromise compromise
} }
fn take_focus(&mut self) -> bool {
if let Some(i) = self.children
.iter_mut()
.map(Child::as_mut)
.position(View::take_focus) {
self.focus = i;
true
} else {
false
}
}
fn on_event(&mut self, event: Event) -> EventResult { fn on_event(&mut self, event: Event) -> EventResult {
match self.children[self.focus].view.on_event(event) { match self.children[self.focus].view.on_event(event) {
EventResult::Ignored => { EventResult::Ignored => {
match event { match event {
Event::Key(Key::Tab) if self.focus > 0 => { Event::Shift(Key::Tab) if self.focus > 0 => {
self.focus -= 1; self.focus_prev()
EventResult::Consumed(None)
} }
Event::Shift(Key::Tab) if self.focus + 1 < Event::Key(Key::Tab) if self.focus + 1 <
self.children.len() => { self.children.len() => {
self.focus += 1; self.focus_next()
EventResult::Consumed(None)
} }
Event::Key(Key::Left) if self.orientation == Event::Key(Key::Left) if self.orientation ==
Orientation::Horizontal && Orientation::Horizontal &&
self.focus > 0 => { self.focus > 0 => {
self.focus -= 1; self.focus_prev()
EventResult::Consumed(None)
} }
Event::Key(Key::Up) if self.orientation == Event::Key(Key::Up) if self.orientation ==
Orientation::Vertical && Orientation::Vertical &&
self.focus > 0 => { self.focus > 0 => self.focus_prev(),
self.focus -= 1;
EventResult::Consumed(None)
}
Event::Key(Key::Right) if self.orientation == Event::Key(Key::Right) if self.orientation ==
Orientation::Horizontal && Orientation::Horizontal &&
self.focus + 1 < self.focus + 1 <
self.children.len() => { self.children.len() => {
self.focus += 1; self.focus_next()
EventResult::Consumed(None)
} }
Event::Key(Key::Down) if self.orientation == Event::Key(Key::Down) if self.orientation ==
Orientation::Vertical && Orientation::Vertical &&
self.focus + 1 < self.focus + 1 <
self.children.len() => { self.children.len() => {
self.focus += 1; self.focus_next()
EventResult::Consumed(None)
} }
_ => EventResult::Ignored, _ => EventResult::Ignored,
} }

View File

@ -162,7 +162,7 @@ impl SizeCache {
} }
/// Returns `true` if `self` is still valid for the given `request`. /// Returns `true` if `self` is still valid for the given `request`.
pub fn accept(&self, request: usize) -> bool { pub fn accept(self, request: usize) -> bool {
if request < self.value { if request < self.value {
false false
} else if request == self.value { } else if request == self.value {

View File

@ -60,9 +60,21 @@ impl <T> XY<Option<T>> {
} }
} }
impl XY<bool> {
/// Returns `true` if any of `x` or `y` is `true`.
pub fn any(&self) -> bool {
self.x || self.y
}
/// Returns `true` if both `x` and `y` are `true`.
pub fn both(&self) -> bool {
self.x && self.y
}
}
impl<T: Copy> XY<T> { impl<T: Copy> XY<T> {
/// Creates a `XY` with both `x` and `y` set to `value`. /// Creates a `XY` with both `x` and `y` set to `value`.
pub fn both(value: T) -> Self { pub fn both_from(value: T) -> Self {
XY::new(value, value) XY::new(value, value)
} }
} }