diff --git a/Cargo.lock b/Cargo.lock index 014d0dd96..58a145b3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -604,12 +604,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "grid" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0634107a3a005070dd73e27e74ecb691a94e9e5ba7829f434db7fbf73a6b5c47" -dependencies = [ - "no-std-compat", -] +checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" [[package]] name = "h2" @@ -1028,12 +1025,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "no-std-compat" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1842,9 +1833,9 @@ dependencies = [ [[package]] name = "taffy" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3540ec65df399929a04a485feb50144475735920cc47eaf8eba09c70b1df4055" +checksum = "82870da09c57a8a5a50f830ce8993a6637b9a4932f69257f12aea3fa68f588c9" dependencies = [ "arrayvec", "grid", diff --git a/crates/figma_import/Cargo.toml b/crates/figma_import/Cargo.toml index 331bb9fbf..fa68c2c4d 100644 --- a/crates/figma_import/Cargo.toml +++ b/crates/figma_import/Cargo.toml @@ -55,4 +55,4 @@ required-features = ["reflection"] [[bin]] name = "fetch" path = "src/bin/fetch.rs" -required-features = ["fetch"] \ No newline at end of file +required-features = ["fetch"] diff --git a/crates/figma_import/src/bin/fetch.rs b/crates/figma_import/src/bin/fetch.rs index 0aabc48c1..56fe18e5a 100644 --- a/crates/figma_import/src/bin/fetch.rs +++ b/crates/figma_import/src/bin/fetch.rs @@ -168,10 +168,10 @@ fn fetch_impl(args: Args) -> Result<(), ConvertError> { } } - println!("### SetNodeSize 250, 24"); - set_node_size(2, 250, 24); - set_node_size(5, 250, 24); - print_layout(0); + //println!("### SetNodeSize 250, 24"); + //set_node_size(2, 250, 24); + //set_node_size(5, 250, 24); + //print_layout(0); // TODO make a unit test for this below based off of #stage-replacements /* diff --git a/crates/figma_import/src/figma_schema.rs b/crates/figma_import/src/figma_schema.rs index 7f2a105bf..c24136739 100644 --- a/crates/figma_import/src/figma_schema.rs +++ b/crates/figma_import/src/figma_schema.rs @@ -721,6 +721,10 @@ pub struct FrameCommon { #[serde(default)] pub counter_axis_sizing_mode: LayoutSizingMode, // FIXED, AUTO #[serde(default)] + pub layout_sizing_horizontal: LayoutSizing, + #[serde(default)] + pub layout_sizing_vertical: LayoutSizing, + #[serde(default)] pub primary_axis_align_items: LayoutAlignItems, // MIN, CENTER, MAX, SPACE_BETWEEN #[serde(default)] pub counter_axis_align_items: LayoutAlignItems, // MIN, CENTER, MAX, diff --git a/crates/figma_import/src/layout.rs b/crates/figma_import/src/layout.rs index 6e3f9a422..d90aba336 100644 --- a/crates/figma_import/src/layout.rs +++ b/crates/figma_import/src/layout.rs @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. use lazy_static::lazy_static; -use log::{error, warn}; +use log::error; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::sync::{Mutex, MutexGuard}; -//use taffy::error::{TaffyError, TaffyResult}; use taffy::prelude::{AvailableSpace, Size, Taffy}; use taffy::tree::LayoutTree; use crate::toolkit_schema; +// The layout response sent back to client which contains a layout state ID and +// a list of layout IDs that have changed. #[derive(Serialize, Deserialize, Debug)] pub struct LayoutChangedResponse { pub layout_state: i32, @@ -33,6 +34,7 @@ impl LayoutChangedResponse { } } +// Customizations that can applied to a node struct Customizations { sizes: HashMap>, } @@ -51,32 +53,27 @@ impl Customizations { } struct LayoutManager { - /* - // node id -> Taffy node used to calculate layout - node_id_to_taffy_node: HashMap, - // Taffy node -> node id - taffy_node_to_node_id: HashMap, - // top level nodes used to recompute layouts - node_queries: HashMap, - */ // node id -> Taffy layout layouts: HashMap, // A struct that keeps track of all customizations customizations: Customizations, - // Incrementing ID used to keep track of layout changes. Incremented every time relayout is done + // Incrementing ID used to keep track of layout changes. Incremented + // every time relayout is done layout_state: i32, + // layout id -> Taffy node used to calculate layout layout_id_to_taffy_node: HashMap, + // Taffy node -> layout id taffy_node_to_layout_id: HashMap, + // layout id -> View layout_id_to_view: HashMap, + // A list of root level layout IDs used to recompute layout whenever + // something has changed root_layout_ids: HashSet, } impl LayoutManager { fn new() -> Self { LayoutManager { - //node_id_to_taffy_node: HashMap::new(), - //taffy_node_to_node_id: HashMap::new(), - //node_queries: HashMap::new(), layouts: HashMap::new(), customizations: Customizations::new(), layout_state: 0, @@ -203,21 +200,6 @@ impl LayoutManager { } } - /* - - fn make_leaf_with_measure( - node_style: taffy::style::Style, - measure_func: taffy::node::MeasureFunc, - id: String, - manager: &mut LayoutManager, - taffy: &mut Taffy, - ) -> TaffyResult { - let leaf_node = taffy.new_leaf_with_measure(node_style, measure_func)?; - manager.node_id_to_taffy_node.insert(id.clone(), leaf_node); - manager.taffy_node_to_node_id.insert(leaf_node, id); - Ok(leaf_node) - } - */ fn add_view( &mut self, layout_id: i32, @@ -229,11 +211,16 @@ impl LayoutManager { ) -> LayoutChangedResponse { let mut taffy = taffy(); - let mut node_style: taffy::style::Style = (&view.style).into(); + let mut node_style: taffy::style::Style = (&view.style).into(); //(&transformed_style).into(); + if view.name == "RectRot" { + //println!("### VIEWSTYLE:"); + //println!("### {:#?}", view.style); + //println!("### TAFFYSTYLE:"); + //println!("### {:#?}", node_style); + //println!(" ### View {} transform: {:?}", view.name, view.style.transform); + } + self.apply_variant_style(&mut node_style, &variant_view); - //warn!("### {}:", view.name); - //warn!("### {:#?}", node_style); - //warn!("###"); self.apply_customizations(layout_id, &mut node_style); let node = self.layout_id_to_taffy_node.get(&layout_id); @@ -268,6 +255,7 @@ impl LayoutManager { self.taffy_node_to_layout_id.insert(node, layout_id); self.layout_id_to_taffy_node.insert(layout_id, node); self.layout_id_to_view.insert(layout_id, view.clone()); + //self.layout_id_to_transforms.insert(layout_id, node_transform); } } Err(e) => { diff --git a/crates/figma_import/src/toolkit_layout_style.rs b/crates/figma_import/src/toolkit_layout_style.rs index 47b726bc9..aa22a57d4 100644 --- a/crates/figma_import/src/toolkit_layout_style.rs +++ b/crates/figma_import/src/toolkit_layout_style.rs @@ -46,6 +46,15 @@ pub struct Size { pub height: T, } +impl Default for Size { + fn default() -> Self { + Size { + width: 0.0, + height: 0.0, + } + } +} + #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] pub enum AlignItems { FlexStart, @@ -325,3 +334,18 @@ impl Default for Size { Self { width: Dimension::Auto, height: Dimension::Auto } } } + +impl Dimension { + pub fn is_points(&self) -> bool { + match self { + Dimension::Points(_) => true, + _ => false + } + } + pub fn points(&self) -> f32 { + match self { + Dimension::Points(p) => *p, + _ => 0.0 + } + } +} \ No newline at end of file diff --git a/crates/figma_import/src/toolkit_style.rs b/crates/figma_import/src/toolkit_style.rs index fff669510..80e0925bc 100644 --- a/crates/figma_import/src/toolkit_style.rs +++ b/crates/figma_import/src/toolkit_style.rs @@ -24,7 +24,7 @@ use crate::{ toolkit_font_style::{FontStretch, FontStyle, FontWeight}, toolkit_layout_style::{ AlignContent, AlignItems, AlignSelf, Dimension, Display, FlexDirection, FlexWrap, - JustifyContent, Number, Overflow, PositionType, Rect, + JustifyContent, Number, Overflow, PositionType, Rect, Size, }, }; @@ -544,6 +544,7 @@ pub struct ViewStyle { pub flex_grow: f32, pub flex_shrink: f32, pub flex_basis: Dimension, + pub bounding_box: Size, pub width: Dimension, pub height: Dimension, pub min_width: Dimension, @@ -606,6 +607,7 @@ impl Default for ViewStyle { flex_grow: 0.0, flex_shrink: 0.0, flex_basis: Dimension::default(), + bounding_box: Size::default(), width: Dimension::default(), height: Dimension::default(), min_width: Dimension::default(), @@ -642,6 +644,16 @@ impl Into for &ViewStyle { tstyle.size.width = (&self.width).into(); tstyle.size.height = (&self.height).into(); + // If we have a fixed size, use the bounding box since that takes into + // account scale and rotation. + // TODO support this with non-fixed sizes also! + if self.width.is_points() { + tstyle.size.width = taffy::Dimension::Points(self.bounding_box.width); + } + if self.height.is_points() { + tstyle.size.height = taffy::Dimension::Points(self.bounding_box.height); + } + tstyle.min_size.width = (&self.min_width).into(); tstyle.min_size.height = (&self.min_height).into(); tstyle.max_size.width = (&self.max_width).into(); diff --git a/crates/figma_import/src/transform_flexbox.rs b/crates/figma_import/src/transform_flexbox.rs index aecc29429..b648b2fc7 100644 --- a/crates/figma_import/src/transform_flexbox.rs +++ b/crates/figma_import/src/transform_flexbox.rs @@ -27,7 +27,6 @@ use crate::toolkit_style::{ LineHeight, MeterData, ShadowBox, StyledTextRun, TextAlign, TextOverflow, TextStyle, ViewStyle, }; -use crate::figma_schema::{TextAutoResize, TextTruncation}; use crate::vector_schema; use crate::{ component_context::ComponentContext, @@ -35,8 +34,8 @@ use crate::{ figma_schema::{ BlendMode, Component, ComponentSet, EffectType, HorizontalLayoutConstraintValue, LayoutAlign, LayoutAlignItems, LayoutMode, LayoutSizingMode, LineHeightUnit, Node, - NodeData, PaintData, StrokeAlign, TextAlignHorizontal, TextAlignVertical, LayoutSizing, - Vector, VerticalLayoutConstraintValue, + NodeData, PaintData, StrokeAlign, TextAlignHorizontal, TextAlignVertical, + Vector, VerticalLayoutConstraintValue, LayoutSizing, TextAutoResize, TextTruncation }, image_context::ImageContext, reaction_schema::{FrameExtras, Reaction, ReactionJson}, @@ -88,6 +87,11 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle { let mut hug_width = false; let mut hug_height = false; + if let Some(bounds) = node.absolute_bounding_box { + style.bounding_box.width = bounds.width(); + style.bounding_box.height = bounds.height(); + } + // Frames can implement Auto Layout on their children. if let Some(frame) = node.frame() { style.position_type = PositionType::Relative; @@ -188,16 +192,15 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle { // Frame is not autolayout, so used a fixed size style.width = dim_points_or_auto(bounds.width().ceil()); style.height = dim_points_or_auto(bounds.height().ceil()); - /* - style.width = match frame.counter_axis_sizing_mode { - LayoutSizingMode::Fixed => dim_points_or_auto(bounds.width().ceil()), - LayoutSizingMode::Auto => Dimension::Auto, - }; - style.height = match frame.primary_axis_sizing_mode { - LayoutSizingMode::Fixed => dim_points_or_auto(bounds.height().ceil()), - LayoutSizingMode::Auto => Dimension::Auto, - }; - */ + + // If we have a node size specified, use that instead. The + // node size is the size without rotation and scale. + if let Some(size) = &node.size { + if size.is_valid() { + style.width = Dimension::Points(size.x()); + style.height = Dimension::Points(size.y()); + } + } } } } diff --git a/designcompose/src/main/java/com/android/designcompose/DesignFrame.kt b/designcompose/src/main/java/com/android/designcompose/DesignFrame.kt index a8840bc7d..a12fa4857 100644 --- a/designcompose/src/main/java/com/android/designcompose/DesignFrame.kt +++ b/designcompose/src/main/java/com/android/designcompose/DesignFrame.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridScope @@ -32,6 +33,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.Offset @@ -147,7 +149,7 @@ internal fun DesignFrame( // annotations set variant properties that match. If so, variantNodeName will be set to the // name of the node with all the variants set to the @DesignVariant parameters val variantNodeName = customizations.getMatchingVariant(componentInfo) - val layoutModifier = Modifier.layoutToModifier(layout).layoutId(layoutId) + val layoutModifier = Modifier.layoutToModifier(layout) var m = Modifier.layoutStyle(name, style).then(layoutModifier) // Only render the frame if we don't have a custom variant node that we are about to // render instead @@ -591,8 +593,10 @@ internal fun DesignFrame( } } is LayoutInfoAbsolute -> { - println("### DesignFrame $name left ${layout?.left} width ${layout?.width}") - Box(layoutInfo.selfModifier.then(m)) { + println("### DesignFrame $name left ${layout?.left} width ${layout?.width} height ${layout?.height}") + val frameModifier = layoutInfo.selfModifier.then(m) + .wrapContentSize(align = Alignment.TopStart, unbounded = true) + Box(frameModifier) { content() } } diff --git a/designcompose/src/main/java/com/android/designcompose/DesignText.kt b/designcompose/src/main/java/com/android/designcompose/DesignText.kt index 5f896a123..bc526fe03 100644 --- a/designcompose/src/main/java/com/android/designcompose/DesignText.kt +++ b/designcompose/src/main/java/com/android/designcompose/DesignText.kt @@ -221,26 +221,6 @@ internal fun DesignText( TextOverflow.Clip else TextOverflow.Ellipsis - /* - val layoutModifier = - when (viewLayoutInfo) { - is LayoutInfoAbsolute -> viewLayoutInfo.marginModifier - is LayoutInfoRow -> viewLayoutInfo.marginModifier - is LayoutInfoColumn -> viewLayoutInfo.marginModifier - is LayoutInfoGrid -> viewLayoutInfo.marginModifier - else -> Modifier - } - - DesignTextLayout( - layoutModifier.textTransform(style).layoutStyle(annotatedText.text, style).clipToBounds(), - style, - TextLayoutData(annotatedText, textStyle, LocalFontLoader.current), - annotatedText.text - ) - */ - // Box(modifier = layoutModifier.textTransform(style).layoutStyle(annotatedText.text, - // style).clipToBounds()) { - val textLayoutData = TextLayoutData(annotatedText, textStyle, LocalFontLoader.current) val maxLines = if (style.line_count.isPresent) style.line_count.get().toInt() else Int.MAX_VALUE val textMeasureData = TextMeasureData(textLayoutData, density, maxLines) @@ -317,7 +297,6 @@ internal fun DesignText( // if the parent frame does not clip contents. val layoutModifier = Modifier .layoutToModifier(layout) - .layoutId(layoutId) .wrapContentSize(align = Alignment.TopStart, unbounded = true) .textTransform(style) // TODO needed? .clipToBounds() // TODO needed? diff --git a/designcompose/src/main/java/com/android/designcompose/FrameRender.kt b/designcompose/src/main/java/com/android/designcompose/FrameRender.kt index 370f32616..12a0b5768 100644 --- a/designcompose/src/main/java/com/android/designcompose/FrameRender.kt +++ b/designcompose/src/main/java/com/android/designcompose/FrameRender.kt @@ -42,7 +42,6 @@ import androidx.core.graphics.plus import com.android.designcompose.serdegen.ArcMeterData import com.android.designcompose.serdegen.BoxShadow import com.android.designcompose.serdegen.Dimension -import com.android.designcompose.serdegen.Layout import com.android.designcompose.serdegen.MeterData import com.android.designcompose.serdegen.Overflow import com.android.designcompose.serdegen.ProgressBarMeterData @@ -73,9 +72,9 @@ private fun calculateParentOffsets( val decomposed = style.transform.decompose(density) // X Node position offset by the X translation value of the transform matrix - val nodeX = style.left.pointsAsDp().value.toDouble() + decomposed.translateX + val nodeX = style.margin.start.pointsAsDp().value.toDouble() + decomposed.translateX // Y Node position offset by the Y translation value of the transform matrix - val nodeY = style.top.pointsAsDp().value.toDouble() + decomposed.translateY + val nodeY = style.margin.top.pointsAsDp().value.toDouble() + decomposed.translateY // Radius of the circle encapsulating the node val r = sqrt(nodeWidth * nodeWidth + nodeHeight * nodeHeight) / 2 @@ -315,7 +314,6 @@ private fun calculateRotationData( rotationData: RotationMeterData, meterValue: Float, style: ViewStyle, - frameSize: Size, density: Float ): androidx.compose.ui.graphics.Matrix? { var overrideTransform: androidx.compose.ui.graphics.Matrix? = null @@ -323,12 +321,15 @@ private fun calculateRotationData( (rotationData.start + meterValue / 100f * (rotationData.end - rotationData.start)) .coerceDiscrete(rotationData.discrete, rotationData.discreteValue) + val nodeWidth = style.width.pointsAsDp().value + val nodeHeight = style.height.pointsAsDp().value + // Calculate offsets from parent when the rotation is 0 val offsets = calculateParentOffsets( style, - frameSize.width.toDouble(), - frameSize.height.toDouble(), + nodeWidth.toDouble(), + nodeHeight.toDouble(), density ) val xOffsetParent = offsets.first @@ -337,8 +338,8 @@ private fun calculateRotationData( // Calculate a rotation transform that rotates about the center of the // node and then moves by xOffset and yOffset overrideTransform = androidx.compose.ui.graphics.Matrix() - val moveX = frameSize.width / 2 - val moveY = frameSize.height / 2 + val moveX = nodeWidth / 2 + val moveY = nodeHeight / 2 // First translate so we rotate about the center val translateOrigin = androidx.compose.ui.graphics.Matrix() @@ -353,8 +354,8 @@ private fun calculateRotationData( // Translate back, with an additional offset from the parent val translateBack = androidx.compose.ui.graphics.Matrix() translateBack.translate( - moveX - style.left.pointsAsDp().value + xOffsetParent.toFloat(), - moveY - style.top.pointsAsDp().value + yOffsetParent.toFloat(), + moveX - style.margin.start.pointsAsDp().value + xOffsetParent.toFloat(), + moveY - style.margin.top.pointsAsDp().value + yOffsetParent.toFloat(), 0f ) overrideTransform.timesAssign(translateBack) @@ -364,7 +365,7 @@ private fun calculateRotationData( private fun calculateProgressBarData( progressBarData: ProgressBarMeterData, meterValue: Float, - size: Size, + height: Float, density: Float ): Size { // Progress bar discrete values are done by percentage @@ -373,7 +374,7 @@ private fun calculateProgressBarData( // Resize the progress bar by interpolating between 0 and endX val barWidth = lerp(0F, progressBarData.endX, meterValue, density) - return Size(barWidth, size.height) + return Size(barWidth, height) } private fun calculateProgressMarkerData( @@ -389,7 +390,7 @@ private fun calculateProgressMarkerData( // along the x axis val moveX = lerp(markerData.startX, markerData.endX, meterValue, density) var overrideTransform = style.getTransform(density) - val leftOffset = style.left.pointsAsDp().value + val leftOffset = style.margin.start.pointsAsDp().value overrideTransform.setXTranslation(moveX - leftOffset) return overrideTransform @@ -449,7 +450,7 @@ internal fun ContentDrawScope.render( drawContext.canvas.save() var overrideTransform: androidx.compose.ui.graphics.Matrix? = null - var frameSize = size + var frameSize: Size? = null var shape = frameShape var customArcAngle = false @@ -466,7 +467,6 @@ internal fun ContentDrawScope.render( rotationData, meterValue, style, - frameSize, density ) } @@ -478,7 +478,7 @@ internal fun ContentDrawScope.render( calculateProgressBarData( progressBarData, meterValue, - frameSize, + style.height.pointsAsDp().value, density ) } @@ -531,7 +531,7 @@ internal fun ContentDrawScope.render( val paint = Paint() paint.alpha = opacity paint.blendMode = blendMode - drawContext.canvas.saveLayer(Rect(Offset.Zero, frameSize), paint) + drawContext.canvas.saveLayer(Rect(Offset.Zero, size), paint) } fun getPaths( @@ -539,19 +539,27 @@ internal fun ContentDrawScope.render( stroke: List, vectorSize: Optional>, ): Pair, List> { - // If we have a vector size different than the frameSize, then constraints have caused the - // container frame to resize. We then check our style.left and style.top attributes and if - // they are of type Dimension.Percent, we know that the vector should scale. Use the vector - // size and frameSize to calculate the scaling factor. + // If we have a vector size different than the frameSize, then constraints or a transform + // have caused the container frame to resize. We then check if constraints caused the resize + // by checking the style's left/right/top/bottom attributes. If left/right are both Percent, + // the vector should scale horizontally. If top/bottom are both Percent, the vector should + // scale vertically. Use the vector size and frameSize to calculate the scaling factor. + // We don't yet support constraint scaled and transformed vectors. var scaleX = 1F var scaleY = 1F vectorSize.ifPresent { val sizeList = vectorSize.get() if (sizeList.size == 2) { - val vecWidth = sizeList[0] * vectorScaleX - val vecHeight = sizeList[1] * vectorScaleY - if (style.left is Dimension.Percent) scaleX = frameSize.width / vecWidth - if (style.top is Dimension.Percent) scaleY = frameSize.height / vecHeight + val scaleHorizontal = (style.left is Dimension.Percent && style.right is Dimension.Percent) + val scaleVertical = (style.top is Dimension.Percent && style.bottom is Dimension.Percent) + if (scaleHorizontal) { + val vecWidth = sizeList[0] * vectorScaleX + scaleX = size.width / vecWidth + } + if (scaleVertical) { + val vecHeight = sizeList[1] * vectorScaleY + scaleY = size.height / vecHeight + } } } return Pair( @@ -559,17 +567,32 @@ internal fun ContentDrawScope.render( stroke.map { path -> path.asPath(density, scaleX, scaleY) } ) } + fun getRectSize(frameSize: Size?, style: ViewStyle): Size { + val width = + frameSize?.width + ?: if (style.width is Dimension.Points) + (style.width as Dimension.Points).value + else + size.width + val height = + frameSize?.height + ?: if (style.height is Dimension.Points) + (style.height as Dimension.Points).value + else + size.height + return Size(width, height) + } // Fill then stroke. val (fills, precomputedStrokes) = when (shape) { is ViewShape.RoundRect -> { - computeRoundedRect(frameSize, shape.corner_radius, density) + computeRoundedRect(getRectSize(frameSize, style), shape.corner_radius, density) } is ViewShape.Path -> { getPaths(shape.path, shape.stroke, shape.size) } is ViewShape.VectorRect -> { - computeRoundedRect(frameSize, shape.corner_radius, density) + computeRoundedRect(getRectSize(frameSize, style), shape.corner_radius, density) } is ViewShape.Arc -> { if (!customArcAngle) { @@ -580,15 +603,16 @@ internal fun ContentDrawScope.render( // the path provided by Figma. Instead, we construct our own path given // the arc parameters if (shape.inner_radius < 1.0F) { - computeArcPath(frameSize, shape) + computeArcPath(size, shape) } else { - computeArcStrokePath(frameSize, shape, style, density) + computeArcStrokePath(size, shape, style, density) } } } else -> { val path = Path() - path.addRect(Rect(0.0f, 0.0f, frameSize.width, frameSize.height)) + val size = getRectSize(frameSize, style) + path.addRect(Rect(0.0f, 0.0f, size.width, size.height)) Pair(listOf(path), listOf()) } } @@ -598,7 +622,7 @@ internal fun ContentDrawScope.render( val b = background.asBrush(document, density) if (b != null) { val (brush, opacity) = b - brush.applyTo(frameSize, p, opacity) + brush.applyTo(size, p, opacity) p } else { null @@ -610,7 +634,7 @@ internal fun ContentDrawScope.render( val b = background.asBrush(document, density) if (b != null) { val (brush, opacity) = b - brush.applyTo(frameSize, p, opacity) + brush.applyTo(size, p, opacity) p } else { null @@ -726,7 +750,7 @@ internal fun ContentDrawScope.render( } drawImage( customImage.asImageBitmap(), - dstSize = IntSize(frameSize.width.roundToInt(), frameSize.height.roundToInt()) + dstSize = IntSize(size.width.roundToInt(), size.height.roundToInt()) ) drawContext.canvas.restore() } else { diff --git a/designcompose/src/main/java/com/android/designcompose/Layout.kt b/designcompose/src/main/java/com/android/designcompose/Layout.kt index 0d4d03972..3a7969dcf 100644 --- a/designcompose/src/main/java/com/android/designcompose/Layout.kt +++ b/designcompose/src/main/java/com/android/designcompose/Layout.kt @@ -93,9 +93,9 @@ internal object LayoutManager { val serializedView = serializer._bytes.toUByteArray().asByteArray() var serializedVariantView = ByteArray(0) if (variantView != null) { - val serializer = BincodeSerializer() - variantView.serialize(serializer) - serializedVariantView = serializer._bytes.toUByteArray().asByteArray() + val variantSerializer = BincodeSerializer() + variantView.serialize(variantSerializer) + serializedVariantView = variantSerializer._bytes.toUByteArray().asByteArray() } val responseBytes = LiveUpdateJni.jniAddNode( diff --git a/designcompose/src/main/java/com/android/designcompose/Utils.kt b/designcompose/src/main/java/com/android/designcompose/Utils.kt index 6ee848e5e..d011d307f 100644 --- a/designcompose/src/main/java/com/android/designcompose/Utils.kt +++ b/designcompose/src/main/java/com/android/designcompose/Utils.kt @@ -457,9 +457,9 @@ internal fun mergeStyles(base: ViewStyle, override: ViewStyle): ViewStyle { } fun com.android.designcompose.serdegen.Rect.isDefault(): Boolean { return this.start is Dimension.Undefined && - this.end is Dimension.Undefined && - this.top is Dimension.Undefined && - this.bottom is Dimension.Undefined + this.end is Dimension.Undefined && + this.top is Dimension.Undefined && + this.bottom is Dimension.Undefined } style.margin = if (!override.margin.isDefault()) { @@ -506,6 +506,12 @@ internal fun mergeStyles(base: ViewStyle, override: ViewStyle): ViewStyle { } else { base.flex_basis } + style.bounding_box = + if (override.bounding_box.width != 0.0f || override.bounding_box.height != 0.0f) { + override.bounding_box + } else { + base.bounding_box + } style.width = if (override.width !is Dimension.Undefined) { override.width @@ -708,7 +714,6 @@ internal constructor( if (center.y == Float.POSITIVE_INFINITY) size.height else center.y * size.height } - println("### radiusX $radiusX width ${size.width} minDim ${size.minDimension}") val shader = RadialGradientShader( colors = colors, diff --git a/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/MainActivity.kt b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/MainActivity.kt index 11cf15d57..f1f745d34 100644 --- a/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/MainActivity.kt +++ b/integration-tests/validation/src/main/java/com/android/designcompose/testapp/validation/MainActivity.kt @@ -184,67 +184,6 @@ interface HelloWorld { @DesignComponent(node = "#RedSquare") fun RedSquare() } - -internal fun getLayout(id: Int): Layout { - return when (id) { - 0 -> Layout(0, 400F, 400F, 0F, 0F) - 1 -> Layout(0, 200F, 200F, 10F, 10F) - 2 -> Layout(0, 200F, 200F, 10F, 10F) - else -> Layout(0, 50F, 50F, (id.toFloat() - 3) * 90F - 10, 30F) - } -} - -@Composable -internal inline fun TestLayout( - modifier: Modifier = Modifier, - name: String = "unnamed", - id: Int, - content: @Composable () -> Unit -) { - val measurePolicy = rememberMeasurePolicy(name, id) - Layout(content = content, measurePolicy = measurePolicy, modifier = modifier) -} -@Composable -internal fun rememberMeasurePolicy(name: String, id: Int) = - remember(name, id) { measurePolicy(name, id) } - -internal fun measurePolicy(name: String, id: Int) = - MeasurePolicy { measurables, constraints -> - val layout = getLayout(id) - val placeableList = arrayListOf>() - var selfWidth = 0 - var selfHeight = 0 - println("### MeasurePolicy $name") - measurables.forEachIndexed { index, measurable -> - val childId = measurable.layoutId as Int - val placeable = measurable.measure(constraints) - val childLayout = getLayout(childId) - - println("### $name measureable $index: ${measurable.layoutId} width ${placeable.width}") - //val childMaxX = childLayout.left.roundToInt() + childLayout.width.roundToInt() - val childMaxWidth = childLayout.left.roundToInt() + placeable.width - val childMaxHeight = childLayout.top.roundToInt() + placeable.height - if (selfWidth < childMaxWidth) - selfWidth = childMaxWidth - if (selfHeight < childMaxHeight) - selfHeight = childMaxHeight - - placeableList.add(Pair(placeable, Point(childLayout.left.roundToInt(), childLayout.top.roundToInt()))) - } - - println("### MeasurePolicy $name selfWidth $selfWidth selfHeight $selfHeight") - - layout(selfWidth, selfHeight) { - placeableList.forEachIndexed { index, placeable -> - placeable.first.place(placeable.second.x, placeable.second.y) - println("### $name place $index ${placeable.second.x}, ${placeable.second.y}") - } - } - } - - - - @Composable fun HelloWorld() { val loremText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." @@ -255,22 +194,6 @@ fun HelloWorld() { val (buttonSquare, setButtonSquare) = remember { mutableStateOf(ButtonSquare.Off) } val (numChildren, setNumChildren) = remember { mutableStateOf(2) } - - /* - TestLayout( - modifier = Modifier.layoutId(0), - name = "Main", - id = 0, - ) { - Box(Modifier.size(200.dp, 200.dp).border(1.dp, Color.Black).layoutId(1)) - TestLayout(modifier = Modifier.layoutId(1), id = 1, name = "Box") { - Box(Modifier.size(50.dp, 50.dp).background(Color.Red).layoutId(3)) - Box(Modifier.size(50.dp, 50.dp).background(Color.Green).layoutId(4)) - Box(Modifier.size(50.dp, 50.dp).background(Color.Blue).border(2.dp, Color.Magenta).layoutId(5)) - } - } - */ - HelloWorldDoc.Main( name = "LongerText", nameAutoWidth = loremText.subSequence(0, autoWidthLen).toString(),