use std::cmp::max;
use std::collections::HashMap;
use crate::experiments::focus_group::{FocusGraph, FocusGraphNode, FocusUpdate};
use crate::io::buffer::Buffer;
use crate::primitives::rect::Rect;
use crate::primitives::sized_xy::SizedXY;
use crate::primitives::xy::XY;
use crate::widget::widget::WID;
fn fill(b: &mut Buffer<WID>, wid: WID, rect: &Rect) {
for x in rect.min_x()..u16::min(rect.max_x(), b.size().x) {
for y in rect.min_y()..u16::min(rect.max_y(), b.size().y) {
let xy = XY::new(x, y);
b[xy] = wid;
}
}
}
fn walk_to_first_hit(buffer: &Buffer<WID>, wid: WID, rect: &Rect, step: (i16, i16)) -> Option<WID> {
let mut walkers: Vec<XY> = Vec::new();
if step == (-1, 0) {
for y in rect.min_y()..rect.max_y() {
walkers.push(XY::new(rect.min_x(), y))
}
}
if step == (1, 0) && rect.max_x() > 0 {
for y in rect.min_y()..rect.max_y() {
walkers.push(XY::new(rect.max_x() - 1, y))
}
}
if step == (0, -1) {
for x in rect.min_x()..rect.max_x() {
walkers.push(XY::new(x, rect.min_y()))
}
}
if step == (0, 1) && rect.max_y() > 0 {
for x in rect.min_x()..rect.max_x() {
walkers.push(XY::new(x, rect.max_y() - 1))
}
}
'outer: loop {
let mut hits: HashMap<WID, (usize, u16)> = HashMap::new();
for w in &mut walkers {
if step.0 == -1 && w.x == 0 {
break 'outer;
}
if step.1 == -1 && w.y == 0 {
break 'outer;
}
w.x = (w.x as i16 + step.0) as u16;
w.y = (w.y as i16 + step.1) as u16;
if !buffer.within(*w) {
break 'outer;
}
let dst = buffer[*w];
if dst != 0 && dst != wid {
let mut prev = match hits.get(&dst) {
None => (0, u16::MAX),
Some(p) => *p,
};
prev.0 += 1;
if step.1 != 0 && prev.1 > w.x {
prev.1 = w.x;
}
if step.0 != 0 && prev.1 > w.y {
prev.1 = w.y;
}
hits.insert(dst, prev);
}
}
let mut best_owid: usize = 0;
let mut best_hit: (usize, u16) = (0, u16::MAX);
for (owid, hit) in &hits {
if *owid == wid {
continue;
}
if hit.0 > best_hit.0 || (hit.0 == best_hit.0 && hit.1 < best_hit.1) {
best_hit = *hit;
best_owid = *owid;
}
}
if best_owid != 0 {
return Some(best_owid);
}
}
None
}
fn get_full_size(widgets_and_positions: &Vec<(WID, Rect)>) -> XY {
let mut full_size = XY::new(0, 0);
for (_, rect) in widgets_and_positions {
full_size.x = max(full_size.x, rect.max_x());
full_size.y = max(full_size.y, rect.max_y());
}
full_size
}
pub fn from_geometry<AdditionalData: Clone>(
widgets_and_positions: &Vec<(WID, AdditionalData, Rect)>,
selected: WID,
output_size: XY,
) -> FocusGraph<AdditionalData> {
let mut nodes: HashMap<WID, FocusGraphNode<AdditionalData>> = HashMap::default();
for (id, add, _) in widgets_and_positions.iter() {
nodes.insert(*id, FocusGraphNode::new(*id, add.clone()));
}
let mut fgi = FocusGraph::<AdditionalData>::new(nodes, selected);
let mut buffer: Buffer<WID> = Buffer::new(output_size);
for (wid, _, rect) in widgets_and_positions.iter() {
fill(&mut buffer, *wid, rect);
}
for (source, _, rect) in widgets_and_positions {
let mut edges: Vec<(FocusUpdate, WID)> = Vec::new();
if let Some(left) = walk_to_first_hit(&buffer, *source, rect, (-1, 0)) {
edges.push((FocusUpdate::Left, left))
}
if let Some(right) = walk_to_first_hit(&buffer, *source, rect, (1, 0)) {
edges.push((FocusUpdate::Right, right))
}
if let Some(down) = walk_to_first_hit(&buffer, *source, rect, (0, 1)) {
edges.push((FocusUpdate::Down, down))
}
if let Some(up) = walk_to_first_hit(&buffer, *source, rect, (0, -1)) {
edges.push((FocusUpdate::Up, up))
}
for (focus_update, target) in edges {
fgi.add_edge(*source, focus_update, target);
}
}
fgi
}
#[cfg(test)]
mod tests {
use crate::experiments::focus_group::FocusUpdate;
use crate::experiments::from_geometry::{from_geometry, get_full_size};
use crate::primitives::rect::Rect;
use crate::primitives::xy::XY;
use crate::widget::widget::WID;
#[test]
fn full_size_test() {
let widgets_and_positions: Vec<(WID, Rect)> = vec![
(1, Rect::new((2, 0).into(), (1, 1).into())),
(2, Rect::new((0, 2).into(), (1, 1).into())),
(3, Rect::new((2, 2).into(), (1, 1).into())),
(4, Rect::new((4, 2).into(), (1, 1).into())),
(5, Rect::new((2, 4).into(), (3, 1).into())),
];
let full_size = get_full_size(&widgets_and_positions);
assert_eq!(full_size, XY::new(5, 5));
}
#[test]
fn from_geometry_test() {
let widgets_and_positions: Vec<(WID, (), Rect)> = vec![
(1, (), Rect::new((2, 0).into(), (1, 1).into())),
(2, (), Rect::new((0, 2).into(), (1, 1).into())),
(3, (), Rect::new((2, 2).into(), (1, 1).into())),
(4, (), Rect::new((4, 2).into(), (1, 1).into())),
(5, (), Rect::new((2, 4).into(), (3, 1).into())),
];
let mut focus_group = from_geometry::<()>(&widgets_and_positions, 1, XY::new(5, 5));
assert_eq!(focus_group.get_focused_id(), 1);
assert!(!focus_group.update_focus(FocusUpdate::Left));
assert!(!focus_group.update_focus(FocusUpdate::Right));
assert!(!focus_group.update_focus(FocusUpdate::Up));
assert!(focus_group.update_focus(FocusUpdate::Down));
assert_eq!(focus_group.get_focused_id(), 3);
assert!(focus_group.update_focus(FocusUpdate::Up));
assert_eq!(focus_group.get_focused_id(), 1);
assert!(focus_group.update_focus(FocusUpdate::Down));
assert_eq!(focus_group.get_focused_id(), 3);
assert!(focus_group.update_focus(FocusUpdate::Left));
assert_eq!(focus_group.get_focused_id(), 2);
assert!(!focus_group.update_focus(FocusUpdate::Up));
assert!(!focus_group.update_focus(FocusUpdate::Down));
assert!(focus_group.update_focus(FocusUpdate::Right));
assert_eq!(focus_group.get_focused_id(), 3);
assert!(focus_group.update_focus(FocusUpdate::Right));
assert_eq!(focus_group.get_focused_id(), 4);
assert!(!focus_group.update_focus(FocusUpdate::Up));
assert!(focus_group.update_focus(FocusUpdate::Down));
assert_eq!(focus_group.get_focused_id(), 5);
assert!(!focus_group.update_focus(FocusUpdate::Left));
assert!(!focus_group.update_focus(FocusUpdate::Right));
assert!(!focus_group.update_focus(FocusUpdate::Down));
assert!(focus_group.update_focus(FocusUpdate::Up));
assert_eq!(focus_group.get_focused_id(), 3);
}
}