///|
fn append_rich_text_editor_commands(
commands : Array[DrawCommand],
element : ElementNode,
data : RichTextEditorViewSpec,
frame : Rect,
focused : Bool,
environment : RenderEnvironment,
) -> Unit {
let border = if focused { data.focused_border } else { data.border }
let rounded = RoundedRect::new(rect=frame, radius=data.corner_radius)
commands.push(FillRoundedRectBrush(rounded, data.background))
match border {
Some(border) =>
if border.width > 0.0 {
commands.push(
StrokeRoundedRectBrush(rounded, border.brush, border.width),
)
}
None => ()
}
let text = data.value.get()
let display_text = text_with_composition(text, element, focused)
let content = rich_text_editor_content_rect(frame)
commands.push(PushClip(content))
if display_text == "" {
commands.push(
DrawText(
TextRun::new(
text=data.placeholder,
frame=content,
font=environment.font.unwrap_or(data.font),
color=data.placeholder_color,
align=TextAlign::TextStart,
),
),
)
} else {
let document = (data.format)(display_text)
if focused && element.control.composition is None {
append_rich_text_selection_commands(
commands,
document,
content,
environment.font.unwrap_or(data.font),
environment.text_system,
element.control.selection,
)
}
append_rich_text_blocks(
commands,
document,
content,
environment.font.unwrap_or(data.font),
environment.foreground.unwrap_or(data.foreground),
environment.text_system,
)
}
if focused {
append_rich_text_editor_cursor(
commands,
element,
data,
text,
display_text,
content,
environment.text_system,
)
}
commands.push(PopClip)
}
///|
fn append_rich_text_blocks(
commands : Array[DrawCommand],
document : RichTextDocument,
content : Rect,
base_font : FontSpec,
foreground : Color,
text_system : TextSystem,
) -> Unit {
let mut y = content.origin.y
for block in document.blocks {
let line_height = rich_text_block_line_height(block, base_font)
let block_height = line_height + block.inset.top + block.inset.bottom
if y > content.origin.y + content.size.height {
return
}
let block_frame = Rect::new(
x=content.origin.x,
y~,
width=content.size.width,
height=block_height,
)
match block.background {
Some(background) =>
commands.push(
FillRoundedRectBrush(
RoundedRect::new(rect=block_frame, radius=block.corner_radius),
background,
),
)
None => ()
}
match block.border_left {
Some(border) =>
if border.width > 0.0 {
commands.push(
FillRoundedRectBrush(
RoundedRect::new(
rect=Rect::new(
x=block_frame.origin.x + block.inset.left,
y=block_frame.origin.y,
width=border.width,
height=block_frame.size.height,
),
radius=border.width / 2.0,
),
border.brush,
),
)
}
None => ()
}
if block.rule {
commands.push(
FillRect(
Rect::new(
x=block_frame.origin.x + block.inset.left,
y=block_frame.origin.y + block_height / 2.0,
width=max_double(
0.0,
block_frame.size.width - block.inset.left - block.inset.right,
),
height=1.5,
),
rich_text_block_color(block, foreground).multiply_alpha(0.25),
),
)
} else if block.table is Some(_) {
append_rich_text_block_table(
commands,
block,
Rect::new(
x=block_frame.origin.x + block.inset.left,
y=block_frame.origin.y + block.inset.top,
width=max_double(
0.0,
block_frame.size.width - block.inset.left - block.inset.right,
),
height=line_height,
),
base_font,
foreground,
)
} else {
append_rich_text_block_runs(
commands,
block,
Rect::new(
x=block_frame.origin.x + block.inset.left,
y=block_frame.origin.y + block.inset.top,
width=max_double(
0.0,
block_frame.size.width - block.inset.left - block.inset.right,
),
height=line_height,
),
base_font,
foreground,
text_system,
)
}
y = y + block_height
}
}
///|
fn append_rich_text_block_table(
commands : Array[DrawCommand],
block : RichTextBlock,
frame : Rect,
base_font : FontSpec,
foreground : Color,
) -> Unit {
match block.table {
Some(table) => {
let row_count = table.rows.length()
let column_count = rich_text_table_column_count(table)
if row_count == 0 || column_count == 0 {
return
}
let column_width = frame.size.width / column_count.to_double()
for row_index in 0..
commands.push(
FillRoundedRectBrush(
RoundedRect::new(
rect=Rect::new(
x=frame.origin.x,
y=row_y,
width=frame.size.width,
height=table.row_height,
),
radius=block.corner_radius,
),
background,
),
)
None => ()
}
}
let row = table.rows[row_index]
for column_index in 0.. ()
}
}
///|
fn append_rich_text_block_runs(
commands : Array[DrawCommand],
block : RichTextBlock,
frame : Rect,
base_font : FontSpec,
foreground : Color,
text_system : TextSystem,
) -> Unit {
let font = rich_text_block_font(block, base_font)
let color = rich_text_block_color(block, foreground)
let mut x = frame.origin.x
if block.prefix != "" {
let width = rich_text_width(block.prefix, font, text_system)
commands.push(
DrawText(
TextRun::new(
text=block.prefix,
frame=Rect::new(
x~,
y=frame.origin.y,
width~,
height=frame.size.height,
),
font~,
color~,
align=TextAlign::TextStart,
),
),
)
x = x + width
}
for run in block.runs {
let run_font = run.font.unwrap_or(font)
let run_color = run.color.unwrap_or(color)
let text = rich_text_run_visual_text(run)
let width = rich_text_run_width(run, run_font, text_system)
let run_frame = Rect::new(
x~,
y=frame.origin.y,
width=max_double(
0.0,
min_double(width, frame.origin.x + frame.size.width - x),
),
height=frame.size.height,
)
match run.background {
Some(background) =>
commands.push(
FillRoundedRectBrush(
RoundedRect::new(
rect=Rect::new(
x=x - 3.0,
y=frame.origin.y + 4.0,
width=width + 6.0,
height=max_double(12.0, frame.size.height - 8.0),
),
radius=run.background_radius,
),
background,
),
)
None => ()
}
match run.image_source {
Some(source) =>
commands.push(
DrawImage({
source,
frame: run_frame,
opacity: 1.0,
fit: run.image_fit,
}),
)
None =>
commands.push(
DrawText(
TextRun::new(
text~,
frame=run_frame,
font=run_font,
color=run_color,
align=TextAlign::TextStart,
),
),
)
}
append_rich_text_run_decoration(
commands, run, run_frame, width, run_font, run_color,
)
x = x + width
}
}
///|
fn append_rich_text_run_decoration(
commands : Array[DrawCommand],
run : RichTextRun,
frame : Rect,
width : Double,
font : FontSpec,
color : Color,
) -> Unit {
match run.decoration {
Some(Strikethrough) => {
let thickness = max_double(1.0, font.size / 14.0)
commands.push(
FillRect(
Rect::new(
x=frame.origin.x,
y=frame.origin.y + frame.size.height * 0.52,
width~,
height=thickness,
),
color.multiply_alpha(0.72),
),
)
}
None => ()
}
}
///|
fn rich_text_block_line_height(
block : RichTextBlock,
base_font : FontSpec,
) -> Double {
let text_height = block.line_height.unwrap_or(
rich_text_block_font(block, base_font).size * 1.55,
)
let mut height = text_height
match block.table {
Some(table) =>
height = max_double(
height,
table.row_height * table.rows.length().to_double(),
)
None => ()
}
for run in block.runs {
match run.image_size {
Some(size) => height = max_double(height, size.height)
None => ()
}
}
height
}
///|
pub fn rich_text_document_height(
document : RichTextDocument,
base_font : FontSpec,
) -> Double {
let mut height = 0.0
for block in document.blocks {
height = height +
rich_text_block_line_height(block, base_font) +
block.inset.top +
block.inset.bottom
}
height
}
///|
fn rich_text_block_font(
block : RichTextBlock,
base_font : FontSpec,
) -> FontSpec {
block.font.unwrap_or(base_font)
}
///|
fn rich_text_block_color(block : RichTextBlock, foreground : Color) -> Color {
block.color.unwrap_or(foreground)
}
///|
fn rich_text_width(
text : String,
font : FontSpec,
text_system : TextSystem,
) -> Double {
text_system.measure_text(TextLayoutInput::new(text~, font~)).size.width
}
///|
fn rich_text_run_width(
run : RichTextRun,
font : FontSpec,
text_system : TextSystem,
) -> Double {
match run.image_size {
Some(size) => size.width
None => rich_text_width(rich_text_run_visual_text(run), font, text_system)
}
}
///|
fn rich_text_run_hit_width(run : RichTextRun, width : Double) -> Double? {
match run.image_source {
Some(_) => Some(width)
None => None
}
}
///|
fn append_rich_text_editor_cursor(
commands : Array[DrawCommand],
element : ElementNode,
data : RichTextEditorViewSpec,
text : String,
display_text : String,
content : Rect,
text_system : TextSystem,
) -> Unit {
let document = (data.format)(display_text)
let caret = clamp_int(
text_field_visual_caret(text, element, true),
0,
text_length(text),
)
let rect = rich_text_document_caret_rect(
document,
caret,
content,
data.font,
text_system,
).unwrap_or(
rich_text_caret_rect(display_text, caret, content, data.font, text_system),
)
commands.push(
FillRoundedRectBrush(
RoundedRect::new(rect~, radius=1.0),
Brush::solid(data.cursor_color),
),
)
}