Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass AlignHints to set_rect, not size_rules #447

Merged
merged 2 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions crates/kas-core/src/core/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::event::ConfigCx;
use crate::geom::{Coord, Offset, Rect};
use crate::layout::{AxisInfo, SizeRules};
use crate::layout::{AlignHints, AxisInfo, SizeRules};
use crate::theme::{DrawCx, SizeCx};
use crate::util::IdentifyWidget;
use crate::{HasId, Id};
Expand Down Expand Up @@ -168,12 +168,10 @@ pub trait Layout {
/// outside of its assigned `rect` and to not function as normal.
///
/// The assigned `rect` may be larger than the widget's size requirements,
/// regardless of the [`Stretch`] policy used. If the widget should never
/// stretch, it must align itself.
/// Example: the `CheckBox` widget uses an [`AlignPair`] (set from
/// `size_rules`'s [`AxisInfo`]) and uses [`ConfigCx::align_feature`].
/// Another example: `Label` uses a `Text` object which handles alignment
/// internally.
/// regardless of the [`Stretch`] policy used: containers divide up space
/// based on children's [`SizeRules`] but do not attempt to align content
/// when excess space is available. Instead, content is responsible for
/// aligning itself using the provided `hints` and/or local information.
///
/// Required: [`Self::size_rules`] is called for both axes before this
/// method is called, and that this method has been called *after* the last
Expand All @@ -184,8 +182,8 @@ pub trait Layout {
/// field of `widget_core!()` to the input `rect`.
///
/// [`Stretch`]: crate::layout::Stretch
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
let _ = (cx, rect);
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
let _ = (cx, rect, hints);
unimplemented!() // make rustdoc show that this is a provided method
}

Expand Down
12 changes: 6 additions & 6 deletions crates/kas-core/src/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use super::Widget;
use crate::event::{ConfigCx, Event, EventCx, IsUsed};
use crate::geom::{Coord, Rect};
use crate::layout::{AxisInfo, SizeRules};
use crate::layout::{AlignHints, AxisInfo, SizeRules};
use crate::theme::{DrawCx, SizeCx};
use crate::{Id, Layout, NavAdvance};

Expand All @@ -25,7 +25,7 @@ trait NodeT {
fn for_child_node(&mut self, index: usize, f: Box<dyn FnOnce(Node<'_>) + '_>);

fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect);
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);

fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize>;
fn find_id(&mut self, coord: Coord) -> Option<Id>;
Expand Down Expand Up @@ -73,8 +73,8 @@ impl<'a, T> NodeT for (&'a mut dyn Widget<Data = T>, &'a T) {
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
self.0.size_rules(sizer, axis)
}
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
self.0.set_rect(cx, rect);
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.0.set_rect(cx, rect, hints);
}

fn nav_next(&self, reverse: bool, from: Option<usize>) -> Option<usize> {
Expand Down Expand Up @@ -293,8 +293,8 @@ impl<'a> Node<'a> {
}

/// Set size and position
pub(crate) fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
self.0.set_rect(cx, rect);
pub(crate) fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.0.set_rect(cx, rect, hints);
}

/// Navigation in spatial order
Expand Down
8 changes: 4 additions & 4 deletions crates/kas-core/src/decorations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ impl_scope! {
}

impl Layout for Self {
fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
axis.set_default_align_hv(Align::Center, Align::Center);
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
sizer.text_rules(&mut self.text, axis)
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.core.rect = rect;
cx.text_set_size(&mut self.text, rect.size);
let align = hints.complete(Align::Center, Align::Center);
cx.text_set_size(&mut self.text, rect.size, align);
}

fn draw(&mut self, mut draw: DrawCx) {
Expand Down
8 changes: 7 additions & 1 deletion crates/kas-core/src/event/cx/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,13 @@ impl<'a> ConfigCx<'a> {
///
/// Call [`text_configure`][Self::text_configure] before this method.
#[inline]
pub fn text_set_size<T: FormattableText>(&self, text: &mut Text<T>, size: Size) {
pub fn text_set_size<T: FormattableText>(
&self,
text: &mut Text<T>,
size: Size,
align: AlignPair,
) {
text.set_align(align.into());
text.set_bounds(size.cast());
text.prepare().expect("not configured");
}
Expand Down
14 changes: 7 additions & 7 deletions crates/kas-core/src/hidden.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use crate::classes::HasStr;
use crate::event::{ConfigCx, Event, EventCx, IsUsed};
use crate::geom::{Coord, Offset, Rect};
use crate::layout::{Align, AxisInfo, SizeRules};
use crate::layout::{Align, AlignHints, AxisInfo, SizeRules};
use crate::theme::{DrawCx, SizeCx, Text, TextClass};
use crate::{Events, Id, Layout, NavAdvance, Node, Widget};
use kas_macros::{autoimpl, impl_scope};
Expand Down Expand Up @@ -45,14 +45,14 @@ impl_scope! {

impl Layout for Self {
#[inline]
fn size_rules(&mut self, sizer: SizeCx, mut axis: AxisInfo) -> SizeRules {
axis.set_default_align_hv(Align::Default, Align::Center);
fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
sizer.text_rules(&mut self.text, axis)
}

fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.core.rect = rect;
cx.text_set_size(&mut self.text, rect.size);
let align = hints.complete(Align::Default, Align::Center);
cx.text_set_size(&mut self.text, rect.size, align);
}

fn draw(&mut self, mut draw: DrawCx) {
Expand Down Expand Up @@ -135,8 +135,8 @@ impl_scope! {
}

#[inline]
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect) {
self.inner.set_rect(cx, rect);
fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) {
self.inner.set_rect(cx, rect, hints);
}

#[inline]
Expand Down
23 changes: 20 additions & 3 deletions crates/kas-core/src/layout/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,20 @@ impl AlignHints {
(self.horiz.unwrap_or(horiz), self.vert.unwrap_or(vert))
}

/// Complete via default alignments
/// Complete, using specified alignments as defaults
pub fn complete(&self, horiz: Align, vert: Align) -> AlignPair {
AlignPair::new(self.horiz.unwrap_or(horiz), self.vert.unwrap_or(vert))
}

/// Complete, using [`Align::Default`] as defaults
pub fn complete_default(&self) -> AlignPair {
self.complete(Align::Default, Align::Default)
}

/// Complete, using [`Align::Center`] as defaults
pub fn complete_center(&self) -> AlignPair {
self.complete(Align::Center, Align::Center)
}
}

/// Provides alignment information on both axes along with ideal size
Expand Down Expand Up @@ -180,10 +190,17 @@ impl AlignPair {
}
}

impl From<(Align, Align)> for AlignHints {
#[inline]
fn from((h, v): (Align, Align)) -> Self {
AlignHints::new(Some(h), Some(v))
}
}

impl From<(Align, Align)> for AlignPair {
#[inline]
fn from(p: (Align, Align)) -> Self {
AlignPair::new(p.0, p.1)
fn from((h, v): (Align, Align)) -> Self {
AlignPair::new(h, v)
}
}

Expand Down
66 changes: 1 addition & 65 deletions crates/kas-core/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,21 @@ pub struct AxisInfo {
vertical: bool,
has_fixed: bool,
other_axis: i32,
align: Option<Align>,
}

impl AxisInfo {
/// Construct with direction and an optional value for the other axis
///
/// This method is *usually* not required by user code.
#[inline]
pub fn new(vertical: bool, fixed: Option<i32>, align: Option<Align>) -> Self {
pub fn new(vertical: bool, fixed: Option<i32>) -> Self {
AxisInfo {
vertical,
has_fixed: fixed.is_some(),
other_axis: fixed.unwrap_or(0),
align,
}
}

/// Construct a copy using the given alignment hints
#[inline]
pub fn with_align_hints(mut self, hints: AlignHints) -> Self {
self.align = hints.extract(self).or(self.align);
self
}

/// True if the current axis is vertical
#[inline]
pub fn is_vertical(self) -> bool {
Expand All @@ -103,61 +94,6 @@ impl AxisInfo {
!self.vertical
}

/// Get align parameter
#[inline]
pub fn align(self) -> Option<Align> {
self.align
}

/// Set align parameter
#[inline]
pub fn set_align(&mut self, align: Option<Align>) {
self.align = align;
}

/// Set default alignment
///
/// If the optional alignment parameter is `None`, replace with `align`.
#[inline]
pub fn set_default_align(&mut self, align: Align) {
if self.align.is_none() {
self.align = Some(align);
}
}

/// Set default alignment
///
/// If the optional alignment parameter is `None`, replace with either
/// `horiz` or `vert` depending on this axis' orientation.
#[inline]
pub fn set_default_align_hv(&mut self, horiz: Align, vert: Align) {
if self.align.is_none() {
if self.is_horizontal() {
self.align = Some(horiz);
} else {
self.align = Some(vert);
}
}
}

/// Get align parameter, defaulting to [`Align::Default`]
#[inline]
pub fn align_or_default(self) -> Align {
self.align.unwrap_or(Align::Default)
}

/// Get align parameter, defaulting to [`Align::Center`]
#[inline]
pub fn align_or_center(self) -> Align {
self.align.unwrap_or(Align::Center)
}

/// Get align parameter, defaulting to [`Align::Stretch`]
#[inline]
pub fn align_or_stretch(self) -> Align {
self.align.unwrap_or(Align::Stretch)
}

/// Size of other axis, if fixed
#[inline]
pub fn other(&self) -> Option<i32> {
Expand Down
7 changes: 2 additions & 5 deletions crates/kas-core/src/layout/size_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,6 @@ impl_scope! {
///
/// If is `None`, max size is limited to ideal size.
pub stretch: Stretch,
/// Alignment (set by `Self::size_rules`)
align: AlignPair,
}
}

Expand All @@ -256,14 +254,13 @@ impl PixmapScaling {
.size
.to_physical(scale_factor * self.ideal_factor)
.extract(axis);
self.align.set_component(axis, axis.align_or_center());
SizeRules::new(min, ideal, margins, self.stretch)
}

/// Constrains and aligns within `rect`
///
/// The resulting size is then aligned using the `align` hints, defaulting to centered.
pub fn align_rect(&mut self, rect: Rect, scale_factor: f32) -> Rect {
pub fn align_rect(&mut self, rect: Rect, align: AlignPair, scale_factor: f32) -> Rect {
let mut size = rect.size;

if self.stretch == Stretch::None {
Expand All @@ -283,7 +280,7 @@ impl PixmapScaling {
}
}

self.align.aligned_rect(size, rect)
align.aligned_rect(size, rect)
}
}

Expand Down
19 changes: 9 additions & 10 deletions crates/kas-core/src/layout/sizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

//! Layout solver

use super::{Align, AxisInfo, Margins, SizeRules};
use super::{AxisInfo, Margins, SizeRules};
use crate::cast::Conv;
use crate::event::ConfigCx;
use crate::geom::{Rect, Size};
use crate::layout::AlignHints;
use crate::theme::SizeCx;
use crate::util::WidgetHierarchy;
use crate::{Layout, Node};
Expand Down Expand Up @@ -76,11 +77,9 @@ pub fn solve_size_rules<W: Layout + ?Sized>(
sizer: SizeCx,
x_size: Option<i32>,
y_size: Option<i32>,
h_align: Option<Align>,
v_align: Option<Align>,
) {
widget.size_rules(sizer.re(), AxisInfo::new(false, y_size, h_align));
widget.size_rules(sizer.re(), AxisInfo::new(true, x_size, v_align));
widget.size_rules(sizer.re(), AxisInfo::new(false, y_size));
widget.size_rules(sizer.re(), AxisInfo::new(true, x_size));
}

/// Size solver
Expand Down Expand Up @@ -137,8 +136,8 @@ impl SolveCache {
pub fn find_constraints(mut widget: Node<'_>, sizer: SizeCx) -> Self {
let start = std::time::Instant::now();

let w = widget.size_rules(sizer.re(), AxisInfo::new(false, None, None));
let h = widget.size_rules(sizer.re(), AxisInfo::new(true, Some(w.ideal_size()), None));
let w = widget.size_rules(sizer.re(), AxisInfo::new(false, None));
let h = widget.size_rules(sizer.re(), AxisInfo::new(true, Some(w.ideal_size())));

let min = Size(w.min_size(), h.min_size());
let ideal = Size(w.ideal_size(), h.ideal_size());
Expand Down Expand Up @@ -197,14 +196,14 @@ impl SolveCache {
// internal layout solving.
if self.refresh_rules || width != self.last_width {
if self.refresh_rules {
let w = widget.size_rules(cx.size_cx(), AxisInfo::new(false, None, None));
let w = widget.size_rules(cx.size_cx(), AxisInfo::new(false, None));
self.min.0 = w.min_size();
self.ideal.0 = w.ideal_size();
self.margins.horiz = w.margins();
width = rect.size.0 - self.margins.sum_horiz();
}

let h = widget.size_rules(cx.size_cx(), AxisInfo::new(true, Some(width), None));
let h = widget.size_rules(cx.size_cx(), AxisInfo::new(true, Some(width)));
self.min.1 = h.min_size();
self.ideal.1 = h.ideal_size();
self.margins.vert = h.margins();
Expand All @@ -216,7 +215,7 @@ impl SolveCache {
rect.size.0 = width;
rect.size.1 -= self.margins.sum_vert();
}
widget.set_rect(cx, rect);
widget.set_rect(cx, rect, AlignHints::NONE);

log::trace!(target: "kas_perf::layout", "apply_rect: {}μs", start.elapsed().as_micros());
self.refresh_rules = false;
Expand Down
Loading