From 0910104c3b72866d205b228a4085cc379d55f220 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Thu, 6 Jun 2024 23:46:16 +0200 Subject: [PATCH] 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. --- core/src/context_menu.rs | 64 ++++++ core/src/display_object/edit_text.rs | 326 ++++++++++++++------------- 2 files changed, 235 insertions(+), 155 deletions(-) diff --git a/core/src/context_menu.rs b/core/src/context_menu.rs index 0894a7b47..bb5a36c90 100644 --- a/core/src/context_menu.rs +++ b/core/src/context_menu.rs @@ -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)] diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 372314954..dedfacbcc 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -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); } }