use crate::geometry::*;
use crate::widget::*;
pub trait AnchorPlacementEnabled
where
Self: Sized,
{
fn place(self) -> PlacedWidget<Self> {
PlacedWidget {
content: self,
placement: AnchorPlacement::default(),
}
}
}
impl<T: AnchorPlacementEnabled> AnchorPlaced for T {
type Placed = PlacedWidget<Self>;
fn left(self, offset: impl Into<Option<u16>>) -> Self::Placed {
self.place().left(offset)
}
fn top(self, offset: impl Into<Option<u16>>) -> Self::Placed {
self.place().top(offset)
}
fn right(self, offset: impl Into<Option<u16>>) -> Self::Placed {
self.place().right(offset)
}
fn bottom(self, offset: impl Into<Option<u16>>) -> Self::Placed {
self.place().bottom(offset)
}
fn height(self, size: impl Into<Option<u16>>) -> Self::Placed {
self.place().height(size)
}
fn width(self, size: impl Into<Option<u16>>) -> Self::Placed {
self.place().width(size)
}
fn fill(self) -> Self::Placed {
self.place().fill()
}
}
#[derive(Default)]
pub struct PlacedWidget<W> {
pub content: W,
pub placement: AnchorPlacement,
}
impl<W> PlacedWidget<W> {
pub fn new(content: W) -> PlacedWidget<W> {
Self {
content,
placement: AnchorPlacement::default(),
}
}
}
impl<'a, M, W, A: AppEvent> Widget<M, A> for PlacedWidget<W>
where
W: Widget<M, A>,
{
fn update(
&mut self,
model: &mut M,
input: &Event<A>,
screen: &mut Screen,
painter: &Painter,
) -> Window {
let scope = self.placement.place(painter.scope());
let painter = painter.with_scope(scope);
let child_scope = self.content.update_asserted(model, input, screen, &painter);
let shift = point(
child_scope.col.saturating_sub(scope.col),
child_scope.row.saturating_sub(scope.row),
);
let shrink = point(
scope
.width
.saturating_sub(child_scope.width)
.saturating_add(shift.col),
scope
.height
.saturating_sub(child_scope.height)
.saturating_add(shift.row),
);
window(
painter.scope().position() + shift,
painter.scope().size() - shrink,
)
}
}
impl<W> AnchorPlaced for PlacedWidget<W> {
type Placed = Self;
fn left(mut self, offset: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.left(offset);
self
}
fn right(mut self, offset: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.right(offset);
self
}
fn top(mut self, offset: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.top(offset);
self
}
fn bottom(mut self, offset: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.bottom(offset);
self
}
fn width(mut self, size: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.width(size);
self
}
fn height(mut self, size: impl Into<Option<u16>>) -> Self {
self.placement = self.placement.height(size);
self
}
fn fill(mut self) -> Self {
self.placement = self.placement.fill();
self
}
}
pub trait Placement {
fn place(&self, window: Window) -> Window;
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Anchor;
impl Placement for Anchor {
fn place(&self, window: Window) -> Window {
window
}
}
pub trait AnchorPlaced {
type Placed;
fn left(self, offset: impl Into<Option<u16>>) -> Self::Placed;
fn top(self, offset: impl Into<Option<u16>>) -> Self::Placed;
fn right(self, offset: impl Into<Option<u16>>) -> Self::Placed;
fn bottom(self, offset: impl Into<Option<u16>>) -> Self::Placed;
fn height(self, size: impl Into<Option<u16>>) -> Self::Placed;
fn width(self, size: impl Into<Option<u16>>) -> Self::Placed;
fn fill(self) -> Self::Placed;
}
impl AnchorPlaced for Anchor {
type Placed = AnchorPlacement;
fn left(self, offset: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().left(offset)
}
fn right(self, offset: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().right(offset)
}
fn top(self, offset: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().top(offset)
}
fn bottom(self, offset: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().bottom(offset)
}
fn width(self, size: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().width(size)
}
fn height(self, size: impl Into<Option<u16>>) -> Self::Placed {
AnchorPlacement::default().height(size)
}
fn fill(self) -> Self::Placed {
AnchorPlacement::default()
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct AnchorPlacement {
pub left: Option<u16>,
pub top: Option<u16>,
pub right: Option<u16>,
pub bottom: Option<u16>,
pub width: Option<u16>,
pub height: Option<u16>,
}
impl AnchorPlaced for AnchorPlacement {
type Placed = Self;
fn left(mut self, offset: impl Into<Option<u16>>) -> Self {
self.left = offset.into();
self
}
fn right(mut self, offset: impl Into<Option<u16>>) -> Self {
self.right = offset.into();
self
}
fn top(mut self, offset: impl Into<Option<u16>>) -> Self {
self.top = offset.into();
self
}
fn bottom(mut self, offset: impl Into<Option<u16>>) -> Self {
self.bottom = offset.into();
self
}
fn width(mut self, size: impl Into<Option<u16>>) -> Self {
self.width = size.into();
self
}
fn height(mut self, size: impl Into<Option<u16>>) -> Self {
self.height = size.into();
self
}
fn fill(self) -> Self::Placed {
AnchorPlacement::default()
}
}
impl Placement for AnchorPlacement {
fn place(&self, w: Window) -> Window {
let s = w.size();
let edges = |scope: u16, start: Option<u16>, size: Option<u16>, end: Option<u16>| match (
start, size, end,
) {
(None, None, None) => (0, scope),
(None, None, Some(offset_end)) => (0, scope.saturating_sub(offset_end)),
(Some(offset_start), None, None) => (
offset_start.clamp(0, scope),
scope.saturating_sub(offset_start),
),
(None, Some(fixed_size), None) => (
scope.saturating_sub(fixed_size) / 2,
fixed_size.clamp(0, scope),
),
(None, Some(fixed_size), Some(offset_end)) => (
scope.saturating_sub(fixed_size).saturating_sub(offset_end),
fixed_size.clamp(0, scope.saturating_sub(offset_end)),
),
(Some(offset_start), Some(fixed_size), None) => (
offset_start.clamp(0, scope),
fixed_size.clamp(0, scope.saturating_sub(offset_start)),
),
(Some(offset_start), None, Some(offset_end)) => (
offset_start.clamp(0, scope),
scope
.saturating_sub(offset_start)
.saturating_sub(offset_end),
),
(Some(offset_start), Some(fixed_size), Some(offset_end)) => {
let space = scope
.saturating_sub(offset_start)
.saturating_sub(offset_end);
(
(offset_start.saturating_add(space.saturating_sub(fixed_size) / 2))
.clamp(0, scope),
fixed_size.clamp(0, space),
)
}
};
let x = edges(s.col, self.left, self.width, self.right);
let y = edges(s.row, self.top, self.height, self.bottom);
let placement = window(point(x.0, y.0), point(x.1, y.1));
w.relative_crop(&placement)
}
}
#[test]
fn test_anchor_placement() {
let sut = AnchorPlacement::default().top(0).height(1);
let w = sut.place(window(point(0, 0), point(5, 5)));
assert_eq!(w, window(point(0, 0), point(5, 1)));
let sut = AnchorPlacement::default().height(1).bottom(0);
let w = sut.place(window(point(0, 0), point(5, 5)));
assert_eq!(w, window(point(0, 4), point(5, 1)));
let sut = AnchorPlacement::default().top(1).bottom(1);
let w = sut.place(window(point(2, 2), point(5, 5)));
assert_eq!(w, window(point(2, 3), point(5, 3)));
}