run stretch's layout on physical coordinates to fix pixel alignment (#1061)

run stretch's layout on physical coordinates to fix pixel alignment of the results
This commit is contained in:
Nathan Jeffords 2020-12-14 23:17:34 -08:00 committed by GitHub
parent 2e2423139e
commit 51650f114f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 70 deletions

View File

@ -3,71 +3,72 @@ use crate::{
JustifyContent, PositionType, Style, Val, JustifyContent, PositionType, Style, Val,
}; };
use bevy_math::{Rect, Size}; use bevy_math::{Rect, Size};
use bevy_reflect::Reflect;
fn from_rect<T, U: Reflect>(rect: Rect<U>) -> stretch::geometry::Rect<T> pub fn from_rect(
where scale_factor: f64,
T: From<U>, rect: Rect<Val>,
{ ) -> stretch::geometry::Rect<stretch::style::Dimension> {
stretch::geometry::Rect { stretch::geometry::Rect {
start: rect.left.into(), start: from_val(scale_factor, rect.left),
end: rect.right.into(), end: from_val(scale_factor, rect.right),
// NOTE: top and bottom are intentionally flipped. stretch has a flipped y-axis // NOTE: top and bottom are intentionally flipped. stretch has a flipped y-axis
top: rect.bottom.into(), top: from_val(scale_factor, rect.bottom),
bottom: rect.top.into(), bottom: from_val(scale_factor, rect.top),
} }
} }
fn from_size<T, U>(size: Size<U>) -> stretch::geometry::Size<T> pub fn from_f32_size(scale_factor: f64, size: Size<f32>) -> stretch::geometry::Size<f32> {
where
U: Reflect,
T: From<U>,
{
stretch::geometry::Size { stretch::geometry::Size {
width: size.width.into(), width: (scale_factor * size.width as f64) as f32,
height: size.height.into(), height: (scale_factor * size.height as f64) as f32,
} }
} }
impl From<&Style> for stretch::style::Style { pub fn from_val_size(
fn from(value: &Style) -> Self { scale_factor: f64,
Self { size: Size<Val>,
overflow: stretch::style::Overflow::Visible, ) -> stretch::geometry::Size<stretch::style::Dimension> {
display: value.display.into(), stretch::geometry::Size {
position_type: value.position_type.into(), width: from_val(scale_factor, size.width),
direction: value.direction.into(), height: from_val(scale_factor, size.height),
flex_direction: value.flex_direction.into(),
flex_wrap: value.flex_wrap.into(),
align_items: value.align_items.into(),
align_self: value.align_self.into(),
align_content: value.align_content.into(),
justify_content: value.justify_content.into(),
position: from_rect(value.position),
margin: from_rect(value.margin),
padding: from_rect(value.padding),
border: from_rect(value.border),
flex_grow: value.flex_grow,
flex_shrink: value.flex_shrink,
flex_basis: value.flex_basis.into(),
size: from_size(value.size),
min_size: from_size(value.min_size),
max_size: from_size(value.max_size),
aspect_ratio: match value.aspect_ratio {
Some(value) => stretch::number::Number::Defined(value),
None => stretch::number::Number::Undefined,
},
}
} }
} }
impl From<Val> for stretch::style::Dimension { pub fn from_style(scale_factor: f64, value: &Style) -> stretch::style::Style {
fn from(val: Val) -> Self { stretch::style::Style {
match val { overflow: stretch::style::Overflow::Visible,
Val::Auto => stretch::style::Dimension::Auto, display: value.display.into(),
Val::Percent(value) => stretch::style::Dimension::Percent(value / 100.0), position_type: value.position_type.into(),
Val::Px(value) => stretch::style::Dimension::Points(value), direction: value.direction.into(),
Val::Undefined => stretch::style::Dimension::Undefined, flex_direction: value.flex_direction.into(),
} flex_wrap: value.flex_wrap.into(),
align_items: value.align_items.into(),
align_self: value.align_self.into(),
align_content: value.align_content.into(),
justify_content: value.justify_content.into(),
position: from_rect(scale_factor, value.position),
margin: from_rect(scale_factor, value.margin),
padding: from_rect(scale_factor, value.padding),
border: from_rect(scale_factor, value.border),
flex_grow: value.flex_grow,
flex_shrink: value.flex_shrink,
flex_basis: from_val(scale_factor, value.flex_basis),
size: from_val_size(scale_factor, value.size),
min_size: from_val_size(scale_factor, value.min_size),
max_size: from_val_size(scale_factor, value.max_size),
aspect_ratio: match value.aspect_ratio {
Some(value) => stretch::number::Number::Defined(value),
None => stretch::number::Number::Undefined,
},
}
}
pub fn from_val(scale_factor: f64, val: Val) -> stretch::style::Dimension {
match val {
Val::Auto => stretch::style::Dimension::Auto,
Val::Percent(value) => stretch::style::Dimension::Percent(value / 100.0),
Val::Px(value) => stretch::style::Dimension::Points((scale_factor * value as f64) as f32),
Val::Undefined => stretch::style::Dimension::Undefined,
} }
} }

View File

@ -35,10 +35,10 @@ impl Default for FlexSurface {
} }
impl FlexSurface { impl FlexSurface {
pub fn upsert_node(&mut self, entity: Entity, style: &Style) { pub fn upsert_node(&mut self, entity: Entity, style: &Style, scale_factor: f64) {
let mut added = false; let mut added = false;
let stretch = &mut self.stretch; let stretch = &mut self.stretch;
let stretch_style = style.into(); let stretch_style = convert::from_style(scale_factor, style);
let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| { let stretch_node = self.entity_to_stretch.entry(entity).or_insert_with(|| {
added = true; added = true;
stretch.new_node(stretch_style, Vec::new()).unwrap() stretch.new_node(stretch_style, Vec::new()).unwrap()
@ -51,14 +51,17 @@ impl FlexSurface {
} }
} }
pub fn upsert_leaf(&mut self, entity: Entity, style: &Style, calculated_size: CalculatedSize) { pub fn upsert_leaf(
&mut self,
entity: Entity,
style: &Style,
calculated_size: CalculatedSize,
scale_factor: f64,
) {
let stretch = &mut self.stretch; let stretch = &mut self.stretch;
let stretch_style = style.into(); let stretch_style = convert::from_style(scale_factor, style);
let measure = Box::new(move |constraints: stretch::geometry::Size<Number>| { let measure = Box::new(move |constraints: stretch::geometry::Size<Number>| {
let mut size = stretch::geometry::Size { let mut size = convert::from_f32_size(scale_factor, calculated_size.size);
width: calculated_size.size.width,
height: calculated_size.size.height,
};
match (constraints.width, constraints.height) { match (constraints.width, constraints.height) {
(Number::Undefined, Number::Undefined) => {} (Number::Undefined, Number::Undefined) => {}
(Number::Defined(width), Number::Undefined) => { (Number::Defined(width), Number::Undefined) => {
@ -116,8 +119,8 @@ impl FlexSurface {
*node, *node,
stretch::style::Style { stretch::style::Style {
size: stretch::geometry::Size { size: stretch::geometry::Size {
width: stretch::style::Dimension::Points(window.width()), width: stretch::style::Dimension::Points(window.physical_width() as f32),
height: stretch::style::Dimension::Points(window.height()), height: stretch::style::Dimension::Points(window.physical_height() as f32),
}, },
..Default::default() ..Default::default()
}, },
@ -174,18 +177,25 @@ pub fn flex_node_system(
flex_surface.update_window(window); flex_surface.update_window(window);
} }
// assume one window for time being...
let logical_to_physical_factor = if let Some(primary_window) = windows.get_primary() {
primary_window.scale_factor()
} else {
1.
};
// update changed nodes // update changed nodes
for (entity, style, calculated_size) in node_query.iter() { for (entity, style, calculated_size) in node_query.iter() {
// TODO: remove node from old hierarchy if its root has changed // TODO: remove node from old hierarchy if its root has changed
if let Some(calculated_size) = calculated_size { if let Some(calculated_size) = calculated_size {
flex_surface.upsert_leaf(entity, &style, *calculated_size); flex_surface.upsert_leaf(entity, &style, *calculated_size, logical_to_physical_factor);
} else { } else {
flex_surface.upsert_node(entity, &style); flex_surface.upsert_node(entity, &style, logical_to_physical_factor);
} }
} }
for (entity, style, calculated_size) in changed_size_query.iter() { for (entity, style, calculated_size) in changed_size_query.iter() {
flex_surface.upsert_leaf(entity, &style, *calculated_size); flex_surface.upsert_leaf(entity, &style, *calculated_size, logical_to_physical_factor);
} }
// TODO: handle removed nodes // TODO: handle removed nodes
@ -203,16 +213,23 @@ pub fn flex_node_system(
// compute layouts // compute layouts
flex_surface.compute_window_layouts(); flex_surface.compute_window_layouts();
let physical_to_logical_factor = 1. / logical_to_physical_factor;
let to_logical = |v| (physical_to_logical_factor * v as f64) as f32;
for (entity, mut node, mut transform, parent) in node_transform_query.iter_mut() { for (entity, mut node, mut transform, parent) in node_transform_query.iter_mut() {
let layout = flex_surface.get_layout(entity).unwrap(); let layout = flex_surface.get_layout(entity).unwrap();
node.size = Vec2::new(layout.size.width, layout.size.height); node.size = Vec2::new(
to_logical(layout.size.width),
to_logical(layout.size.height),
);
let position = &mut transform.translation; let position = &mut transform.translation;
position.x = layout.location.x + layout.size.width / 2.0; position.x = to_logical(layout.location.x + layout.size.width / 2.0);
position.y = layout.location.y + layout.size.height / 2.0; position.y = to_logical(layout.location.y + layout.size.height / 2.0);
if let Some(parent) = parent { if let Some(parent) = parent {
if let Ok(parent_layout) = flex_surface.get_layout(parent.0) { if let Ok(parent_layout) = flex_surface.get_layout(parent.0) {
position.x -= parent_layout.size.width / 2.0; position.x -= to_logical(parent_layout.size.width / 2.0);
position.y -= parent_layout.size.height / 2.0; position.y -= to_logical(parent_layout.size.height / 2.0);
} }
} }
} }