|
@@ -0,0 +1,258 @@
|
|
|
+use std::{
|
|
|
+ ops::{Deref, DerefMut},
|
|
|
+ rc::Rc,
|
|
|
+};
|
|
|
+
|
|
|
+use cache::{Layer, LayoutCache, LayoutCacheKey, NodeState};
|
|
|
+use crate::geom::IRect;
|
|
|
+
|
|
|
+mod arr;
|
|
|
+mod cache;
|
|
|
+mod calc;
|
|
|
+
|
|
|
+pub use calc::recalculate;
|
|
|
+
|
|
|
+/// Sizing policy for a layout dimension
|
|
|
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
|
|
|
+pub struct SizePolicy {
|
|
|
+ /// The minimum number of pixels required along this dimension to avoid overlap and other
|
|
|
+ /// issues.
|
|
|
+ pub minimum: usize,
|
|
|
+ /// The number of pixels requested to have a comfortable amount of space and avoid ugly-looking
|
|
|
+ /// UI.
|
|
|
+ pub desired: usize,
|
|
|
+ /// How much to take up any remaining slack space.
|
|
|
+ pub slack_weight: usize,
|
|
|
+}
|
|
|
+
|
|
|
+impl SizePolicy {
|
|
|
+ pub fn max(self, rhs: SizePolicy) -> SizePolicy {
|
|
|
+ Self {
|
|
|
+ minimum: self.minimum.max(rhs.minimum),
|
|
|
+ desired: self.desired.max(rhs.desired),
|
|
|
+ slack_weight: self.slack_weight.max(rhs.slack_weight),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn max_preserve_slack(self, rhs: SizePolicy) -> SizePolicy {
|
|
|
+ Self {
|
|
|
+ minimum: self.minimum.max(rhs.minimum),
|
|
|
+ desired: self.desired.max(rhs.desired),
|
|
|
+ slack_weight: self.slack_weight,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl std::ops::Add<Self> for SizePolicy {
|
|
|
+ type Output = Self;
|
|
|
+ fn add(self, rhs: Self) -> Self::Output {
|
|
|
+ Self {
|
|
|
+ minimum: self.minimum + rhs.minimum,
|
|
|
+ desired: self.desired + rhs.desired,
|
|
|
+ slack_weight: self.slack_weight + rhs.slack_weight,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
|
|
|
+pub struct BoxMeasure {
|
|
|
+ pub left: usize,
|
|
|
+ pub right: usize,
|
|
|
+ pub top: usize,
|
|
|
+ pub bottom: usize,
|
|
|
+}
|
|
|
+
|
|
|
+impl From<usize> for BoxMeasure {
|
|
|
+ fn from(value: usize) -> Self {
|
|
|
+ Self::new_from_value(value)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl BoxMeasure {
|
|
|
+ pub fn new_from_value(value: usize) -> Self {
|
|
|
+ Self {
|
|
|
+ left: value,
|
|
|
+ right: value,
|
|
|
+ top: value,
|
|
|
+ bottom: value,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// What sort of layout does a node represent?
|
|
|
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
|
+pub enum NodeBehaviour {
|
|
|
+ /// A fixed rendering area, probably a root node.
|
|
|
+ Fixed { rect: IRect },
|
|
|
+ /// An ordinary box sitting inside another node, hinting its size and receiving a final
|
|
|
+ /// size assignment from its parent.
|
|
|
+ Element,
|
|
|
+ /// A node that floats above other content, anchored with a zero-size flexbox.
|
|
|
+ Anchored,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+pub enum ChildArrangement {
|
|
|
+ Custom(std::rc::Rc<dyn arr::ArrangementCalculator>),
|
|
|
+ Column,
|
|
|
+ Row,
|
|
|
+ Table,
|
|
|
+}
|
|
|
+
|
|
|
+impl std::fmt::Debug for ChildArrangement {
|
|
|
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
+ match self {
|
|
|
+ Self::Custom(_) => f.write_str("ChildArrangement::Custom"),
|
|
|
+ Self::Column => f.write_str("ChildArrangement::Column"),
|
|
|
+ Self::Row => f.write_str("ChildArrangement::Row"),
|
|
|
+ Self::Table => f.write_str("ChildArrangement::Table"),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Deref for ChildArrangement {
|
|
|
+ type Target = dyn arr::ArrangementCalculator;
|
|
|
+ fn deref(&self) -> &Self::Target {
|
|
|
+ match self {
|
|
|
+ Self::Custom(calc) => calc.as_ref(),
|
|
|
+ Self::Column => &arr::LineArrangement::Column,
|
|
|
+ Self::Row => &arr::LineArrangement::Row,
|
|
|
+ Self::Table => todo!(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Debug)]
|
|
|
+pub struct LayoutNode {
|
|
|
+ cache_key: LayoutCacheKey,
|
|
|
+ cache: Rc<LayoutCache>,
|
|
|
+ behaviour: NodeBehaviour,
|
|
|
+ child_arrangement: ChildArrangement,
|
|
|
+ width_policy: SizePolicy,
|
|
|
+ height_policy: SizePolicy,
|
|
|
+
|
|
|
+ padding: BoxMeasure,
|
|
|
+ margin: BoxMeasure,
|
|
|
+}
|
|
|
+
|
|
|
+impl LayoutNode {
|
|
|
+ pub fn new(cache: Rc<LayoutCache>) -> Self {
|
|
|
+ let cache_key = LayoutCacheKey::generate();
|
|
|
+ cache.update_queue.borrow_mut().push(cache_key);
|
|
|
+ Self {
|
|
|
+ cache_key,
|
|
|
+ cache,
|
|
|
+ behaviour: NodeBehaviour::Element,
|
|
|
+ child_arrangement: ChildArrangement::Column,
|
|
|
+ width_policy: SizePolicy::default(),
|
|
|
+ height_policy: SizePolicy::default(),
|
|
|
+ padding: 0.into(),
|
|
|
+ margin: 0.into(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn relayout(&self) {
|
|
|
+ self.cache.update_queue.borrow_mut().push(self.cache_key);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// accessors
|
|
|
+impl LayoutNode {
|
|
|
+ pub fn behaviour(&self) -> NodeBehaviour {
|
|
|
+ self.behaviour
|
|
|
+ }
|
|
|
+ pub fn set_behaviour(&mut self, mode: NodeBehaviour) {
|
|
|
+ self.behaviour = mode;
|
|
|
+ self.relayout();
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn arrangement(&self) -> &ChildArrangement {
|
|
|
+ &self.child_arrangement
|
|
|
+ }
|
|
|
+ pub fn set_arrangement(&mut self, arr: ChildArrangement) {
|
|
|
+ self.child_arrangement = arr;
|
|
|
+ self.relayout();
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn width_policy(&self) -> SizePolicy {
|
|
|
+ self.width_policy
|
|
|
+ }
|
|
|
+ pub fn set_width_policy(&mut self, policy: SizePolicy) {
|
|
|
+ self.width_policy = policy;
|
|
|
+ self.relayout();
|
|
|
+ }
|
|
|
+ pub fn height_policy(&self) -> SizePolicy {
|
|
|
+ self.height_policy
|
|
|
+ }
|
|
|
+ pub fn set_height_policy(&mut self, policy: SizePolicy) {
|
|
|
+ self.height_policy = policy;
|
|
|
+ self.relayout();
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn padding(&self) -> BoxMeasure {
|
|
|
+ self.padding
|
|
|
+ }
|
|
|
+ pub fn padding_mut(&mut self) -> &mut BoxMeasure {
|
|
|
+ &mut self.padding
|
|
|
+ }
|
|
|
+ pub fn margin(&self) -> BoxMeasure {
|
|
|
+ self.margin
|
|
|
+ }
|
|
|
+ pub fn margin_mut(&mut self) -> &mut BoxMeasure {
|
|
|
+ &mut self.margin
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Clone for LayoutNode {
|
|
|
+ fn clone(&self) -> Self {
|
|
|
+ Self {
|
|
|
+ cache_key: LayoutCacheKey::generate(),
|
|
|
+ cache: self.cache.clone(),
|
|
|
+ behaviour: self.behaviour,
|
|
|
+ child_arrangement: self.child_arrangement.clone(),
|
|
|
+ width_policy: self.width_policy,
|
|
|
+ height_policy: self.height_policy,
|
|
|
+ padding: self.padding,
|
|
|
+ margin: self.margin,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl Drop for LayoutNode {
|
|
|
+ fn drop(&mut self) {
|
|
|
+ self.cache.update_queue.borrow_mut().push(self.cache_key);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Clone)]
|
|
|
+pub struct LayoutChildIter<'l> {
|
|
|
+ lna: &'l dyn LayoutNodeAccess,
|
|
|
+ next_index: usize,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'l> LayoutChildIter<'l> {
|
|
|
+ pub fn new(lna: &'l dyn LayoutNodeAccess) -> Self {
|
|
|
+ Self { lna, next_index: 0 }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'l> Iterator for LayoutChildIter<'l> {
|
|
|
+ type Item = &'l dyn LayoutNodeAccess;
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ let index = self.next_index;
|
|
|
+ if index >= self.lna.child_count() {
|
|
|
+ None
|
|
|
+ } else {
|
|
|
+ self.next_index += 1;
|
|
|
+ self.lna.child(index)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Accessor trait for a [`LayoutNode`] tree, which has externally-stored relationships.
|
|
|
+pub trait LayoutNodeAccess: Deref<Target = LayoutNode> + DerefMut<Target = LayoutNode> {
|
|
|
+ fn child_count(&self) -> usize;
|
|
|
+ fn child(&self, ndx: usize) -> Option<&dyn LayoutNodeAccess>;
|
|
|
+ fn child_iter(&self) -> LayoutChildIter;
|
|
|
+}
|