Skip to content

Commit

Permalink
Fix text layouts
Browse files Browse the repository at this point in the history
For auto width text fields, set their viewstyle's width and height to auto. The width needs to be auto so that it can expand. The height needs to be auto because Compose's text rendering is slightly bigger than Figma's so if it isn't auto it would get cut off.

For auto height with a fill container width text field, set the viewstyle's width and height to auto, and also set the flex_grow field to 1. This is what differentiates this type of text layout with just auto width.

Fix an issue where text would get clipped by its parent frame, even if the parent frame did not have clip contents set. To fix this, the Box() parent of DesignText() needs to have wrapContentSize(align = Alignment.TopStart, unbounded = true) set in its Modifier.
  • Loading branch information
rylin8 committed Aug 26, 2023
1 parent 03a5a75 commit 9a5a81c
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 215 deletions.
20 changes: 17 additions & 3 deletions crates/figma_import/src/bin/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use clap::Parser;
use figma_import::{
add_view, add_view_measure, print_layout, remove_view, set_node_size,
toolkit_schema::View, Document, NodeQuery, ProxyConfig, SerializedDesignDoc, ViewData,
toolkit_layout_style::Dimension,
};
use std::io;
use std::io::prelude::*;
Expand Down Expand Up @@ -100,7 +101,19 @@ fn test_layout(view: &View, id: &mut i32, parent_layout_id: i32, child_index: i3
let my_id: i32 = id.clone();
*id = *id + 1;
if let ViewData::Text { content: _ } = &view.data {
add_view_measure(my_id, parent_layout_id, child_index, view.clone(), measure_func);
let mut use_measure_func = false;
if let Dimension::Auto = view.style.width {
if let Dimension::Auto = view.style.height {
if view.style.flex_grow > 0.0 {
use_measure_func = true;
}
}
}
if use_measure_func {
add_view_measure(my_id, parent_layout_id, child_index, view.clone(), measure_func);
} else {
add_view(my_id, parent_layout_id, child_index, view.clone(), None);
}
//taffy::node::MeasureFunc::Raw(measure_func));
} else if let ViewData::Container { shape: _, children } = &view.data {
add_view(my_id, parent_layout_id, child_index, view.clone(), None);
Expand Down Expand Up @@ -155,8 +168,9 @@ fn fetch_impl(args: Args) -> Result<(), ConvertError> {
}
}

println!("### SetNodeSize 400, 65");
set_node_size(1, 400, 65);
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
Expand Down
6 changes: 6 additions & 0 deletions crates/figma_import/src/figma_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,12 @@ impl Node {
_ => false,
}
}
pub fn is_text(&self) -> bool {
match &self.data {
NodeData::Text {..} => true,
_ => false,
}
}
pub fn constraints(&self) -> Option<&LayoutConstraint> {
if let Some(frame) = self.frame() {
Some(&frame.constraints)
Expand Down
14 changes: 7 additions & 7 deletions crates/figma_import/src/transform_flexbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::figma_schema::{TextAutoResize, TextTruncation};
use crate::vector_schema;
use crate::{
component_context::ComponentContext,
extended_layout_schema::{ExtendedAutoLayout, ExtendedTextLayout, LayoutType, SizePolicy},
extended_layout_schema::{ExtendedAutoLayout, LayoutType, SizePolicy}, //ExtendedTextLayout
figma_schema::{
BlendMode, Component, ComponentSet, EffectType, HorizontalLayoutConstraintValue,
LayoutAlign, LayoutAlignItems, LayoutMode, LayoutSizing, LayoutSizingMode, LineHeightUnit,
Expand Down Expand Up @@ -308,15 +308,15 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle {
style.left = Dimension::Percent(0.0);
style.right = Dimension::Auto;
style.margin.start = Dimension::Points(left);
if !hug_width {
if !hug_width && !node.is_text() {
style.width = Dimension::Points(width);
}
}
Some(HorizontalLayoutConstraintValue::Right) => {
style.left = Dimension::Auto;
style.right = Dimension::Percent(0.0);
style.margin.end = Dimension::Points(right);
if !hug_width {
if !hug_width && !node.is_text() {
style.width = Dimension::Points(width);
}
}
Expand All @@ -338,7 +338,7 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle {
style.right = Dimension::Auto;
style.margin.start =
Dimension::Points(left - parent_bounds.width().ceil() / 2.0);
if !hug_width {
if !hug_width && !node.is_text() {
style.width = Dimension::Points(width);
}
}
Expand All @@ -354,15 +354,15 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle {
style.top = Dimension::Percent(0.0);
style.bottom = Dimension::Auto;
style.margin.top = Dimension::Points(top);
if !hug_height {
if !hug_height && !node.is_text() {
style.height = Dimension::Points(height);
}
}
Some(VerticalLayoutConstraintValue::Bottom) => {
style.top = Dimension::Auto;
style.bottom = Dimension::Percent(0.0);
style.margin.bottom = Dimension::Points(bottom);
if !hug_height {
if !hug_height && !node.is_text() {
style.height = Dimension::Points(height);
}
}
Expand All @@ -378,7 +378,7 @@ fn compute_layout(node: &Node, parent: Option<&Node>) -> ViewStyle {
style.bottom = Dimension::Auto;
style.margin.top =
Dimension::Points(top - parent_bounds.height().ceil() / 2.0);
if !hug_height {
if !hug_height && !node.is_text() {
style.height = Dimension::Points(height);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
Expand All @@ -38,6 +40,7 @@ import androidx.compose.ui.geometry.toRect
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.withSaveLayer
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.Density
Expand All @@ -47,27 +50,32 @@ import com.android.designcompose.serdegen.ComponentInfo
import com.android.designcompose.serdegen.GridLayoutType
import com.android.designcompose.serdegen.GridSpan
import com.android.designcompose.serdegen.ItemSpacing
import com.android.designcompose.serdegen.Layout
import com.android.designcompose.serdegen.NodeQuery
import com.android.designcompose.serdegen.View
import com.android.designcompose.serdegen.ViewData
import com.android.designcompose.serdegen.ViewShape
import com.android.designcompose.serdegen.ViewStyle
import java.util.Optional

@Composable
internal fun DesignFrame(
modifier: Modifier = Modifier,
layoutModifier: Modifier,
view: View,
baseVariantView: View?,
style: ViewStyle,
shape: ViewShape,
name: String,
variantParentName: String,
layoutInfo: SimplifiedLayoutInfo,
document: DocContent,
customizations: CustomizationContext,
parentLayout: ParentLayoutInfo?,
layoutId: Int,
componentInfo: Optional<ComponentInfo>,
parentComponents: List<ParentComponentInfo>,
maskInfo: MaskInfo?,
content: @Composable (parentLayoutInfo: ParentLayoutInfo) -> Unit,
content: @Composable () -> Unit,
) {
val name = view.name
if (!customizations.getVisible(name)) return

// Check for an image customization with context. If it exists, call the custom image function
Expand Down Expand Up @@ -97,10 +105,49 @@ internal fun DesignFrame(
val meterValue = customizations.getMeterFunction(name)?.let { it() }
meterValue?.let { customizations.setMeterValue(name, it) }

val shape = (view.data as ViewData.Container).shape

// Get the layout for this view that describes its size and position.
val (layout, setLayout) = remember { mutableStateOf<Layout?>(null) }
// Keep track of the layout state, which changes whenever this view's layout changes
val (layoutState, setLayoutState) = remember { mutableStateOf(0) }
// Subscribe for layout changes whenever the view changes. The view can change if it is a
// component instance that changes to another variant. It can also change due to a live update.
// Subscribing when already subscribed simply updates the view in layout system.
// TODO: The new variant renders for a frame with the old layout, causing a twitch when
// variants change. Fix this by somehow waiting for the new layout before rendering.
LaunchedEffect(view) {
val parentLayoutId = parentLayout?.parentLayoutId ?: -1
val childIndex = parentLayout?.childIndex ?: -1

println("### subscribe FRAME ${view.name} layoutId $layoutId parent $parentLayoutId index $childIndex")
LayoutManager.subscribe(
layoutId,
setLayoutState,
parentLayoutId,
childIndex,
view,
baseVariantView
)
}
// Unsubscribe to layout changes when the composable is no longer in view.
DisposableEffect(Unit) {
onDispose {
println("### unsubscribe ${view.name} $layoutId")
LayoutManager.unsubscribe(layoutId)
}
}
LaunchedEffect(layoutState) {
val newLayout = LayoutManager.getLayout(layoutId)
//println("### setLayout $layoutState ${v.name} layoutId $layoutId => width ${newLayout?.width} height ${newLayout?.height} x ${newLayout?.left} y ${newLayout?.top}")
setLayout(newLayout)
}

// Check to see if this node is part of a component set with variants and if any @DesignVariant
// 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)
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
Expand All @@ -124,9 +171,7 @@ internal fun DesignFrame(
override val appearanceModifier = m
@Composable
override fun Content() {
content(
ParentLayoutInfo(0, 0)
) // TODO fix. Maybe move this replacement into DesignView
content()
}
override val textStyle: TextStyle? = null
}
Expand Down Expand Up @@ -197,7 +242,7 @@ internal fun DesignFrame(
horizontalArrangement = layoutInfo.arrangement,
verticalAlignment = layoutInfo.alignment
) {
content(ParentLayoutInfo(0, 0)) // TODO
content()
}
}
}
Expand Down Expand Up @@ -241,7 +286,7 @@ internal fun DesignFrame(
verticalArrangement = layoutInfo.arrangement,
horizontalAlignment = layoutInfo.alignment
) {
content(ParentLayoutInfo(0, 0)) // TODO
content()
}
}
}
Expand Down Expand Up @@ -546,15 +591,10 @@ internal fun DesignFrame(
}
}
is LayoutInfoAbsolute -> {
// TODO call JNI function to get layout here??
Box(modifier = layoutInfo.selfModifier.then(m)) {
content(ParentLayoutInfo(0, 0)) // TODO fix??
}
/*
DesignLayout(modifier = layoutInfo.selfModifier.then(m), style = style, layout = layout, name = name) {
content(absoluteParentLayoutInfo)
println("### DesignFrame $name left ${layout?.left} width ${layout?.width}")
Box(layoutInfo.selfModifier.then(m)) {
content()
}
*/
}
}
}
Expand Down
Loading

0 comments on commit 9a5a81c

Please sign in to comment.