use itertools::Itertools;
use std::cmp::{Ord, Ordering};
use std::collections::hash_map::{Entry, HashMap};
use std::fmt::Debug;
use std::hash::Hash;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LayoutRect {
pub width: usize,
pub height: usize,
}
pub struct AutoLayout<K, V> {
layout_cell: LayoutRect,
vacant: Vec<usize>,
items: Vec<Option<Item<V>>>,
pointers: HashMap<K, usize>,
}
impl<K, V> AutoLayout<K, V> {
pub fn new(layout_cell: LayoutRect) -> Self {
Self {
layout_cell,
vacant: Vec::new(),
items: Vec::new(),
pointers: HashMap::new(),
}
}
pub fn change_cell(&mut self, layout_cell: LayoutRect) {
self.layout_cell = layout_cell;
}
}
impl<K, V> AutoLayout<K, V> {
pub fn keys(&self) -> impl Iterator<Item = &'_ K> {
self.pointers.keys()
}
pub fn values(&self) -> impl Iterator<Item = Option<&Item<V>>> {
self.items.iter().map(Option::as_ref)
}
}
impl<K, V> AutoLayout<K, V>
where
V: Ord,
{
pub fn relayout(&mut self, real_width: f64, _real_height: f64) -> usize {
let view_width = real_width as usize;
let rounded_cols = (view_width / self.layout_cell.width).max(1);
let col_width = view_width / rounded_cols;
let row_height = self.layout_cell.height;
let mut items = self
.items
.iter_mut()
.map(Option::as_mut)
.flatten()
.sorted_by(Item::by_priority)
.peekable();
let mut row_num = 0;
let mut col_shift = 0;
while let Some(item) = items.next() {
let item_height = item.preffered_size.rows * row_height;
let mut remained_cols = rounded_cols - col_shift;
let mut occupied_cols = item
.preffered_size
.cols
.min(remained_cols)
.min(rounded_cols);
let occupied_rows = item.preffered_size.rows;
remained_cols -= occupied_cols;
let mut stretch_x = false;
if let Some(next_item) = items.peek() {
if remained_cols < next_item.preffered_size.cols {
stretch_x = true;
}
if occupied_rows != next_item.preffered_size.rows {
stretch_x = true;
}
} else {
let more_than_one_col = occupied_cols > 1;
let more_than_half_occupied = remained_cols < (col_shift + occupied_cols);
if more_than_one_col || more_than_half_occupied {
stretch_x = true;
}
}
let real_cols = occupied_cols; if stretch_x {
occupied_cols += remained_cols;
}
let item_width = real_cols * col_width;
let item_shift = col_shift * col_width;
item.relocate(row_num * row_height, item_shift);
item.resize(item_width, item_height);
col_shift += occupied_cols;
if col_shift >= rounded_cols {
row_num += occupied_rows;
col_shift = 0;
}
}
if col_shift > 0 {
row_num += 1;
}
row_num * row_height }
}
impl<K, V> AutoLayout<K, V>
where
K: Debug + Eq + Hash,
{
pub fn acquire(&mut self, key: K, item: Item<V>) {
let entry = self.pointers.entry(key);
let value = Some(item);
match entry {
Entry::Vacant(entry) => {
if let Some(idx) = self.vacant.pop() {
let cell = self.items.get_mut(idx).unwrap();
*cell = value;
entry.insert(idx);
} else {
entry.insert(self.items.len());
self.items.push(value);
}
}
Entry::Occupied(entry) => {
log::warn!("Attempt to add by the key {:?} twice.", entry.key());
}
}
}
pub fn release(&mut self, key: K) {
if let Some(idx) = self.pointers.remove(&key) {
let cell = self.items.get_mut(idx).unwrap();
*cell = None;
self.vacant.push(idx);
} else {
log::error!("No cell in frame with the key: {:?}", key);
}
}
}
#[derive(Debug, Clone)]
pub struct PrefferedSize {
pub cols: usize,
pub rows: usize,
pub priority: usize,
}
pub struct Item<T> {
pub preffered_size: PrefferedSize,
pub width: usize,
pub height: usize,
pub top: usize,
pub left: usize,
pub record: T,
}
impl<T> Item<T> {
pub fn new(preffered_size: PrefferedSize, record: T) -> Self {
Self {
preffered_size,
width: 200,
height: 100,
top: 0,
left: 0,
record,
}
}
pub fn by_priority<'r, 's>(a: &'r &mut Self, b: &'s &mut Self) -> Ordering
where
T: Ord,
{
let ord = Ord::cmp(&a.preffered_size.priority, &b.preffered_size.priority);
if ord == Ordering::Equal {
Ord::cmp(&a.record, &b.record)
} else {
ord
}
}
fn relocate(&mut self, top: usize, left: usize) {
self.top = top;
self.left = left;
}
fn resize(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
}
}