// =============================================================================
// Paint Model Package - Visual properties and paint node types
// =============================================================================
///|
pub(all) struct PaintProperties {
z_index : @style.ZIndex
visibility : @style.Visibility
pointer_events : @style.PointerEvents
opacity : Double
color : @types.Color
background_color : @types.Color
background_image : @types.BackgroundImage
box_shadows : Array[@style.BoxShadow]
font_size : Double
line_height : Double
letter_spacing : Double
word_spacing : Double
font_weight : Double
is_bold : Bool
font_family : String
clip_path : @style.ClipPath
border_top_left_radius : Double
border_top_right_radius : Double
border_bottom_right_radius : Double
border_bottom_left_radius : Double
overflow_x : @types.Overflow
overflow_y : @types.Overflow
text_decoration_underline : Bool
text_decoration_line_through : Bool
text_decoration_overline : Bool
border_top_width : Double
border_right_width : Double
border_bottom_width : Double
border_left_width : Double
border_top_color : @types.Color
border_right_color : @types.Color
border_bottom_color : @types.Color
border_left_color : @types.Color
}
///|
pub fn PaintProperties::default() -> PaintProperties {
{
z_index: @style.ZIndex::Auto,
visibility: @style.Visibility::Visible,
pointer_events: @style.PointerEvents::Auto,
opacity: 1.0,
color: @types.Color::black(),
background_color: @types.Color::transparent(),
background_image: @types.None,
box_shadows: [],
font_size: 16.0,
line_height: 16.0,
letter_spacing: 0.0,
word_spacing: 0.0,
font_weight: 400.0,
is_bold: false,
font_family: "",
clip_path: @style.ClipPath::none(),
border_top_left_radius: 0.0,
border_top_right_radius: 0.0,
border_bottom_right_radius: 0.0,
border_bottom_left_radius: 0.0,
overflow_x: @types.Visible,
overflow_y: @types.Visible,
text_decoration_underline: false,
text_decoration_line_through: false,
text_decoration_overline: false,
border_top_width: 0.0,
border_right_width: 0.0,
border_bottom_width: 0.0,
border_left_width: 0.0,
border_top_color: @types.Color::transparent(),
border_right_color: @types.Color::transparent(),
border_bottom_color: @types.Color::transparent(),
border_left_color: @types.Color::transparent(),
}
}
///|
pub fn PaintProperties::from_style(style : @style.Style) -> PaintProperties {
{
z_index: style.z_index,
visibility: style.visibility,
pointer_events: style.pointer_events,
opacity: style.opacity,
color: style.color,
background_color: style.background_color,
background_image: style.background_image,
box_shadows: style.box_shadows,
font_size: style.font_size,
line_height: style.line_height,
letter_spacing: style.letter_spacing,
word_spacing: style.word_spacing,
font_weight: style.font_weight,
is_bold: is_bold_weight(style.font_weight),
font_family: style.font_family,
clip_path: style.clip_path,
border_top_left_radius: style.border_top_left_radius,
border_top_right_radius: style.border_top_right_radius,
border_bottom_right_radius: style.border_bottom_right_radius,
border_bottom_left_radius: style.border_bottom_left_radius,
overflow_x: style.overflow_x,
overflow_y: style.overflow_y,
text_decoration_underline: style.text_decoration_underline,
text_decoration_line_through: style.text_decoration_line_through,
text_decoration_overline: style.text_decoration_overline,
border_top_width: 0.0,
border_right_width: 0.0,
border_bottom_width: 0.0,
border_left_width: 0.0,
border_top_color: style.border_color.top,
border_right_color: style.border_color.right,
border_bottom_color: style.border_color.bottom,
border_left_color: style.border_color.left,
}
}
///|
pub impl Show for PaintProperties with fn output(self, logger) {
logger.write_string("PaintProperties { z_index: ")
self.z_index.output(logger)
logger.write_string(", visibility: ")
self.visibility.output(logger)
logger.write_string(", opacity: ")
self.opacity.output(logger)
logger.write_string(", pointer_events: ")
self.pointer_events.output(logger)
logger.write_string(", color: ")
self.color.output(logger)
logger.write_string(", background_color: ")
self.background_color.output(logger)
logger.write_string(" }")
}
///|
pub fn PaintProperties::clips_overflow(self : PaintProperties) -> Bool {
match (self.overflow_x, self.overflow_y) {
(@types.Visible, @types.Visible) => false
_ => true
}
}
///|
pub fn PaintProperties::should_render(self : PaintProperties) -> Bool {
match self.visibility {
@style.Visibility::Visible => self.opacity > 0.0
_ => false
}
}
///|
pub fn PaintProperties::z_index_value(self : PaintProperties) -> Int {
match self.z_index {
@style.ZIndex::Auto => 0
@style.ZIndex::Value(v) => v
}
}
///|
fn is_bold_weight(weight : Double) -> Bool {
weight >= 600.0
}
///|
pub(all) struct PaintNode {
id : String
tag : String
x : Double
y : Double
width : Double
height : Double
overflow_x : @types.Overflow
overflow_y : @types.Overflow
scroll_width : Double
scroll_height : Double
clip : @types.ClipRect
paint : PaintProperties
stacking_order : Int
text : String?
src : String?
visual_polygon : Array[(Double, Double)] // local points relative to node x/y; empty means rect
children : Array[PaintNode]
}
///|
pub fn PaintNode::new(
id : String,
tag : String,
x : Double,
y : Double,
width : Double,
height : Double,
paint : PaintProperties,
children : Array[PaintNode],
text? : String? = None,
src? : String? = None,
overflow_x? : @types.Overflow = @types.Visible,
overflow_y? : @types.Overflow = @types.Visible,
scroll_width? : Double = 0.0,
scroll_height? : Double = 0.0,
clip? : @types.ClipRect = @types.ClipRect::Auto,
visual_polygon? : Array[(Double, Double)] = [],
) -> PaintNode {
{
id,
tag,
x,
y,
width,
height,
overflow_x,
overflow_y,
scroll_width: if scroll_width == 0.0 {
width
} else {
scroll_width
},
scroll_height: if scroll_height == 0.0 {
height
} else {
scroll_height
},
clip,
paint,
stacking_order: paint.z_index_value(),
text,
src,
visual_polygon,
children,
}
}
///|
pub impl Show for PaintNode with fn output(self, logger) {
logger.write_string(self.id)
logger.write_string(" (")
self.x.output(logger)
logger.write_string(", ")
self.y.output(logger)
logger.write_string(") ")
self.width.output(logger)
logger.write_string("x")
self.height.output(logger)
if self.stacking_order != 0 {
logger.write_string(" z:")
self.stacking_order.output(logger)
}
if !self.paint.should_render() {
logger.write_string(" [hidden]")
}
if self.paint.opacity < 1.0 {
logger.write_string(" opacity:")
self.paint.opacity.output(logger)
}
if self.clips_content() {
logger.write_string(" [clips]")
}
}
///|
pub fn PaintNode::clips_content(self : PaintNode) -> Bool {
let overflow_clips = match (self.overflow_x, self.overflow_y) {
(@types.Visible, @types.Visible) => false
_ => true
}
let clip_clips = match self.clip {
@types.ClipRect::Auto => false
@types.ClipRect::Rect(_) => true
}
overflow_clips || clip_clips
}
///|
pub fn PaintNode::has_horizontal_scrollbar(self : PaintNode) -> Bool {
match self.overflow_x {
@types.Overflow::Scroll => true
@types.Overflow::Auto => self.scroll_width > self.width
_ => false
}
}
///|
pub fn PaintNode::has_vertical_scrollbar(self : PaintNode) -> Bool {
match self.overflow_y {
@types.Overflow::Scroll => true
@types.Overflow::Auto => self.scroll_height > self.height
_ => false
}
}
///|
pub fn PaintNode::is_scrollable(self : PaintNode) -> Bool {
self.has_horizontal_scrollbar() || self.has_vertical_scrollbar()
}
///|
pub fn PaintNode::get_max_scroll(self : PaintNode) -> (Double, Double) {
let max_x = if self.scroll_width > self.width {
self.scroll_width - self.width
} else {
0.0
}
let max_y = if self.scroll_height > self.height {
self.scroll_height - self.height
} else {
0.0
}
(max_x, max_y)
}
///|
pub fn PaintNode::has_legacy_clip(self : PaintNode) -> Bool {
match self.clip {
@types.ClipRect::Auto => false
@types.ClipRect::Rect(_) => true
}
}
///|
pub fn PaintNode::get_legacy_clip(
self : PaintNode,
) -> (Double, Double, Double, Double)? {
match self.clip {
@types.ClipRect::Auto => None
@types.ClipRect::Rect(top~, right~, bottom~, left~) =>
Some((top, right, bottom, left))
}
}
///|
pub fn PaintNode::get_clip_rect(
self : PaintNode,
) -> (Double, Double, Double, Double)? {
match self.clip {
@types.ClipRect::Rect(top~, right~, bottom~, left~) => {
let clip_x = self.x + left
let clip_y = self.y + top
let clip_width = right - left
let clip_height = bottom - top
Some((clip_x, clip_y, clip_width, clip_height))
}
@types.ClipRect::Auto =>
match (self.overflow_x, self.overflow_y) {
(@types.Visible, @types.Visible) => None
_ => Some((self.x, self.y, self.width, self.height))
}
}
}