use log::warn;
use std::ops::{Index, IndexMut};
use super::DragHandle;
use kas::layout::{RulesSetter, RulesSolver};
use kas::prelude::*;
pub type RowSplitter<W> = Splitter<kas::Right, W>;
pub type ColumnSplitter<W> = Splitter<kas::Down, W>;
pub type BoxRowSplitter<M> = BoxSplitter<kas::Right, M>;
pub type BoxColumnSplitter<M> = BoxSplitter<kas::Down, M>;
pub type BoxSplitter<D, M> = Splitter<D, Box<dyn Widget<Msg = M>>>;
pub type RefRowSplitter<'a, M> = RefSplitter<'a, kas::Right, M>;
pub type RefColumnSplitter<'a, M> = RefSplitter<'a, kas::Down, M>;
pub type RefSplitter<'a, D, M> = Splitter<D, &'a mut dyn Widget<Msg = M>>;
#[handler(send=noauto, msg=<W as event::Handler>::Msg)]
#[widget(children=noauto)]
#[derive(Clone, Default, Debug, Widget)]
pub struct Splitter<D: Directional, W: Widget> {
#[widget_core]
core: CoreData,
widgets: Vec<W>,
handles: Vec<DragHandle>,
handle_size: Size,
data: layout::DynRowStorage,
direction: D,
}
impl<D: Directional, W: Widget> WidgetChildren for Splitter<D, W> {
#[inline]
fn len(&self) -> usize {
self.widgets.len() + self.handles.len()
}
#[inline]
fn get(&self, index: usize) -> Option<&dyn WidgetConfig> {
if (index & 1) != 0 {
self.handles.get(index >> 1).map(|w| w.as_widget())
} else {
self.widgets.get(index >> 1).map(|w| w.as_widget())
}
}
#[inline]
fn get_mut(&mut self, index: usize) -> Option<&mut dyn WidgetConfig> {
if (index & 1) != 0 {
self.handles.get_mut(index >> 1).map(|w| w.as_widget_mut())
} else {
self.widgets.get_mut(index >> 1).map(|w| w.as_widget_mut())
}
}
}
impl<D: Directional, W: Widget> Layout for Splitter<D, W> {
fn size_rules(&mut self, size_handle: &mut dyn SizeHandle, axis: AxisInfo) -> SizeRules {
if self.widgets.len() == 0 {
return SizeRules::EMPTY;
}
assert!(self.handles.len() + 1 == self.widgets.len());
self.handle_size = size_handle.frame();
let handle_size = axis.extract_size(self.handle_size);
let dim = (self.direction, WidgetChildren::len(self));
let mut solver = layout::RowSolver::new(axis, dim, &mut self.data);
let mut n = 0;
loop {
assert!(n < self.widgets.len());
let widgets = &mut self.widgets;
solver.for_child(&mut self.data, n << 1, |axis| {
widgets[n].size_rules(size_handle, axis)
});
if n >= self.handles.len() {
break;
}
solver.for_child(&mut self.data, (n << 1) + 1, |_axis| {
SizeRules::fixed(handle_size, (0, 0))
});
n += 1;
}
solver.finish(&mut self.data)
}
fn set_rect(&mut self, rect: Rect, align: AlignHints) {
self.core.rect = rect;
if self.widgets.len() == 0 {
return;
}
assert!(self.handles.len() + 1 == self.widgets.len());
if self.direction.is_horizontal() {
self.handle_size.1 = rect.size.1;
} else {
self.handle_size.0 = rect.size.0;
}
let dim = (self.direction, WidgetChildren::len(self));
let is_horiz = dim.0.is_horizontal();
let aa = if is_horiz { align.horiz } else { align.vert };
if aa.unwrap_or(Align::Stretch) != Align::Stretch {
warn!("Splitter: found alignment != Stretch");
}
let mut setter = layout::RowSetter::<D, Vec<u32>, _>::new(rect, dim, align, &mut self.data);
let mut n = 0;
loop {
assert!(n < self.widgets.len());
let align = AlignHints::default();
self.widgets[n].set_rect(setter.child_rect(&mut self.data, n << 1), align);
if n >= self.handles.len() {
break;
}
let index = (n << 1) + 1;
let track = setter.maximal_rect_of(&mut self.data, index);
self.handles[n].set_rect(track, AlignHints::default());
let handle = setter.child_rect(&mut self.data, index);
let _ = self.handles[n].set_size_and_offset(handle.size, handle.pos - track.pos);
n += 1;
}
}
fn find_id(&self, coord: Coord) -> Option<WidgetId> {
if !self.rect().contains(coord) {
return None;
}
let solver = layout::RowPositionSolver::new(self.direction);
if let Some(child) = solver.find_child(&self.widgets, coord) {
return child.find_id(coord).or(Some(self.id()));
}
let solver = layout::RowPositionSolver::new(self.direction);
if let Some(child) = solver.find_child(&self.handles, coord) {
return child.find_id(coord).or(Some(self.id()));
}
Some(self.id())
}
fn draw(&self, draw_handle: &mut dyn DrawHandle, mgr: &event::ManagerState, disabled: bool) {
let solver = layout::RowPositionSolver::new(self.direction);
let disabled = disabled || self.is_disabled();
solver.for_children(&self.widgets, draw_handle.target_rect(), |w| {
w.draw(draw_handle, mgr, disabled)
});
let solver = layout::RowPositionSolver::new(self.direction);
solver.for_children(&self.handles, draw_handle.target_rect(), |w| {
draw_handle.separator(w.rect())
});
}
}
impl<D: Directional, W: Widget> event::SendEvent for Splitter<D, W> {
fn send(&mut self, mgr: &mut Manager, id: WidgetId, event: Event) -> Response<Self::Msg> {
if !self.is_disabled() && self.widgets.len() > 0 {
assert!(self.handles.len() + 1 == self.widgets.len());
let mut n = 0;
loop {
assert!(n < self.widgets.len());
if id <= self.widgets[n].id() {
return self.widgets[n].send(mgr, id, event);
}
if n >= self.handles.len() {
break;
}
if id <= self.handles[n].id() {
return self.handles[n]
.send(mgr, id, event)
.try_into()
.unwrap_or_else(|_| {
self.adjust_size(n);
Response::None
});
}
n += 1;
}
}
Response::Unhandled(event)
}
}
impl<D: Directional + Default, W: Widget> Splitter<D, W> {
pub fn new(widgets: Vec<W>) -> Self {
let direction = D::default();
Self::new_with_direction(direction, widgets)
}
}
impl<D: Directional, W: Widget> Splitter<D, W> {
pub fn new_with_direction(direction: D, widgets: Vec<W>) -> Self {
let mut handles = Vec::new();
handles.resize_with(widgets.len().saturating_sub(1), || DragHandle::new());
Splitter {
core: Default::default(),
widgets,
handles,
handle_size: Size::ZERO,
data: Default::default(),
direction,
}
}
fn adjust_size(&mut self, n: usize) {
assert!(n < self.handles.len());
assert_eq!(self.widgets.len(), self.handles.len() + 1);
let index = 2 * n + 1;
let is_horiz = self.direction.is_horizontal();
let extract_p = |p: Coord| if is_horiz { p.0 } else { p.1 } as u32;
let extract_s = |s: Size| if is_horiz { s.0 } else { s.1 } as u32;
let hrect = self.handles[n].rect();
let width1 = extract_p(hrect.pos - self.core.rect.pos) as u32;
let width2 = extract_s(self.core.rect.size - hrect.size) - width1;
let dim = (self.direction, WidgetChildren::len(self));
let mut setter =
layout::RowSetter::<D, Vec<u32>, _>::new_unsolved(self.core.rect, dim, &mut self.data);
setter.solve_range(&mut self.data, 0..index, width1);
setter.solve_range(&mut self.data, (index + 1)..dim.1, width2);
setter.update_offsets(&mut self.data);
let mut n = 0;
loop {
assert!(n < self.widgets.len());
let align = AlignHints::default();
self.widgets[n].set_rect(setter.child_rect(&mut self.data, n << 1), align);
if n >= self.handles.len() {
break;
}
let index = (n << 1) + 1;
let track = self.handles[n].track();
self.handles[n].set_rect(track, AlignHints::default());
let handle = setter.child_rect(&mut self.data, index);
let _ = self.handles[n].set_size_and_offset(handle.size, handle.pos - track.pos);
n += 1;
}
}
pub fn is_empty(&self) -> bool {
self.widgets.is_empty()
}
pub fn len(&self) -> usize {
self.widgets.len()
}
pub fn capacity(&self) -> usize {
self.widgets.capacity()
}
pub fn reserve(&mut self, additional: usize) {
self.widgets.reserve(additional);
self.handles.reserve(additional);
}
pub fn clear(&mut self) -> TkAction {
let action = match self.widgets.is_empty() {
true => TkAction::None,
false => TkAction::Reconfigure,
};
self.widgets.clear();
self.handles.clear();
action
}
pub fn push(&mut self, widget: W) -> TkAction {
if !self.widgets.is_empty() {
self.handles.push(DragHandle::new());
}
self.widgets.push(widget);
TkAction::Reconfigure
}
pub fn pop(&mut self) -> (Option<W>, TkAction) {
let action = match self.widgets.is_empty() {
true => TkAction::None,
false => TkAction::Reconfigure,
};
let _ = self.handles.pop();
(self.widgets.pop(), action)
}
pub fn insert(&mut self, index: usize, widget: W) -> TkAction {
if !self.widgets.is_empty() {
self.handles.push(DragHandle::new());
}
self.widgets.insert(index, widget);
TkAction::Reconfigure
}
pub fn remove(&mut self, index: usize) -> (W, TkAction) {
let _ = self.handles.pop();
let r = self.widgets.remove(index);
(r, TkAction::Reconfigure)
}
pub fn replace(&mut self, index: usize, mut widget: W) -> (W, TkAction) {
std::mem::swap(&mut widget, &mut self.widgets[index]);
(widget, TkAction::Reconfigure)
}
pub fn extend<T: IntoIterator<Item = W>>(&mut self, iter: T) -> TkAction {
let len = self.widgets.len();
self.widgets.extend(iter);
self.handles
.resize_with(self.widgets.len().saturating_sub(1), || DragHandle::new());
match len == self.widgets.len() {
true => TkAction::None,
false => TkAction::Reconfigure,
}
}
pub fn resize_with<F: Fn(usize) -> W>(&mut self, len: usize, f: F) -> TkAction {
let l0 = self.widgets.len();
if l0 == len {
return TkAction::None;
} else if l0 > len {
self.widgets.truncate(len);
} else {
self.widgets.reserve(len);
for i in l0..len {
self.widgets.push(f(i));
}
}
self.handles
.resize_with(self.widgets.len().saturating_sub(1), || DragHandle::new());
TkAction::Reconfigure
}
pub fn retain<F: FnMut(&W) -> bool>(&mut self, f: F) -> TkAction {
let len = self.widgets.len();
self.widgets.retain(f);
self.handles
.resize_with(self.widgets.len().saturating_sub(1), || DragHandle::new());
match len == self.widgets.len() {
true => TkAction::None,
false => TkAction::Reconfigure,
}
}
}
impl<D: Directional, W: Widget> Index<usize> for Splitter<D, W> {
type Output = W;
fn index(&self, index: usize) -> &Self::Output {
&self.widgets[index]
}
}
impl<D: Directional, W: Widget> IndexMut<usize> for Splitter<D, W> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.widgets[index]
}
}