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

Introducing intrinsics #2172

Merged
merged 9 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
10 changes: 10 additions & 0 deletions druid/src/box_constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,16 @@ impl BoxConstraints {
}
}
}

/// Set max width to max_width
pub fn set_max_width(&mut self, max_width: f64) {
self.max.width = max_width;
}

/// Set max height to max_height
pub fn set_max_height(&mut self, max_height: f64) {
self.max.height = max_height;
}
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(test)]
Expand Down
28 changes: 28 additions & 0 deletions druid/src/widget/aspect_ratio_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,32 @@ impl<T: Data> Widget<T> for AspectRatioBox<T> {
..Default::default()
}
}

fn compute_max_intrinsic_width(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
if bc.is_height_bounded() {
bc.max().height * self.ratio
} else {
self.child.compute_max_intrinsic_width(ctx, bc, data, env)
}
}

fn compute_max_intrinsic_height(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
if bc.is_width_bounded() {
bc.max().width / self.ratio
} else {
self.child.compute_max_intrinsic_height(ctx, bc, data, env)
}
}
}
41 changes: 41 additions & 0 deletions druid/src/widget/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,21 @@ impl<T: Data> Container<T> {
pub(crate) fn border_is_some(&self) -> bool {
self.border.is_some()
}

/// max_intrinsic_size = child.max_intrinsic_size + container's border size * 2
fn compute_intrinsic<F>(&mut self, bc: &BoxConstraints, env: &Env, f: F) -> f64
where
F: FnOnce(&BoxConstraints, &mut Box<dyn Widget<T>>) -> f64,
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
{
let container_width = match &self.border {
Some(border) => border.width.resolve(env),
None => 0.0,
};
let child_bc = bc.shrink((2.0 * container_width, 2.0 * container_width));
let child_size = f(&child_bc, self.child.widget_mut());
let border_width_on_both_sides = container_width * 2.;
child_size + border_width_on_both_sides
}
}

impl<T: Data> Widget<T> for Container<T> {
Expand Down Expand Up @@ -242,4 +257,30 @@ impl<T: Data> Widget<T> for Container<T> {
..Default::default()
}
}

fn compute_max_intrinsic_width(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
let f = |bc: &BoxConstraints, child: &mut Box<dyn Widget<T>>| {
child.compute_max_intrinsic_width(ctx, bc, data, env)
};
self.compute_intrinsic(bc, env, f)
}

fn compute_max_intrinsic_height(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
let f = |bc: &BoxConstraints, child: &mut Box<dyn Widget<T>>| {
child.compute_max_intrinsic_height(ctx, bc, data, env)
};
self.compute_intrinsic(bc, env, f)
}
}
20 changes: 20 additions & 0 deletions druid/src/widget/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,26 @@ impl<T, W: Widget<T>, C: Controller<T, W>> Widget<T> for ControllerHost<W, C> {
..Default::default()
}
}

fn compute_max_intrinsic_width(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
self.widget.compute_max_intrinsic_width(ctx, bc, data, env)
}

fn compute_max_intrinsic_height(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
self.widget.compute_max_intrinsic_height(ctx, bc, data, env)
}
}

impl<W, C> WidgetWrapper for ControllerHost<W, C> {
Expand Down
204 changes: 204 additions & 0 deletions druid/src/widget/flex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::debug_state::DebugState;
use crate::kurbo::{common::FloatExt, Vec2};
use crate::widget::prelude::*;
use crate::{Data, KeyOrValue, Point, Rect, WidgetPod};
use std::ops::Add;
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
use tracing::{instrument, trace};

/// A container with either horizontal or vertical layout.
Expand Down Expand Up @@ -625,6 +626,116 @@ impl<T: Data> Flex<T> {
let new_child = Child::FlexedSpacer(flex, 0.0);
self.children.push(new_child);
}

/// This function calculates max intrinsic cross axis size of opposite direction. Ie. calculate
/// intrinsic width of [`Axis::Vertical`] or intrinsic height of a [`Axis::Horizontal`].
/// This function for each child:
/// * Computes intrinsic main axis size with infinite cross axis. Then,
/// * Computes intrinsic cross axis size with main axis bounded from previous step.
///
/// It returns the max of all intrinsic cross axis sizes.
fn do_opposite<F1, F2>(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
env: &Env,
initial_available_size_on_main_axis: f64,
mut f1: F1,
mut f2: F2,
) -> f64
where
F1: FnMut(
f64,
&mut LayoutCtx,
BoxConstraints,
&mut WidgetPod<T, Box<dyn Widget<T>>>,
) -> f64,
F2: FnMut(
f64,
&mut LayoutCtx,
BoxConstraints,
&mut WidgetPod<T, Box<dyn Widget<T>>>,
) -> f64,
{
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
let mut max_size_on_cross_axis: f64 = 0.;
let mut available_size_on_main_axis = initial_available_size_on_main_axis;
let mut total_flex = 0.;
//non-flex children
for child in self.children.iter_mut() {
match child {
Child::Fixed { widget, .. } => {
let size_on_main_axis = f1(available_size_on_main_axis, ctx, *bc, widget);
available_size_on_main_axis -= size_on_main_axis;
let size_on_cross_axis = f2(size_on_main_axis, ctx, *bc, widget);
max_size_on_cross_axis = max_size_on_cross_axis.max(size_on_cross_axis);
}
Child::FixedSpacer(kv, _) => {
let mut s = kv.resolve(env);
if s < 0.0 {
tracing::warn!("Length provided to fixed spacer was less than 0");
s = 0.;
}
max_size_on_cross_axis = max_size_on_cross_axis.max(s);
}
Child::Flex { flex, .. } | Child::FlexedSpacer(flex, _) => total_flex += *flex,
}
}

let space_per_flex = available_size_on_main_axis / total_flex;

// flex children
if space_per_flex > 0.0 {
for child in self.children.iter_mut() {
// We ignore Child::FlexedSpacer because its cross size is irrelevant.
// Its flex matters only on main axis. But here we are interested in cross size of
// each flex child.
if let Child::Flex { widget, flex, .. } = child {
let main_axis_available_space = *flex * space_per_flex;
let size_on_cross_axis = f2(main_axis_available_space, ctx, *bc, widget);
max_size_on_cross_axis = max_size_on_cross_axis.max(size_on_cross_axis);
}
}
}
max_size_on_cross_axis
}

/// This function calculates intrinsic size on main axis. Ie. it calculate intrinsic width of
/// [`Axis::Horizontal`] or intrinsic height of a [`Axis::Vertical`].
///
/// It returns sum of intrinsic sizes on main axis of each child.
fn do_same<F>(&mut self, env: &Env, mut f: F) -> f64
where
F: FnMut(&mut WidgetPod<T, Box<dyn Widget<T>>>) -> f64,
{
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
let mut total: f64 = 0.;
let mut max_flex_fraction: f64 = 0.;
let mut total_flex = 0.;
for child in self.children.iter_mut() {
match child {
Child::Fixed { widget, .. } => {
let s = f(widget);
total = total.add(s);
}
Child::Flex { widget, flex, .. } => {
let flex_fraction = f(widget) / *flex;
total_flex += *flex;
max_flex_fraction = max_flex_fraction.max(flex_fraction);
}
Child::FixedSpacer(kv, _) => {
let mut s = kv.resolve(env);
if s < 0.0 {
tracing::warn!("Length provided to fixed spacer was less than 0");
s = 0.;
}
total = total.add(s);
}
Child::FlexedSpacer(flex, _) => {
total_flex += *flex;
}
}
}
total + max_flex_fraction * total_flex
}
}

impl<T: Data> Widget<T> for Flex<T> {
Expand Down Expand Up @@ -942,6 +1053,99 @@ impl<T: Data> Widget<T> for Flex<T> {
..Default::default()
}
}

fn compute_max_intrinsic_width(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
match self.direction {
Axis::Horizontal => {
let f = |widget: &mut WidgetPod<T, Box<dyn Widget<T>>>| {
widget
.widget_mut()
.compute_max_intrinsic_width(ctx, bc, data, env)
sjoshid marked this conversation as resolved.
Show resolved Hide resolved
};
self.do_same(env, f)
}
Axis::Vertical => {
let hh = |available: f64,
ctx: &mut LayoutCtx,
mut bc: BoxConstraints,
widget: &mut WidgetPod<T, Box<dyn Widget<T>>>|
-> f64 {
bc.set_max_width(f64::INFINITY);
bc.set_max_height(available);
let h = widget
.widget_mut()
.compute_max_intrinsic_height(ctx, &bc, data, env);
h
};

let hw = |available: f64,
ctx: &mut LayoutCtx,
mut bc: BoxConstraints,
widget: &mut WidgetPod<T, Box<dyn Widget<T>>>|
-> f64 {
bc.set_max_height(available);
widget
.widget_mut()
.compute_max_intrinsic_width(ctx, &bc, data, env)
};

// only data is captured in above closures
self.do_opposite(ctx, bc, env, bc.max().height, hh, hw)
}
}
}

fn compute_max_intrinsic_height(
&mut self,
ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> f64 {
match self.direction {
Axis::Horizontal => {
let ww = |available: f64,
ctx: &mut LayoutCtx,
mut bc: BoxConstraints,
widget: &mut WidgetPod<T, Box<dyn Widget<T>>>|
-> f64 {
bc.set_max_width(available);
bc.set_max_height(f64::INFINITY);
widget
.widget_mut()
.compute_max_intrinsic_width(ctx, &bc, data, env)
};

let wh = |available: f64,
ctx: &mut LayoutCtx,
mut bc: BoxConstraints,
widget: &mut WidgetPod<T, Box<dyn Widget<T>>>|
-> f64 {
bc.set_max_width(available);
widget
.widget_mut()
.compute_max_intrinsic_height(ctx, &bc, data, env)
};

// only data is captured in above closures
self.do_opposite(ctx, bc, env, bc.max().width, ww, wh)
}
Axis::Vertical => {
let f = |widget: &mut WidgetPod<T, Box<dyn Widget<T>>>| {
widget
.widget_mut()
.compute_max_intrinsic_height(ctx, bc, data, env)
};
self.do_same(env, f)
}
}
}
}

impl CrossAxisAlignment {
Expand Down
Loading