mirror of
https://github.com/danbulant/cushy
synced 2026-06-15 12:31:11 +00:00
213 lines
6.6 KiB
Rust
213 lines
6.6 KiB
Rust
use std::fmt::Debug;
|
|
|
|
use figures::units::UPx;
|
|
use figures::{Fraction, IntoSigned, Point, Rect, ScreenScale, Size, Zero};
|
|
|
|
use crate::context::{AsEventContext, EventContext, LayoutContext};
|
|
use crate::styles::{Edges, FlexibleDimension};
|
|
use crate::value::{IntoValue, Value};
|
|
use crate::widget::{MakeWidget, RootBehavior, WidgetRef, WrappedLayout, WrapperWidget};
|
|
use crate::ConstraintLimit;
|
|
|
|
/// A widget aligns its contents to its container's boundaries.
|
|
#[derive(Debug)]
|
|
pub struct Align {
|
|
child: WidgetRef,
|
|
edges: Value<Edges<FlexibleDimension>>,
|
|
}
|
|
|
|
impl Align {
|
|
/// Returns a new spacing widget containing `widget`, surrounding it with
|
|
/// `margin`.
|
|
pub fn new(margin: impl IntoValue<Edges<FlexibleDimension>>, widget: impl MakeWidget) -> Self {
|
|
Self {
|
|
child: WidgetRef::new(widget),
|
|
edges: margin.into_value(),
|
|
}
|
|
}
|
|
|
|
/// Returns a new spacing widget that centers `widget` vertically and
|
|
/// horizontally.
|
|
pub fn centered(widget: impl MakeWidget) -> Self {
|
|
Self::new(FlexibleDimension::Auto, widget)
|
|
}
|
|
|
|
/// Sets the left edge of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn align_left(mut self) -> Self {
|
|
self.edges
|
|
.map_mut(|mut edges| edges.left = FlexibleDimension::ZERO);
|
|
self
|
|
}
|
|
|
|
/// Sets the top edge of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn align_top(mut self) -> Self {
|
|
self.edges
|
|
.map_mut(|mut edges| edges.top = FlexibleDimension::ZERO);
|
|
self
|
|
}
|
|
|
|
/// Sets the bottom edge of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn align_bottom(mut self) -> Self {
|
|
self.edges
|
|
.map_mut(|mut edges| edges.bottom = FlexibleDimension::ZERO);
|
|
self
|
|
}
|
|
|
|
/// Sets the right edge of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn align_right(mut self) -> Self {
|
|
self.edges
|
|
.map_mut(|mut edges| edges.right = FlexibleDimension::ZERO);
|
|
self
|
|
}
|
|
|
|
/// Sets the left and right edges of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn fit_horizontally(mut self) -> Self {
|
|
self.edges.map_mut(|mut edges| {
|
|
edges.left = FlexibleDimension::ZERO;
|
|
edges.right = FlexibleDimension::ZERO;
|
|
});
|
|
self
|
|
}
|
|
|
|
/// Sets the top and bottom edges of alignment to 0 and returns self.
|
|
#[must_use]
|
|
pub fn fit_vertically(mut self) -> Self {
|
|
self.edges.map_mut(|mut edges| {
|
|
edges.top = FlexibleDimension::ZERO;
|
|
edges.bottom = FlexibleDimension::ZERO;
|
|
});
|
|
self
|
|
}
|
|
|
|
fn measure(
|
|
&mut self,
|
|
available_space: Size<ConstraintLimit>,
|
|
context: &mut LayoutContext<'_, '_, '_, '_>,
|
|
) -> Layout {
|
|
let margin = self.edges.get();
|
|
let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom);
|
|
let horizontal = FrameInfo::new(context.gfx.scale(), margin.left, margin.right);
|
|
|
|
let content_available = Size::new(
|
|
horizontal.child_constraint(available_space.width),
|
|
vertical.child_constraint(available_space.height),
|
|
);
|
|
|
|
let child = self.child.mounted(&mut context.as_event_context());
|
|
let content_size = context.for_other(&child).layout(content_available);
|
|
|
|
let (left, right, width) = horizontal.measure(available_space.width, content_size.width);
|
|
let (top, bottom, height) = vertical.measure(available_space.height, content_size.height);
|
|
|
|
Layout {
|
|
margin: Edges {
|
|
left,
|
|
top,
|
|
right,
|
|
bottom,
|
|
},
|
|
content: Size::new(width, height),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FrameInfo {
|
|
a: Option<UPx>,
|
|
b: Option<UPx>,
|
|
}
|
|
|
|
impl FrameInfo {
|
|
fn new(scale: Fraction, a: FlexibleDimension, b: FlexibleDimension) -> Self {
|
|
let a = match a {
|
|
FlexibleDimension::Auto => None,
|
|
FlexibleDimension::Dimension(dimension) => Some(dimension.into_upx(scale)),
|
|
};
|
|
let b = match b {
|
|
FlexibleDimension::Auto => None,
|
|
FlexibleDimension::Dimension(dimension) => Some(dimension.into_upx(scale)),
|
|
};
|
|
Self { a, b }
|
|
}
|
|
|
|
fn child_constraint(&self, available: ConstraintLimit) -> ConstraintLimit {
|
|
match (self.a, self.b) {
|
|
(Some(a), Some(b)) => available - (a + b),
|
|
// If we have at least one auto-measurement, force the constraint
|
|
// into ClippedAfter mode to make the widget attempt to size the
|
|
// content to fit.
|
|
(Some(one), None) | (None, Some(one)) => {
|
|
ConstraintLimit::SizeToFit(available.max() - one)
|
|
}
|
|
(None, None) => ConstraintLimit::SizeToFit(available.max()),
|
|
}
|
|
}
|
|
|
|
fn measure(&self, available: ConstraintLimit, content: UPx) -> (UPx, UPx, UPx) {
|
|
match available {
|
|
ConstraintLimit::Fill(size) => {
|
|
let size = size.max(content);
|
|
let remaining = size.saturating_sub(content);
|
|
let (a, b) = match (self.a, self.b) {
|
|
(Some(a), Some(b)) => (a, b),
|
|
(Some(a), None) => (a, remaining - a),
|
|
(None, Some(b)) => (remaining - b, b),
|
|
(None, None) => {
|
|
let a = remaining / 2;
|
|
let b = remaining - a;
|
|
(a, b)
|
|
}
|
|
};
|
|
|
|
(a, b, size - a - b)
|
|
}
|
|
ConstraintLimit::SizeToFit(_) => (
|
|
self.a.unwrap_or_default(),
|
|
self.b.unwrap_or_default(),
|
|
content,
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl WrapperWidget for Align {
|
|
fn child_mut(&mut self) -> &mut WidgetRef {
|
|
&mut self.child
|
|
}
|
|
|
|
fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option<RootBehavior> {
|
|
Some(RootBehavior::Align)
|
|
}
|
|
|
|
fn layout_child(
|
|
&mut self,
|
|
available_space: Size<ConstraintLimit>,
|
|
context: &mut LayoutContext<'_, '_, '_, '_>,
|
|
) -> WrappedLayout {
|
|
let layout = self.measure(available_space, context);
|
|
|
|
WrappedLayout {
|
|
child: Rect::new(
|
|
Point::new(layout.margin.left, layout.margin.top).into_signed(),
|
|
layout.content.into_signed(),
|
|
),
|
|
size: layout.content + layout.margin.size(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AsMut<WidgetRef> for Align {
|
|
fn as_mut(&mut self) -> &mut WidgetRef {
|
|
&mut self.child
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct Layout {
|
|
margin: Edges<UPx>,
|
|
content: Size<UPx>,
|
|
}
|