///|
/// Select the next column or row to resize. Returning None stops resizing.
/// Arguments after the selector are minimum sizes and current sizes, in index order.
/// An empty minimum-size array means no explicit lower bounds were supplied.
/// Zero sizes are skipped by default. A negative minimum makes a zero-sized
/// entry eligible, allowing growth operations to expand an initially empty row.
/// Table options call `fresh` once per resize operation. Stateful selectors
/// should return an independent initial state; stateless selectors can use the
/// default implementation, which returns the selector unchanged.
///
/// ```mbt check
/// test {
/// let priority = @tabular.Priority::round_robin()
/// let values = [5, 0, 2]
/// assert_eq(
/// @tabular.ResizeSelector::select(priority, [0, 0, 0], values),
/// Some(0),
/// )
/// assert_eq(
/// @tabular.ResizeSelector::select(priority, [0, 0, 0], values),
/// Some(2),
/// )
/// assert_eq(
/// @tabular.ResizeSelector::select(priority, [0, 0, 0], values),
/// Some(0),
/// )
/// let independent = priority.fresh()
/// assert_eq(independent.select([], values), Some(0))
/// assert_eq(priority.select([], values), Some(2))
/// }
/// ```
pub(open) trait ResizeSelector {
fn select(Self, Array[Int], Array[Int]) -> Int?
fn fresh(Self) -> &ResizeSelector = _
}
///|
impl ResizeSelector with fn fresh(self) {
self
}
///|
/// Factories for the upstream dimension-resizing priorities.
pub enum Priority {}
///|
/// Choose the lowest or highest index when eligible sizes are equal.
/// This applies to both columns (left/right) and rows (top/bottom).
pub(all) enum TieBreak {
First
Last
} derive(Eq, Debug)
///|
/// Round-robin selection retains its next index. Zero sizes are skipped unless
/// a negative minimum explicitly makes them eligible for growth.
pub struct PriorityRoundRobin {
priv mut index : Int
} derive(Eq, Debug)
///|
pub fn PriorityRoundRobin::new() -> PriorityRoundRobin {
{ index: 0, }
}
///|
pub fn PriorityRoundRobin::default() -> PriorityRoundRobin {
PriorityRoundRobin::new()
}
///|
pub impl ResizeSelector for PriorityRoundRobin with fn fresh(_self) {
PriorityRoundRobin::new()
}
///|
pub impl ResizeSelector for PriorityRoundRobin with fn select(
self,
minimums,
sizes,
) {
if sizes.is_empty() {
return None
}
let mut index = self.index % sizes.length()
for _ in 0.. PriorityFirst {
PriorityFirst::{ }
}
///|
pub fn PriorityFirst::default() -> PriorityFirst {
PriorityFirst::new()
}
///|
pub impl ResizeSelector for PriorityFirst with fn select(_self, minimums, sizes) {
for index in 0.. minimums.get(index).unwrap_or(0) {
return Some(index)
}
}
None
}
///|
/// Select from the highest eligible index (rightmost column or bottommost row).
pub struct PriorityLast {} derive(Eq, Debug)
///|
pub fn PriorityLast::new() -> PriorityLast {
PriorityLast::{ }
}
///|
pub fn PriorityLast::default() -> PriorityLast {
PriorityLast::new()
}
///|
pub impl ResizeSelector for PriorityLast with fn select(_self, minimums, sizes) {
for index = sizes.length() - 1; index >= 0; index = index - 1 {
if sizes[index] > minimums.get(index).unwrap_or(0) {
return Some(index)
}
}
None
}
///|
/// Select a minimum size; equal values use the explicitly chosen tie break.
pub struct PriorityMin {
priv tie_break : TieBreak
} derive(Eq, Debug)
///|
pub fn PriorityMin::new(tie_break~ : TieBreak) -> PriorityMin {
{ tie_break, }
}
///|
/// Preserve the upstream default: equal minimum sizes prefer the last index.
pub fn PriorityMin::default() -> PriorityMin {
PriorityMin::prefer_last()
}
///|
pub fn PriorityMin::prefer_last() -> PriorityMin {
PriorityMin::new(tie_break=Last)
}
///|
pub fn PriorityMin::prefer_first() -> PriorityMin {
PriorityMin::new(tie_break=First)
}
///|
/// Select a maximum size; equal values use the explicitly chosen tie break.
pub struct PriorityMax {
priv tie_break : TieBreak
} derive(Eq, Debug)
///|
pub fn PriorityMax::new(tie_break~ : TieBreak) -> PriorityMax {
{ tie_break, }
}
///|
/// Preserve the upstream default: equal maximum sizes prefer the first index.
pub fn PriorityMax::default() -> PriorityMax {
PriorityMax::prefer_first()
}
///|
pub fn PriorityMax::prefer_first() -> PriorityMax {
PriorityMax::new(tie_break=First)
}
///|
pub fn PriorityMax::prefer_last() -> PriorityMax {
PriorityMax::new(tie_break=Last)
}
///|
fn select_extreme(
minimums : Array[Int],
sizes : Array[Int],
tie_break : TieBreak,
maximum : Bool,
) -> Int? {
let mut chosen : Int? = None
for index in 0.. chosen = Some(index)
Some(current) =>
if (maximum && sizes[index] > sizes[current]) ||
(!maximum && sizes[index] < sizes[current]) ||
(sizes[index] == sizes[current] && tie_break == Last) {
chosen = Some(index)
}
}
}
chosen.filter(index => {
sizes[index] != 0 || minimums.get(index).unwrap_or(0) < 0
})
}
///|
pub impl ResizeSelector for PriorityMin with fn select(self, minimums, sizes) {
select_extreme(minimums, sizes, self.tie_break, false)
}
///|
pub impl ResizeSelector for PriorityMax with fn select(self, minimums, sizes) {
select_extreme(minimums, sizes, self.tie_break, true)
}
///|
pub fn Priority::round_robin() -> PriorityRoundRobin {
PriorityRoundRobin::new()
}
///|
pub fn Priority::first() -> PriorityFirst {
PriorityFirst::new()
}
///|
pub fn Priority::last() -> PriorityLast {
PriorityLast::new()
}
///|
pub fn Priority::min(tie_break~ : TieBreak) -> PriorityMin {
PriorityMin::new(tie_break~)
}
///|
pub fn Priority::max(tie_break~ : TieBreak) -> PriorityMax {
PriorityMax::new(tie_break~)
}
///|
pub extend TieBreak with Eq::{not_equal, equal}
///|
pub extend TieBreak with @debug.Debug::{to_repr}
///|
pub extend PriorityRoundRobin with Eq::{not_equal, equal}
///|
pub extend PriorityRoundRobin with @debug.Debug::{to_repr}
///|
pub extend PriorityRoundRobin with ResizeSelector::{select, fresh}
///|
pub extend PriorityFirst with Eq::{not_equal, equal}
///|
pub extend PriorityFirst with @debug.Debug::{to_repr}
///|
pub extend PriorityFirst with ResizeSelector::{select, fresh}
///|
pub extend PriorityLast with Eq::{not_equal, equal}
///|
pub extend PriorityLast with @debug.Debug::{to_repr}
///|
pub extend PriorityLast with ResizeSelector::{select, fresh}
///|
pub extend PriorityMin with Eq::{not_equal, equal}
///|
pub extend PriorityMin with @debug.Debug::{to_repr}
///|
pub extend PriorityMax with Eq::{not_equal, equal}
///|
pub extend PriorityMax with @debug.Debug::{to_repr}
///|
pub extend PriorityMin with ResizeSelector::{select, fresh}
///|
pub extend PriorityMax with ResizeSelector::{select, fresh}