///|
fn[Msg] rich_text_editor(
value~ : String,
placeholder~ : String,
size~ : @core.Size,
format~ : RichTextFormatter,
font~ : @core.FontSpec,
foreground~ : @core.Color,
placeholder_color~ : @core.Color,
background~ : @core.Brush,
border? : @core.BorderStyle? = None,
focused_border? : @core.BorderStyle? = None,
cursor_color~ : @core.Color,
corner_radius? : Double = 0.0,
on_input? : ((String) -> Msg)? = None,
on_input_edit? : ((String, Int, @core.TextRange?) -> Msg)? = None,
on_selection_change? : ((Int, @core.TextRange?) -> Msg)? = None,
on_prefix_click? : ((Int) -> Msg)? = None,
on_command_click? : ((Int) -> Msg)? = None,
input_transform? : ((String, Int, @core.TextRange?, String) -> RichTextInputTransform?)? = None,
accepts_tab_input? : Bool = false,
visible_scroll_y? : Double = 0.0,
on_scroll? : ((Double) -> Msg)? = None,
visible_viewport_height? : Double = 0.0,
visible_overscan? : Double = 320.0,
selection_caret? : Int = -1,
selection_range? : @core.TextRange? = None,
kind? : String = "RichTextEditor",
key? : String? = None,
) -> @core.View[Msg] {
rich_text_editor_snapshot(
value~,
placeholder~,
size~,
format~,
font~,
foreground~,
placeholder_color~,
background~,
border~,
focused_border~,
cursor_color~,
corner_radius~,
on_input~,
on_input_edit~,
on_selection_change~,
on_prefix_click~,
on_command_click~,
input_transform~,
accepts_tab_input~,
visible_scroll_y~,
on_scroll~,
visible_viewport_height~,
visible_overscan~,
selection_caret~,
selection_range~,
kind~,
key~,
)
}
///|
fn[Msg] rich_text_editor_snapshot(
value~ : String,
placeholder~ : String,
size~ : @core.Size,
format~ : RichTextFormatter,
font~ : @core.FontSpec,
foreground~ : @core.Color,
placeholder_color~ : @core.Color,
background~ : @core.Brush,
border~ : @core.BorderStyle?,
focused_border~ : @core.BorderStyle?,
cursor_color~ : @core.Color,
corner_radius~ : Double,
on_input~ : ((String) -> Msg)?,
on_input_edit~ : ((String, Int, @core.TextRange?) -> Msg)?,
on_selection_change~ : ((Int, @core.TextRange?) -> Msg)?,
on_prefix_click~ : ((Int) -> Msg)?,
on_command_click~ : ((Int) -> Msg)?,
input_transform~ : ((String, Int, @core.TextRange?, String) -> RichTextInputTransform?)?,
accepts_tab_input~ : Bool,
visible_scroll_y~ : Double,
on_scroll~ : ((Double) -> Msg)?,
visible_viewport_height~ : Double,
visible_overscan~ : Double,
selection_caret~ : Int,
selection_range~ : @core.TextRange?,
kind~ : String,
key~ : String?,
) -> @core.View[Msg] {
// Internal event paths publish the committed edit record alongside the
// text; the public channel keeps its three-argument shape and ignores it.
let on_input_edit = match on_input_edit {
Some(handler) =>
Some(fn(
text : String,
caret : Int,
selection : @core.TextRange?,
_edit : RichTextInputEdit?,
) {
handler(text, caret, selection)
})
None => None
}
let value_snapshot = value
@core.View::from_node(
RichTextEditorViewNode::{
value: value_snapshot,
placeholder,
size,
format,
font,
foreground,
placeholder_color,
background,
border,
focused_border,
cursor_color,
corner_radius,
accepts_tab_input,
visible_scroll_y,
visible_viewport_height,
visible_overscan,
selection_caret,
selection_range,
kind,
key,
},
event=(node, ctx) => {
rich_text_editor_event(
ctx,
value~,
format=node.format,
font=node.font,
on_input~,
on_input_edit~,
on_selection_change~,
on_prefix_click~,
on_command_click~,
input_transform~,
accepts_tab_input=node.accepts_tab_input,
visible_scroll_y~,
on_scroll~,
visible_viewport_height=node.visible_viewport_height,
visible_overscan=node.visible_overscan,
)
},
text_command=(node, ctx, intent, paste_text) => {
rich_text_editor_command(
ctx,
value~,
format=node.format,
on_input~,
on_input_edit~,
on_selection_change~,
input_transform~,
intent,
paste_text~,
)
},
semantics_actions=_node => {
[
@core.ViewSemanticsActionHandler::set_text((ctx, next_text) => {
let caret = text_length(next_text)
let next_control = {
..ctx.text_control,
caret,
selection: None,
text_selection_anchor: None,
goal_column: None,
composition: None,
composition_cursor: None,
}
let messages : Array[Msg] = []
match on_input {
Some(handler) => messages.push(handler(next_text))
None => ()
}
match on_input_edit {
Some(handler) =>
messages.push(handler(next_text, caret, None, None))
None => ()
}
@core.ViewSemanticsActionResult::accepted(
text_control=Some(next_control),
dirty=ViewPaintDirty,
messages~,
)
}),
]
},
)
}
///|
priv struct RichTextEditorViewNode {
value : String
placeholder : String
size : @core.Size
format : RichTextFormatter
font : @core.FontSpec
foreground : @core.Color
placeholder_color : @core.Color
background : @core.Brush
border : @core.BorderStyle?
focused_border : @core.BorderStyle?
cursor_color : @core.Color
corner_radius : Double
accepts_tab_input : Bool
visible_scroll_y : Double
visible_viewport_height : Double
visible_overscan : Double
selection_caret : Int
selection_range : @core.TextRange?
kind : String
key : String?
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn identity(self) {
@core.ViewIdentity::new(kind=self.kind, key=self.key)
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn declaration(self) {
rich_text_declaration(
layout=rich_text_exact_channel(declaration_size_key(self.size)),
paint=Uncacheable,
semantics=rich_text_exact_channel(
rich_text_record_key("RichTextEditor.semantics", [
("label", @core.DeclarationKey::string(self.placeholder)),
("value", @core.DeclarationKey::string(self.value)),
]),
),
)
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn layout(self, ctx) {
@core.ViewLayoutResult::new(size=ctx.constraints.constrain(self.size))
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn paint(self, ctx) {
rich_text_editor_paint(
ctx,
value=self.value,
placeholder=self.placeholder,
format=self.format,
font=self.font,
foreground=self.foreground,
placeholder_color=self.placeholder_color,
background=self.background,
border=self.border,
focused_border=self.focused_border,
cursor_color=self.cursor_color,
corner_radius=self.corner_radius,
visible_scroll_y=self.visible_scroll_y,
visible_viewport_height=self.visible_viewport_height,
visible_overscan=self.visible_overscan,
)
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn text_input_state(
self,
ctx,
) {
Some(
rich_text_input_state(
text=self.value,
frame=ctx.frame,
format=self.format,
font=self.font,
control=ctx.text_control,
selection_caret=self.selection_caret,
selection_range=self.selection_range,
visible_scroll_y=self.visible_scroll_y,
visible_viewport_height=self.visible_viewport_height,
visible_overscan=self.visible_overscan,
text_system=ctx.text_system,
),
)
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn semantics(self) {
@core.ViewSemanticsInfo::new(
composition=Boundary,
role=TextField,
label=self.placeholder,
value=self.value,
)
}
///|
impl @core.ViewNode for RichTextEditorViewNode with fn focusable(_self) {
true
}
///|
pub fn[Msg] controlled_rich_text_editor(
value~ : String,
on_input~ : (String) -> Msg,
placeholder~ : String,
size~ : @core.Size,
format~ : RichTextFormatter,
font~ : @core.FontSpec,
foreground~ : @core.Color,
placeholder_color~ : @core.Color,
background~ : @core.Brush,
border? : @core.BorderStyle? = None,
focused_border? : @core.BorderStyle? = None,
cursor_color~ : @core.Color,
corner_radius? : Double = 0.0,
on_selection_change? : ((Int, @core.TextRange?) -> Msg)? = None,
on_prefix_click? : ((Int) -> Msg)? = None,
on_command_click? : ((Int) -> Msg)? = None,
input_transform? : ((String, Int, @core.TextRange?, String) -> RichTextInputTransform?)? = None,
accepts_tab_input? : Bool = false,
visible_scroll_y? : Double = 0.0,
on_scroll? : ((Double) -> Msg)? = None,
visible_viewport_height? : Double = 0.0,
visible_overscan? : Double = 320.0,
selection_caret? : Int = -1,
selection_range? : @core.TextRange? = None,
kind? : String = "RichTextEditor",
key? : String? = None,
) -> @core.View[Msg] {
rich_text_editor(
value~,
placeholder~,
size~,
format~,
font~,
foreground~,
placeholder_color~,
background~,
border~,
focused_border~,
cursor_color~,
corner_radius~,
on_input=Some(on_input),
on_input_edit=None,
on_selection_change~,
on_prefix_click~,
on_command_click~,
input_transform~,
accepts_tab_input~,
visible_scroll_y~,
on_scroll~,
visible_viewport_height~,
visible_overscan~,
selection_caret~,
selection_range~,
kind~,
key~,
)
}