core: Add EditText::is_text_control_applicable

This method is useful for checking whether the given text control code
may be applied to the current state of the EditText.
This commit is contained in:
Kamil Jarosz 2024-06-06 23:46:16 +02:00 committed by Nathan Adams
parent 1f3c230691
commit 0910104c3b
2 changed files with 235 additions and 155 deletions

View File

@ -127,6 +127,70 @@ impl<'gc> ContextMenuState<'gc> {
);
}
}
fn build_text_items(&mut self, text: EditText<'gc>, context: &mut UpdateContext<'_, 'gc>) {
let language = &context.ui.language();
self.push(
ContextMenuItem {
enabled: text.is_text_control_applicable(TextControlCode::Cut),
separator_before: true,
caption: core_text(language, "context-menu-cut"),
checked: false,
},
ContextMenuCallback::TextControl {
code: TextControlCode::Cut,
text,
},
);
self.push(
ContextMenuItem {
enabled: text.is_text_control_applicable(TextControlCode::Copy),
separator_before: false,
caption: core_text(language, "context-menu-copy"),
checked: false,
},
ContextMenuCallback::TextControl {
code: TextControlCode::Copy,
text,
},
);
self.push(
ContextMenuItem {
enabled: text.is_text_control_applicable(TextControlCode::Paste),
separator_before: false,
caption: core_text(language, "context-menu-paste"),
checked: false,
},
ContextMenuCallback::TextControl {
code: TextControlCode::Paste,
text,
},
);
self.push(
ContextMenuItem {
enabled: text.is_text_control_applicable(TextControlCode::Delete),
separator_before: false,
caption: core_text(language, "context-menu-delete"),
checked: false,
},
ContextMenuCallback::TextControl {
code: TextControlCode::Delete,
text,
},
);
self.push(
ContextMenuItem {
enabled: text.is_text_control_applicable(TextControlCode::SelectAll),
separator_before: true,
caption: core_text(language, "context-menu-select-all"),
checked: false,
},
ContextMenuCallback::TextControl {
code: TextControlCode::SelectAll,
text,
},
);
}
}
#[derive(Clone, Serialize)]

View File

@ -1420,191 +1420,207 @@ impl<'gc> EditText<'gc> {
}
}
pub fn is_text_control_applicable(self, control_code: TextControlCode) -> bool {
if !self.is_editable() && control_code.is_edit_input() {
return false;
}
let Some(selection) = self.selection() else {
return false;
};
match control_code {
TextControlCode::SelectLeft
| TextControlCode::SelectLeftWord
| TextControlCode::SelectLeftLine
| TextControlCode::SelectLeftDocument
| TextControlCode::SelectRight
| TextControlCode::SelectRightWord
| TextControlCode::SelectRightLine
| TextControlCode::SelectRightDocument
| TextControlCode::SelectAll => self.is_selectable(),
TextControlCode::Copy | TextControlCode::Cut => !selection.is_caret(),
_ => true,
}
}
pub fn text_control_input(
self,
control_code: TextControlCode,
context: &mut UpdateContext<'_, 'gc>,
) {
if !self.is_editable() && control_code.is_edit_input() {
if !self.is_text_control_applicable(control_code) {
return;
}
if let Some(selection) = self.selection() {
let mut changed = false;
let is_selectable = self.is_selectable();
match control_code {
TextControlCode::Enter => {
self.text_input(Self::INPUT_NEWLINE, context);
}
TextControlCode::MoveLeft
| TextControlCode::MoveLeftWord
| TextControlCode::MoveLeftLine
| TextControlCode::MoveLeftDocument => {
let new_pos = if selection.is_caret() {
self.find_new_position(control_code, selection.to)
} else {
selection.start()
};
let Some(selection) = self.selection() else {
return;
};
let mut changed = false;
let is_selectable = self.is_selectable();
match control_code {
TextControlCode::Enter => {
self.text_input(Self::INPUT_NEWLINE, context);
}
TextControlCode::MoveLeft
| TextControlCode::MoveLeftWord
| TextControlCode::MoveLeftLine
| TextControlCode::MoveLeftDocument => {
let new_pos = if selection.is_caret() {
self.find_new_position(control_code, selection.to)
} else {
selection.start()
};
self.set_selection(
Some(TextSelection::for_position(new_pos)),
context.gc_context,
);
}
TextControlCode::MoveRight
| TextControlCode::MoveRightWord
| TextControlCode::MoveRightLine
| TextControlCode::MoveRightDocument => {
let new_pos = if selection.is_caret() && selection.to < self.text().len() {
self.find_new_position(control_code, selection.to)
} else {
selection.end()
};
self.set_selection(
Some(TextSelection::for_position(new_pos)),
context.gc_context,
);
}
TextControlCode::SelectLeft
| TextControlCode::SelectLeftWord
| TextControlCode::SelectLeftLine
| TextControlCode::SelectLeftDocument => {
if selection.to > 0 {
let new_pos = self.find_new_position(control_code, selection.to);
self.set_selection(
Some(TextSelection::for_position(new_pos)),
Some(TextSelection::for_range(selection.from, new_pos)),
context.gc_context,
);
}
TextControlCode::MoveRight
| TextControlCode::MoveRightWord
| TextControlCode::MoveRightLine
| TextControlCode::MoveRightDocument => {
let new_pos = if selection.is_caret() && selection.to < self.text().len() {
self.find_new_position(control_code, selection.to)
} else {
selection.end()
};
}
TextControlCode::SelectRight
| TextControlCode::SelectRightWord
| TextControlCode::SelectRightLine
| TextControlCode::SelectRightDocument => {
if selection.to < self.text().len() {
let new_pos = self.find_new_position(control_code, selection.to);
self.set_selection(
Some(TextSelection::for_position(new_pos)),
Some(TextSelection::for_range(selection.from, new_pos)),
context.gc_context,
)
}
}
TextControlCode::SelectAll => {
self.set_selection(
Some(TextSelection::for_range(0, self.text().len())),
context.gc_context,
);
}
TextControlCode::Copy => {
let text = &self.text()[selection.start()..selection.end()];
context.ui.set_clipboard_content(text.to_string());
}
TextControlCode::Paste => {
let text = context.ui.clipboard_content();
let mut text = self.0.read().restrict.filter_allowed(&text);
if text.len() > self.available_chars() && self.available_chars() > 0 {
text = text[0..self.available_chars()].to_owned();
}
if text.len() <= self.available_chars() {
self.replace_text(
selection.start(),
selection.end(),
&WString::from_utf8(&text),
context,
);
}
TextControlCode::SelectLeft
| TextControlCode::SelectLeftWord
| TextControlCode::SelectLeftLine
| TextControlCode::SelectLeftDocument => {
if is_selectable && selection.to > 0 {
let new_pos = self.find_new_position(control_code, selection.to);
self.set_selection(
Some(TextSelection::for_range(selection.from, new_pos)),
context.gc_context,
);
}
}
TextControlCode::SelectRight
| TextControlCode::SelectRightWord
| TextControlCode::SelectRightLine
| TextControlCode::SelectRightDocument => {
if is_selectable && selection.to < self.text().len() {
let new_pos = self.find_new_position(control_code, selection.to);
self.set_selection(
Some(TextSelection::for_range(selection.from, new_pos)),
context.gc_context,
)
}
}
TextControlCode::SelectAll => {
let new_pos = selection.start() + text.len();
if is_selectable {
self.set_selection(
Some(TextSelection::for_range(0, self.text().len())),
Some(TextSelection::for_position(new_pos)),
context.gc_context,
);
} else {
self.set_selection(
Some(TextSelection::for_position(self.text().len())),
context.gc_context,
);
}
changed = true;
}
TextControlCode::Copy => {
if !selection.is_caret() {
let text = &self.text()[selection.start()..selection.end()];
context.ui.set_clipboard_content(text.to_string());
}
}
TextControlCode::Paste => {
let text = context.ui.clipboard_content();
let mut text = self.0.read().restrict.filter_allowed(&text);
}
TextControlCode::Cut => {
let text = &self.text()[selection.start()..selection.end()];
context.ui.set_clipboard_content(text.to_string());
if text.len() > self.available_chars() && self.available_chars() > 0 {
text = text[0..self.available_chars()].to_owned();
}
if text.len() <= self.available_chars() {
self.replace_text(
selection.start(),
selection.end(),
&WString::from_utf8(&text),
context,
);
let new_pos = selection.start() + text.len();
if is_selectable {
self.set_selection(
Some(TextSelection::for_position(new_pos)),
context.gc_context,
);
} else {
self.set_selection(
Some(TextSelection::for_position(self.text().len())),
context.gc_context,
);
}
changed = true;
}
}
TextControlCode::Cut => {
if !selection.is_caret() {
let text = &self.text()[selection.start()..selection.end()];
context.ui.set_clipboard_content(text.to_string());
self.replace_text(
selection.start(),
selection.end(),
WStr::empty(),
context,
);
if is_selectable {
self.set_selection(
Some(TextSelection::for_position(selection.start())),
context.gc_context,
);
} else {
self.set_selection(
Some(TextSelection::for_position(self.text().len())),
context.gc_context,
);
}
changed = true;
}
}
TextControlCode::Backspace
| TextControlCode::BackspaceWord
| TextControlCode::Delete
| TextControlCode::DeleteWord
if !selection.is_caret() =>
{
// Backspace or delete with multiple characters selected
self.replace_text(selection.start(), selection.end(), WStr::empty(), context);
self.replace_text(selection.start(), selection.end(), WStr::empty(), context);
if is_selectable {
self.set_selection(
Some(TextSelection::for_position(selection.start())),
context.gc_context,
);
} else {
self.set_selection(
Some(TextSelection::for_position(self.text().len())),
context.gc_context,
);
}
changed = true;
}
TextControlCode::Backspace
| TextControlCode::BackspaceWord
| TextControlCode::Delete
| TextControlCode::DeleteWord
if !selection.is_caret() =>
{
// Backspace or delete with multiple characters selected
self.replace_text(selection.start(), selection.end(), WStr::empty(), context);
self.set_selection(
Some(TextSelection::for_position(selection.start())),
context.gc_context,
);
changed = true;
}
TextControlCode::Backspace | TextControlCode::BackspaceWord => {
// Backspace with caret
if selection.start() > 0 {
// Delete previous character(s)
let start = self.find_new_position(control_code, selection.start());
self.replace_text(start, selection.start(), WStr::empty(), context);
self.set_selection(
Some(TextSelection::for_position(start)),
context.gc_context,
);
changed = true;
}
TextControlCode::Backspace | TextControlCode::BackspaceWord => {
// Backspace with caret
if selection.start() > 0 {
// Delete previous character(s)
let start = self.find_new_position(control_code, selection.start());
self.replace_text(start, selection.start(), WStr::empty(), context);
self.set_selection(
Some(TextSelection::for_position(start)),
context.gc_context,
);
changed = true;
}
}
TextControlCode::Delete | TextControlCode::DeleteWord => {
// Delete with caret
if selection.end() < self.text_length() {
// Delete next character(s)
let end = self.find_new_position(control_code, selection.start());
self.replace_text(selection.start(), end, WStr::empty(), context);
// No need to change selection, reset it to prevent caret from blinking
self.reset_selection_blinking(context.gc_context);
changed = true;
}
}
TextControlCode::Delete | TextControlCode::DeleteWord => {
// Delete with caret
if selection.end() < self.text_length() {
// Delete next character(s)
let end = self.find_new_position(control_code, selection.start());
self.replace_text(selection.start(), end, WStr::empty(), context);
// No need to change selection, reset it to prevent caret from blinking
self.reset_selection_blinking(context.gc_context);
changed = true;
}
}
if changed {
let mut activation = Avm1Activation::from_nothing(
context.reborrow(),
ActivationIdentifier::root("[Propagate Text Binding]"),
self.into(),
);
self.propagate_text_binding(&mut activation);
self.on_changed(&mut activation);
}
}
if changed {
let mut activation = Avm1Activation::from_nothing(
context.reborrow(),
ActivationIdentifier::root("[Propagate Text Binding]"),
self.into(),
);
self.propagate_text_binding(&mut activation);
self.on_changed(&mut activation);
}
}