Skip to content

Commit

Permalink
Merge pull request #348 from kas-gui/work
Browse files Browse the repository at this point in the history
New FrameRules model; adjust margins
  • Loading branch information
dhardy committed Aug 13, 2022
2 parents 6249117 + 9f8f679 commit 3c914fa
Show file tree
Hide file tree
Showing 22 changed files with 367 additions and 374 deletions.
2 changes: 0 additions & 2 deletions crates/kas-core/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
//!
//! [`AxisInfo`], [`Margins`] and [`Stretch`] are auxilliary data types.
//!
//! [`MarginSelector`] is a utility type facilitating user-selection of margins.
//!
//! ## Layout engines
//!
//! The [`RulesSolver`] and [`RulesSetter`] traits define interfaces for
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/layout/size_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use crate::theme::SizeMgr;
/// margin-merging behaviour, one cannot simply "add" two `SizeRules`. Instead,
/// when placing one widget next to another, use [`SizeRules::append`] or
/// [`SizeRules::appended`]; when placing a widget within a frame, use
/// [`FrameRules::surround_with_margin`] or [`FrameRules::surround_as_margin`].
/// [`FrameRules::surround`].
/// When calculating the size of a sequence of
/// widgets, one may use the [`Sum`] implementation (this assumes that the
/// sequence is in left-to-right or top-to-bottom order).
Expand Down
160 changes: 46 additions & 114 deletions crates/kas-core/src/layout/size_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use super::{Align, AlignHints, AxisInfo, SizeRules};
use crate::cast::*;
use crate::dir::Directional;
use crate::geom::{Rect, Size, Vec2};
use kas_macros::{impl_default, impl_scope};
use crate::theme::MarginStyle;
use kas_macros::impl_scope;

// for doc use
#[allow(unused)]
Expand Down Expand Up @@ -181,42 +182,6 @@ impl From<Size> for Margins {
}
}

/// Margins (selectable)
///
/// Default value: [`MarginSelector::Outer`].
#[impl_default(MarginSelector::Outer)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum MarginSelector {
/// No margins
None,
/// Use the theme's default around-widget margins
Outer,
/// Use the theme's default within-widget margins
Inner,
/// Use theme's default around-text margins
Text,
/// Use fixed margins
Fixed(Margins),
/// Use scaled margins (single value)
ScaledSplat(f32),
}

impl MarginSelector {
/// Convert to fixed [`Margins`]
pub fn select(&self, mgr: SizeMgr) -> Margins {
match self {
MarginSelector::None => Margins::ZERO,
MarginSelector::Outer => mgr.outer_margins(),
MarginSelector::Inner => Margins::from(mgr.inner_margin()),
MarginSelector::Text => mgr.text_margins(),
MarginSelector::Fixed(fixed) => *fixed,
MarginSelector::ScaledSplat(m) => {
Margins::splat(u16::conv_nearest(m * mgr.scale_factor()))
}
}
}
}

/// Priority for stretching widgets beyond ideal size
///
/// Space is allocated based on priority, with extra space (beyond the minimum)
Expand Down Expand Up @@ -253,7 +218,7 @@ impl_scope! {
#[derive(Clone, Debug, PartialEq)]
pub struct PixmapScaling {
/// Margins
pub margins: MarginSelector,
pub margins: MarginStyle,
/// Display size
///
/// This may be set by the providing type or by the user.
Expand All @@ -280,7 +245,7 @@ impl_scope! {
impl PixmapScaling {
/// Generates `size_rules` based on size
pub fn size_rules(&mut self, mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
let margins = self.margins.select(mgr.re()).extract(axis);
let margins = mgr.margins(self.margins).extract(axis);
let scale_factor = mgr.scale_factor();
let min = self
.size
Expand Down Expand Up @@ -329,100 +294,67 @@ impl PixmapScaling {
/// frame rules have a content offset and a minimum internal margin size.
#[derive(Clone, Copy, Debug)]
pub struct FrameRules {
offset: i32,
// (pre, post) pairs
size: i32,
inner_margin: i32,
// (pre, post) margins
m: (u16, u16),
inner: (u16, u16),
outer: (u16, u16),
}

impl FrameRules {
/// Construct
pub const ZERO: Self = FrameRules::new_sym(0, 0, 0);

/// Construct new `FrameRules`
///
/// All parameters use pairs `(first, second)` where `first` is the top/left
/// component. Parameters `inner` and `outer` are inner and outer margin
/// sizes respectively while `size` is the frame size.
///
/// - `first`: size of left or top edge
/// - `second`: size of right or bottom edge
/// - `inner_margin`: minimum size of inner margins
/// - `outer_margins`: size of (left, right) or (top, bottom) outer margins
/// If `size > 0` then internal margins are the maximum of `inner` and
/// content margin; generated rules have size
/// `content_size + size + inner_margin` and outer margin `outer`.
///
/// If `size ≤ 0` then the generated rules are simply content rules but
/// with margins the maximum of `inner` and content margins; `outer` and
/// `size` are ignored (other than to enable this mode).
#[inline]
pub fn new(first: i32, second: i32, inner_margin: i32, outer_margins: (u16, u16)) -> Self {
FrameRules {
offset: first,
size: first + second,
inner_margin,
m: outer_margins,
}
pub const fn new(size: i32, inner: (u16, u16), outer: (u16, u16)) -> Self {
FrameRules { size, inner, outer }
}

/// Construct (symmetric on axis)
#[inline]
pub fn new_sym(size: i32, inner_margin: i32, outer_margin: u16) -> Self {
Self::new(size, size, inner_margin, (outer_margin, outer_margin))
pub const fn new_sym(size: i32, inner: u16, outer: u16) -> Self {
Self::new(size, (inner, inner), (outer, outer))
}

/// Generate rules for content surrounded by this frame
///
/// The content's margins apply inside this frame. External margins come
/// from this type.
///
/// Returns the tuple `(rules, offset, size)`:
///
/// - the generated `rules`
/// - the content `offset` within the allocated rect
/// - the size consumed by the frame and inner margins (thus the content's
/// size will be that allocated for this object minus this `size` value)
pub fn surround_with_margin(self, content: SizeRules) -> (SizeRules, i32, i32) {
let (m0, m1) = content.margins_i32();
let m0 = m0.max(self.inner_margin);
let m1 = m1.max(self.inner_margin);
let offset = self.offset + m0;
let size = self.size + m0 + m1;

let rules = SizeRules::new(
content.min_size() + size,
content.ideal_size() + size,
self.m,
content.stretch(),
);
(rules, offset, size)
}

/// Variant: frame is content margin
///
/// The content's margin is reduced by the size of the frame, with any
/// residual margin applying outside the frame (using the max of the
/// frame's own margin and the residual). In other respects,
/// this is the same as [`FrameRules::surround_with_margin`].
pub fn surround_as_margin(self, content: SizeRules) -> (SizeRules, i32, i32) {
let (m0, m1) = content.margins();
let offset = self.offset + self.inner_margin;
let m0 = u16::conv((i32::conv(m0) - offset).max(0));
let size = self.size + 2 * self.inner_margin;
let m1 = u16::conv((i32::conv(m1) + offset - size).max(0));
let margins = (self.m.0.max(m0), self.m.1.max(m1));

let rules = SizeRules::new(
content.min_size() + size,
content.ideal_size() + size,
margins,
content.stretch(),
);
(rules, offset, size)
}

/// Variant: frame replaces content margin
///
/// The content's margin is ignored. In other respects,
/// this is the same as [`FrameRules::surround_with_margin`].
pub fn surround_no_margin(self, content: SizeRules) -> (SizeRules, i32, i32) {
let offset = self.offset + self.inner_margin;
let size = self.size + 2 * self.inner_margin;

let rules = SizeRules::new(
content.min_size() + size,
content.ideal_size() + size,
self.m,
content.stretch(),
);
(rules, offset, size)
pub fn surround(self, content: SizeRules) -> (SizeRules, i32, i32) {
if self.size > 0 {
let (m0, m1) = content.margins();
let m0 = m0.max(self.inner.0);
let m1 = m1.max(self.inner.1);

let offset = self.size + i32::conv(m0);
let size = offset + self.size + i32::conv(m1);

let rules = SizeRules::new(
content.min_size() + size,
content.ideal_size() + size,
self.outer,
content.stretch(),
);
(rules, offset, size)
} else {
let mut rules = content;
rules.include_margins(self.inner);
(rules, 0, 0)
}
}
}
41 changes: 25 additions & 16 deletions crates/kas-core/src/layout/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
// Methods have to take `&mut self`
#![allow(clippy::wrong_self_convention)]

use super::{Align, AlignHints, AxisInfo, MarginSelector, SizeRules};
use super::{Align, AlignHints, AxisInfo, SizeRules};
use super::{DynRowStorage, RowPositionSolver, RowSetter, RowSolver, RowStorage};
use super::{GridChildInfo, GridDimensions, GridSetter, GridSolver, GridStorage};
use super::{RulesSetter, RulesSolver};
use crate::draw::color::Rgb;
use crate::event::ConfigMgr;
use crate::geom::{Coord, Offset, Rect, Size};
use crate::theme::{Background, DrawMgr, FrameStyle, SizeMgr};
use crate::theme::{Background, DrawMgr, FrameStyle, MarginStyle, SizeMgr};
use crate::WidgetId;
use crate::{dir::Directional, dir::Directions, Layout, Widget};
use std::iter::ExactSizeIterator;
Expand Down Expand Up @@ -46,13 +46,31 @@ enum LayoutType<'a> {
/// Apply alignment hints to some sub-layout
AlignLayout(Box<Visitor<'a>>, AlignHints),
/// Replace (some) margins
Margins(Box<Visitor<'a>>, Directions, MarginSelector),
Margins(Box<Visitor<'a>>, Directions, MarginStyle),
/// Frame around content
Frame(Box<Visitor<'a>>, &'a mut FrameStorage, FrameStyle),
/// Button frame around content
Button(Box<Visitor<'a>>, &'a mut FrameStorage, Option<Rgb>),
}

/* unused utility method:
impl<'a> LayoutType<'a> {
fn id(&self) -> Option<WidgetId> {
use crate::WidgetExt;
match self {
LayoutType::None => None,
LayoutType::Component(_) => None,
LayoutType::BoxComponent(_) => None,
LayoutType::Single(w) => Some(w.id()),
LayoutType::AlignSingle(w, _) => Some(w.id()),
LayoutType::AlignLayout(l, _) => l.layout.id(),
LayoutType::Margins(l, _, _) => l.layout.id(),
LayoutType::Frame(l, _, _) => l.layout.id(),
LayoutType::Button(l, _, _) => l.layout.id(),
}
}
}*/

impl<'a> Default for Visitor<'a> {
fn default() -> Self {
Visitor::none()
Expand Down Expand Up @@ -85,7 +103,7 @@ impl<'a> Visitor<'a> {
}

/// Replace the margins of a sub-layout
pub fn margins(layout: Self, dirs: Directions, margins: MarginSelector) -> Self {
pub fn margins(layout: Self, dirs: Directions, margins: MarginStyle) -> Self {
let layout = LayoutType::Margins(Box::new(layout), dirs, margins);
Visitor { layout }
}
Expand Down Expand Up @@ -190,7 +208,7 @@ impl<'a> Visitor<'a> {
let mut child_rules = child.size_rules_(mgr.re(), axis);
if dirs.intersects(Directions::from(axis)) {
let mut rule_margins = child_rules.margins();
let margins = margins.select(mgr).extract(axis);
let margins = mgr.margins(*margins).extract(axis);
if dirs.intersects(Directions::LEFT | Directions::UP) {
rule_margins.0 = margins.0;
}
Expand Down Expand Up @@ -496,19 +514,10 @@ impl FrameStorage {
mgr: SizeMgr,
axis: AxisInfo,
child_rules: SizeRules,
mut style: FrameStyle,
style: FrameStyle,
) -> SizeRules {
let frame_rules = mgr.frame(style, axis);
if axis.is_horizontal() && style == FrameStyle::MenuEntry {
style = FrameStyle::InnerMargin;
}
let (rules, offset, size) = match style {
FrameStyle::InnerMargin | FrameStyle::EditBox => {
frame_rules.surround_with_margin(child_rules)
}
FrameStyle::NavFocus => frame_rules.surround_as_margin(child_rules),
_ => frame_rules.surround_no_margin(child_rules),
};
let (rules, offset, size) = frame_rules.surround(child_rules);
self.offset.set_component(axis, offset);
self.size.set_component(axis, size);
self.ideal_size.set_component(axis, rules.ideal_size());
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/theme/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ impl<'a> DrawMgr<'a> {
///
/// This appears as a dashed box or similar around this `rect`. Note that
/// the selection indicator is drawn *outside* of this rect, within a margin
/// of size `inner_margin` that is expected to be present around this box.
/// of size [`SizeMgr::inner_margins`] that is expected to be present around this box.
pub fn selection_box(&mut self, rect: Rect) {
self.h.selection_box(rect);
}
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/theme/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0

//! Theme APIs
//! Theme interface

mod draw;
mod size;
Expand Down
Loading

0 comments on commit 3c914fa

Please sign in to comment.