Skip to content

Commit

Permalink
Merge pull request #350 from kas-gui/work
Browse files Browse the repository at this point in the history
Provide align hints to size_rules
  • Loading branch information
dhardy committed Aug 17, 2022
2 parents 3c914fa + 422845d commit 963a36b
Show file tree
Hide file tree
Showing 46 changed files with 562 additions and 429 deletions.
27 changes: 13 additions & 14 deletions crates/kas-core/src/core/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::fmt;

use crate::event::{ConfigMgr, Event, EventMgr, Response, Scroll};
use crate::geom::{Coord, Offset, Rect};
use crate::layout::{AlignHints, AxisInfo, SizeRules};
use crate::layout::{AxisInfo, SizeRules};
use crate::theme::{DrawMgr, SizeMgr};
use crate::util::IdentifyWidget;
use crate::WidgetId;
Expand All @@ -18,7 +18,7 @@ use kas_macros::autoimpl;
#[allow(unused)]
use crate::event::EventState;
#[allow(unused)]
use crate::layout::{self, AutoLayout};
use crate::layout::{self, AlignPair, AutoLayout};
#[allow(unused)]
use crate::TkAction;
#[allow(unused)]
Expand Down Expand Up @@ -187,13 +187,12 @@ 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. The [`AlignHints`] should be
/// used to align content such as text within this space, and also content
/// such as a button (which could, but does not need to, stretch).
///
/// The [`AlignHints`] are usually passed down to children, though there are
/// some exceptions: a `Button` always centers content; a `ScrollRegion`
/// isolates the inside from outside influence over layout.
/// 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 [`ConfigMgr::align_feature`].
/// Another example: `Label` uses a `Text` object which handles alignment
/// internally.
///
/// Default implementation:
///
Expand All @@ -205,7 +204,7 @@ pub trait Layout {
/// is used, also calls `<Self as AutoLayout>::set_rect`.
///
/// [`Stretch`]: crate::layout::Stretch
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect, align: AlignHints);
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect);

/// Translate a coordinate to a [`WidgetId`]
///
Expand Down Expand Up @@ -348,14 +347,14 @@ pub trait Layout {
/// }
///
/// impl Layout for Self {
/// fn size_rules(&mut self, size_mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
/// fn size_rules(&mut self, size_mgr: SizeMgr, mut axis: AxisInfo) -> SizeRules {
/// axis.set_default_align_hv(Align::Default, Align::Center);
/// size_mgr.text_rules(&mut self.label, self.class, axis)
/// }
///
/// fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect, align: AlignHints) {
/// fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
/// self.core.rect = rect;
/// let align = align.unwrap_or(Align::Default, Align::Center);
/// mgr.text_set_size(&mut self.label, self.class, rect.size, align);
/// mgr.text_set_size(&mut self.label, self.class, rect.size, None);
/// }
///
/// fn draw(&mut self, mut draw: DrawMgr) {
Expand Down
15 changes: 9 additions & 6 deletions crates/kas-core/src/event/manager/config_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::Pending;
use crate::draw::DrawShared;
use crate::event::EventState;
use crate::geom::{Rect, Size};
use crate::layout::{Align, AlignHints};
use crate::layout::AlignPair;
use crate::text::TextApi;
use crate::theme::{Feature, SizeMgr, TextClass, ThemeSize};
use crate::{TkAction, Widget, WidgetExt, WidgetId};
Expand Down Expand Up @@ -103,21 +103,24 @@ impl<'a> ConfigMgr<'a> {
/// In case the input `rect` is larger than desired on either axis, it is
/// reduced in size and offset within the original `rect` as is preferred.
#[inline]
pub fn align_feature(&self, feature: Feature, rect: Rect, hints: AlignHints) -> Rect {
self.sh.align_feature(feature, rect, hints)
pub fn align_feature(&self, feature: Feature, rect: Rect, align: AlignPair) -> Rect {
self.sh.align_feature(feature, rect, align)
}

/// Prepare a text object
///
/// This sets the text's font, font size, wrapping and alignment and
/// performs text preparation necessary before display.
/// This sets the text's font, font size, wrapping and optionally alignment,
/// then performs the text preparation necessary before display.
///
/// Note: setting alignment here is not necessary when the default alignment
/// is desired or when [`SizeMgr::text_rules`] is used.
#[inline]
pub fn text_set_size(
&self,
text: &mut dyn TextApi,
class: TextClass,
size: Size,
align: (Align, Align),
align: Option<AlignPair>,
) {
self.sh.text_set_size(text, class, size, align)
}
Expand Down
30 changes: 8 additions & 22 deletions crates/kas-core/src/geom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ impl Conv<Coord> for kas_text::Vec2 {
/// and implementations of subtraction will check this, but only in debug mode
/// (similar to overflow checks on integers).
///
/// Subtraction is defined to be saturating subtraction.
///
/// This may be converted to [`Offset`] with `from` / `into`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand All @@ -294,14 +296,6 @@ impl Size {
Self(n, n)
}

/// Subtraction, clamping the result to 0 or greater
#[inline]
#[must_use = "method does not modify self but returns a new value"]
pub fn clamped_sub(self, rhs: Self) -> Self {
// This impl should aid vectorisation. We avoid Sub impl because of its check.
Size(self.0 - rhs.0, self.1 - rhs.1).max(Size::ZERO)
}

/// Scale to fit within the target size, keeping aspect ratio
///
/// If either dimension of self is 0, this returns None.
Expand Down Expand Up @@ -338,31 +332,23 @@ impl std::ops::AddAssign for Size {

/// Subtract a `Size` from a `Size`
///
/// In debug mode this asserts that the result is non-negative.
/// This is saturating subtraction: `Size::ZERO - Size::splat(6) == Size::ZERO`.
impl std::ops::Sub for Size {
type Output = Self;

#[inline]
fn sub(self, rhs: Self) -> Self {
debug_assert!(
self.0 >= rhs.0 && self.1 >= rhs.1,
"Size::sub: expected lhs >= rhs"
);
Self(self.0 - rhs.0, self.1 - rhs.1)
// This impl should aid vectorisation.
Size(self.0 - rhs.0, self.1 - rhs.1).max(Size::ZERO)
}
}
/// Subtract a `Size` from a `Size`
///
/// In debug mode this asserts that the result is non-negative.
/// This is saturating subtraction.
impl std::ops::SubAssign for Size {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
debug_assert!(
self.0 >= rhs.0 && self.1 >= rhs.1,
"Size::sub: expected lhs >= rhs"
);
self.0 -= rhs.0;
self.1 -= rhs.1;
*self = *self - rhs;
}
}

Expand Down Expand Up @@ -600,7 +586,7 @@ impl Rect {
#[must_use = "method does not modify self but returns a new value"]
pub fn shrink(&self, n: i32) -> Rect {
let pos = self.pos + Offset::splat(n);
let size = self.size.clamped_sub(Size::splat(n + n));
let size = self.size - Size::splat(n + n);
Rect { pos, size }
}

Expand Down
10 changes: 5 additions & 5 deletions crates/kas-core/src/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
use crate::class::HasStr;
use crate::event::ConfigMgr;
use crate::geom::Rect;
use crate::layout::{Align, AlignHints, AxisInfo, SizeRules};
use crate::layout::{Align, AxisInfo, SizeRules};
use crate::text::{Text, TextApi};
use crate::theme::{DrawMgr, SizeMgr, TextClass};
use crate::{Layout, WidgetCore};
Expand Down Expand Up @@ -46,14 +46,14 @@ impl_scope! {

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

fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect, align: AlignHints) {
fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect) {
self.core.rect = rect;
let align = align.unwrap_or(Align::Default, Align::Center);
mgr.text_set_size(&mut self.label, Self::CLASS, rect.size, align);
mgr.text_set_size(&mut self.label, Self::CLASS, rect.size, None);
}

fn draw(&mut self, mut draw: DrawMgr) {
Expand Down
92 changes: 77 additions & 15 deletions crates/kas-core/src/layout/align.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#[allow(unused)]
use super::Stretch; // for doc-links
use crate::dir::Directional;
use crate::geom::{Rect, Size};

pub use crate::text::Align;
Expand All @@ -29,7 +30,7 @@ pub use crate::text::Align;
/// .aligned_rect(pref_size, rect);
/// // self.core.rect = rect;
/// ```
#[derive(Copy, Clone, Debug, Default)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct AlignHints {
pub horiz: Option<Align>,
pub vert: Option<Align>,
Expand All @@ -50,6 +51,24 @@ impl AlignHints {
Self { horiz, vert }
}

/// Take horizontal/vertical component
#[inline]
pub fn extract(self, dir: impl Directional) -> Option<Align> {
match dir.is_vertical() {
false => self.horiz,
true => self.vert,
}
}

/// Set one component of self, based on a direction
#[inline]
pub fn set_component<D: Directional>(&mut self, dir: D, align: Option<Align>) {
match dir.is_vertical() {
false => self.horiz = align,
true => self.vert = align,
}
}

/// Combine two hints (first takes priority)
#[must_use = "method does not modify self but returns a new value"]
pub fn combine(self, rhs: AlignHints) -> Self {
Expand All @@ -65,38 +84,67 @@ impl AlignHints {
}

/// Complete via default alignments
pub fn complete(&self, horiz: Align, vert: Align) -> CompleteAlignment {
CompleteAlignment {
halign: self.horiz.unwrap_or(horiz),
valign: self.vert.unwrap_or(vert),
}
pub fn complete(&self, horiz: Align, vert: Align) -> AlignPair {
AlignPair::new(self.horiz.unwrap_or(horiz), self.vert.unwrap_or(vert))
}
}

/// Provides alignment information on both axes along with ideal size
///
/// Note that the `ideal` size detail is only used for non-stretch alignment.
#[derive(Copy, Clone, Debug)]
pub struct CompleteAlignment {
halign: Align,
valign: Align,
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct AlignPair {
pub horiz: Align,
pub vert: Align,
}

impl CompleteAlignment {
impl AlignPair {
/// Default on both axes
pub const DEFAULT: AlignPair = AlignPair::new(Align::Default, Align::Default);

/// Center on both axes
pub const CENTER: AlignPair = AlignPair::new(Align::Center, Align::Center);

/// Stretch on both axes
pub const STRETCH: AlignPair = AlignPair::new(Align::Stretch, Align::Stretch);

/// Construct with horiz. and vert. alignment
pub const fn new(horiz: Align, vert: Align) -> Self {
Self { horiz, vert }
}

/// Extract one component, based on a direction
#[inline]
pub fn extract<D: Directional>(self, dir: D) -> Align {
match dir.is_vertical() {
false => self.horiz,
true => self.vert,
}
}

/// Set one component of self, based on a direction
#[inline]
pub fn set_component<D: Directional>(&mut self, dir: D, align: Align) {
match dir.is_vertical() {
false => self.horiz = align,
true => self.vert = align,
}
}

/// Construct a rect of size `ideal` within `rect` using the given alignment
pub fn aligned_rect(&self, ideal: Size, rect: Rect) -> Rect {
let mut pos = rect.pos;
let mut size = rect.size;
if ideal.0 < size.0 && self.halign != Align::Stretch {
pos.0 += match self.halign {
if ideal.0 < size.0 && self.horiz != Align::Stretch {
pos.0 += match self.horiz {
Align::Center => (size.0 - ideal.0) / 2,
Align::BR => size.0 - ideal.0,
Align::Default | Align::TL | Align::Stretch => 0,
};
size.0 = ideal.0;
}
if ideal.1 < size.1 && self.valign != Align::Stretch {
pos.1 += match self.valign {
if ideal.1 < size.1 && self.vert != Align::Stretch {
pos.1 += match self.vert {
Align::Center => (size.1 - ideal.1) / 2,
Align::BR => size.1 - ideal.1,
Align::Default | Align::TL | Align::Stretch => 0,
Expand All @@ -106,3 +154,17 @@ impl CompleteAlignment {
Rect { pos, size }
}
}

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

impl From<AlignPair> for (Align, Align) {
#[inline]
fn from(p: AlignPair) -> Self {
(p.horiz, p.vert)
}
}
Loading

0 comments on commit 963a36b

Please sign in to comment.