///|
fn ElementNode::find_focusable_at(
self : ElementNode,
point : Point,
render : RenderNode,
) -> ElementId? {
match self.spec {
TextFieldSpec(_)
| CheckboxSpec(_)
| ButtonSpec(_)
| PickerSpec(_)
| DatePickerSpec(_)
| SearchBarSpec(_) =>
if render.frame.contains(point) {
Some(self.id)
} else {
None
}
RichTextEditorSpec(_) =>
if render.frame.contains(point) {
Some(self.id)
} else {
None
}
FlexSpec(_)
| PaddingSpec(_)
| ContainerSpec(_)
| FrameSpec(_)
| StackSpec(_)
| ScrollSpec(_)
| GridSpec(_)
| ListSpec(_)
| CustomLayoutSpec(_)
| ComponentSpec(_) => {
let mut index = self.children.length() - 1
while index >= 0 {
if index < render.children.length() &&
render.children[index].frame.contains(point) {
match
self.children[index].find_focusable_at(
point,
render.children[index],
) {
Some(id) => return Some(id)
None => ()
}
}
index = index - 1
}
None
}
ModifiedSpec(data) =>
match data.modifier {
DisabledModifier(true) => None
FocusableModifier(true) =>
if render.frame.contains(point) {
Some(self.id)
} else {
None
}
_ => {
let mut index = self.children.length() - 1
while index >= 0 {
if index < render.children.length() &&
render.children[index].frame.contains(point) {
match
self.children[index].find_focusable_at(
point,
render.children[index],
) {
Some(id) => return Some(id)
None => ()
}
}
index = index - 1
}
None
}
}
_ => None
}
}
///|
fn ElementNode::focusable_ids(
self : ElementNode,
ids : Array[ElementId],
) -> Unit {
match self.spec {
TextFieldSpec(_)
| RichTextEditorSpec(_)
| ButtonSpec(_)
| CheckboxSpec(_) => ids.push(self.id)
ModifiedSpec(data) =>
match data.modifier {
DisabledModifier(true) => ()
FocusableModifier(true) => ids.push(self.id)
_ =>
for child in self.children {
child.focusable_ids(ids)
}
}
FlexSpec(_)
| PaddingSpec(_)
| ContainerSpec(_)
| FrameSpec(_)
| StackSpec(_)
| ScrollSpec(_)
| GridSpec(_)
| ListSpec(_)
| ComponentSpec(_) =>
for child in self.children {
child.focusable_ids(ids)
}
_ => ()
}
}
///|
fn ElementNode::accepts_tab_input(
self : ElementNode,
target : ElementId,
) -> Bool {
if self.id == target {
match self.spec {
RichTextEditorSpec(data) => data.accepts_tab_input
_ => false
}
} else {
for child in self.children {
if child.accepts_tab_input(target) {
return true
}
}
false
}
}
///|
fn ElementNode::next_focus(
self : ElementNode,
current~ : ElementId?,
reverse~ : Bool,
) -> ElementId? {
let ids : Array[ElementId] = []
self.focusable_ids(ids)
if ids.length() == 0 {
None
} else {
let mut index = -1
match current {
Some(current) =>
for i, id in ids {
if id == current {
index = i
}
}
None => ()
}
if reverse {
let next = if index <= 0 { ids.length() - 1 } else { index - 1 }
Some(ids[next])
} else {
let next = if index < 0 || index + 1 >= ids.length() {
0
} else {
index + 1
}
Some(ids[next])
}
}
}
///|
fn ElementNode::find_keyed(self : ElementNode, key : String) -> ElementId? {
if self.key == Some(Key::new(key)) {
Some(self.id)
} else {
for child in self.children {
match child.find_keyed(key) {
Some(id) => return Some(id)
None => ()
}
}
None
}
}
///|
fn handle_rich_text_editor_keyboard(
element : ElementNode,
data : RichTextEditorViewSpec,
event : KeyboardEvent,
) -> ElementEventResult {
if !event.pressed {
return { changed: false, activated: false, focused: None }
}
if event.key == "Enter" {
return handle_rich_text_editor_input(element, data, InsertText("\n"))
}
if event.key == "Tab" && data.accepts_tab_input {
let text = if event.modifiers.shift { "\u{000b}" } else { "\t" }
return handle_rich_text_editor_input(element, data, InsertText(text))
}
if is_select_all_shortcut(event) {
return select_text_range(element, data.value.get())
}
if is_undo_shortcut(event) {
let result = handle_rich_text_editor_command(
element,
data,
Undo,
paste_text=None,
)
return { changed: result.changed, activated: false, focused: None }
}
if is_redo_shortcut(event) {
let result = handle_rich_text_editor_command(
element,
data,
Redo,
paste_text=None,
)
return { changed: result.changed, activated: false, focused: None }
}
if event.key == "Backspace" {
match active_text_selection(element.control.selection) {
Some(selection) =>
return handle_rich_text_editor_input(
element,
data,
DeleteRange(selection),
)
None =>
return handle_rich_text_editor_input(
element,
data,
DeleteSurrounding(1, 0),
)
}
}
if event.key == "Delete" {
match active_text_selection(element.control.selection) {
Some(selection) =>
return handle_rich_text_editor_input(
element,
data,
DeleteRange(selection),
)
None =>
return handle_rich_text_editor_input(
element,
data,
DeleteSurrounding(0, 1),
)
}
}
if event.key == "ArrowLeft" {
return apply_rich_text_caret_move(
element,
data,
move_text_caret(
data.value.get(),
element.control.caret,
element.control.selection,
MoveLeft,
extend=event.modifiers.shift,
),
)
}
if event.key == "ArrowRight" {
return apply_rich_text_caret_move(
element,
data,
move_text_caret(
data.value.get(),
element.control.caret,
element.control.selection,
MoveRight,
extend=event.modifiers.shift,
),
)
}
if event.key == "ArrowUp" {
return move_multiline_caret_vertical(element, data, -1)
}
if event.key == "ArrowDown" {
return move_multiline_caret_vertical(element, data, 1)
}
if event.key == "Home" {
element.control.caret = multiline_line_start(
data.value.get(),
element.control.caret,
)
element.control.selection = None
notify_rich_text_selection_change(data, element)
element.dirty.mark_paint()
return { changed: true, activated: false, focused: None }
}
if event.key == "End" {
element.control.caret = multiline_line_end(
data.value.get(),
element.control.caret,
)
element.control.selection = None
notify_rich_text_selection_change(data, element)
element.dirty.mark_paint()
return { changed: true, activated: false, focused: None }
}
match event.text {
Some(text) =>
if text != "" {
handle_rich_text_editor_input(element, data, InsertText(text))
} else {
{ changed: false, activated: false, focused: None }
}
None => { changed: false, activated: false, focused: None }
}
}