///|
fn render_modified_node(
element : ElementNode,
data : ModifiedViewSpec,
placed : PlacedNode,
focused : ElementId?,
environment : RenderEnvironment,
children : Array[RenderNode],
commands : Array[DrawCommand],
post_commands : Array[DrawCommand],
) -> Unit {
let bounds = placed.frame
match data.modifier {
PaddingModifier(_) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
FrameModifier(_) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
BackgroundModifier(color) => {
let radius = environment.corner_radius.unwrap_or(0.0)
if radius > 0.0 {
commands.push(
FillRoundedRectBrush(RoundedRect::new(rect=bounds, radius~), color),
)
} else {
commands.push(
FillRoundedRectBrush(RoundedRect::new(rect=bounds), color),
)
}
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
}
ForegroundModifier(color) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
{ ..environment, foreground: Some(color) },
),
)
}
FontModifier(font) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
{ ..environment, font: Some(font) },
),
)
}
CornerRadiusModifier(radius) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
{ ..environment, corner_radius: Some(radius) },
),
)
}
OpacityModifier(opacity) => {
commands.push(PushOpacity(opacity))
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
post_commands.push(PopOpacity)
}
ShadowModifier(style) => {
commands.push(
DrawShadow(
shadow_style_to_spec(
style,
bounds,
environment.corner_radius.unwrap_or(0.0),
),
),
)
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
}
BorderModifier(style) => {
let radius = environment.corner_radius.unwrap_or(0.0)
if style.width > 0.0 {
commands.push(
StrokeRoundedRectBrush(
RoundedRect::new(rect=bounds, radius~),
style.brush,
style.width,
),
)
}
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
}
OffsetModifier(_, _) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
ClipModifier => {
commands.push(PushClip(bounds))
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
post_commands.push(PopClip)
}
ScaleModifier(sx, sy) => {
commands.push(PushTransform(Transform2D::scale(sx~, sy~)))
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
post_commands.push(PopTransform)
}
DisabledModifier(disabled) => {
let child_environment = { ..environment, enabled: !disabled }
if disabled {
commands.push(PushOpacity(0.42))
}
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
child_environment,
),
)
}
if disabled {
post_commands.push(PopOpacity)
}
}
AccessibilityLabelModifier(_)
| SemanticsRoleModifier(_)
| FocusableModifier(_)
| OnTapModifier(_)
| OnFileDropModifier(_)
| KeyboardShortcutModifier(_)
| FlexibleModifier(_)
| LayoutPriorityModifier(_)
| IntrinsicWidthModifier
| IntrinsicHeightModifier =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
AlignModifier(_) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
AspectRatioModifier(_) =>
if element.children.length() > 0 && placed.children.length() > 0 {
children.push(
element.children[0].render_with_environment(
placed.children[0],
focused,
environment,
),
)
}
}
}
///|
fn ElementNode::intrinsic_size(self : ElementNode) -> Size {
if !self.dirty.needs_layout {
match self.layout_cache.cached_intrinsic_size {
Some(size) => return size
None => ()
}
match self.layout_cache.cached_measured {
Some(measured) => return measured.size
None => ()
}
}
let size = match self.spec {
EmptySpec => Size::new(width=0.0, height=0.0)
TextSpec(data) => data.size
ButtonSpec(data) => data.size
TextFieldSpec(data) => data.size
RichTextEditorSpec(data) => data.size
ImageSpec(data) => data.size.unwrap_or(Size::new(width=0.0, height=0.0))
CheckboxSpec(data) => checkbox_intrinsic_size(data)
PickerSpec(_) => Size::new(width=200.0, height=40.0)
DatePickerSpec(_) => Size::new(width=200.0, height=40.0)
SearchBarSpec(_) => Size::new(width=240.0, height=40.0)
SheetSpec(_data) =>
if self.children.length() > 0 {
// Add padding to child size
self.children[0]
.intrinsic_size()
.inflate(Insets::symmetric(horizontal=16.0, vertical=16.0))
} else {
Size::new(width=240.0, height=200.0)
}
SpacerSpec(data) =>
if data.weight > 0.0 {
Size::new(width=0.0, height=0.0)
} else {
Size::new(width=data.min_length, height=data.min_length)
}
PaddingSpec(data) =>
if self.children.length() > 0 {
self.children[0].intrinsic_size().inflate(data.insets)
} else {
Size::new(width=0.0, height=0.0).inflate(data.insets)
}
ContainerSpec(data) =>
match data.size {
Some(size) => size
None =>
if self.children.length() > 0 {
self.children[0].intrinsic_size()
} else {
Size::new(width=0.0, height=0.0)
}
}
FlexSpec(data) => measure_flex_sizes(data.axis, data.spacing, self.children)
FrameSpec(data) => {
let child_size = if self.children.length() > 0 {
self.children[0].intrinsic_size()
} else {
Size::new(width=0.0, height=0.0)
}
resolve_frame_size(data, child_size)
}
StackSpec(_) => measure_stack_size(self.children)
ScrollSpec(data) =>
match data.viewport {
Some(size) => size
None =>
if self.children.length() > 0 {
self.children[0].intrinsic_size()
} else {
Size::new(width=0.0, height=0.0)
}
}
GridSpec(data) =>
measure_grid_size(
data.columns,
data.row_spacing,
data.column_spacing,
self.children,
)
ListSpec(data) => measure_list_size(data.spacing, self.children)
ModifiedSpec(data) => modified_intrinsic_size(data.modifier, self.children)
CustomSpec(data) => (data.measure)(Constraints::unbounded())
CustomLayoutSpec(data) =>
(data.measure)({
constraints: Constraints::unbounded(),
child_sizes: self.children.map(child => child.intrinsic_size()),
child_baselines: self.children.map(child => child.first_baseline()),
child_priorities: self.children.map(child => child.layout_priority()),
child_alignment_guides: custom_alignment_guides(
self.children.map(child => child.intrinsic_size()),
self.children.map(child => child.first_baseline()),
),
cache: data.cache,
cache_key: custom_layout_cache_key(
self.spec,
self.children.map(child => child.intrinsic_size()),
),
layout_direction: LayoutDirection::LeftToRight,
safe_area: Insets::symmetric(),
viewport: None,
})
ComponentSpec(_) =>
if self.children.length() > 0 {
self.children[0].intrinsic_size()
} else {
Size::new(width=0.0, height=0.0)
}
}
self.layout_cache.cached_intrinsic_size = Some(size)
size
}
///|
fn checkbox_intrinsic_size(data : CheckboxViewSpec) -> Size {
let spacing = 8.0
let label_width = text_length(data.label).to_double() * data.font.size * 0.58
Size::new(
width=data.size.width + spacing + label_width,
height=max_double(data.size.height, data.font.size * 1.25),
)
}
///|
fn shadow_style_to_spec(
style : ShadowStyle,
rect : Rect,
radius : Double,
) -> ShadowSpec {
ShadowSpec::new(
rect=RoundedRect::new(rect~, radius~),
color=style.color,
offset=style.offset,
blur_radius=style.blur_radius,
spread=style.spread,
)
}
///|
fn append_checkbox_commands(
commands : Array[DrawCommand],
data : CheckboxViewSpec,
frame : Rect,
environment : RenderEnvironment,
) -> Unit {
let box_side = data.size.width
let box_y = frame.origin.y +
max_double(0.0, (frame.size.height - box_side) / 2.0)
let box_rect = Rect::new(
x=frame.origin.x,
y=box_y,
width=box_side,
height=box_side,
)
let box = RoundedRect::new(rect=box_rect, radius=4.0)
if data.checked.get() {
commands.push(FillRoundedRect(box, data.check_color))
commands.push(
DrawText(
TextRun::new(
text="✓",
frame=box_rect,
font=FontSpec::new(size=box_side * 0.78, weight=700),
color=Color::white(),
),
),
)
} else {
commands.push(StrokeRoundedRect(box, data.check_color, 2.0))
}
commands.push(
DrawText(
TextRun::new(
text=data.label,
frame=Rect::new(
x=frame.origin.x + box_side + 8.0,
y=frame.origin.y,
width=max_double(0.0, frame.size.width - box_side - 8.0),
height=frame.size.height,
),
font=environment_font(environment, data.font),
color=environment_color(environment, data.foreground),
align=TextAlign::TextStart,
),
),
)
}