diff --git a/core/src/backend/ui.rs b/core/src/backend/ui.rs index 203268da4..b86447517 100644 --- a/core/src/backend/ui.rs +++ b/core/src/backend/ui.rs @@ -73,6 +73,11 @@ pub trait UiBackend: Downcast { /// Get the clipboard content fn clipboard_content(&mut self) -> String; + /// Check if the clipboard is available and not empty + fn clipboard_available(&mut self) -> bool { + !self.clipboard_content().is_empty() + } + /// Sets the clipboard to the given content. fn set_clipboard_content(&mut self, content: String); diff --git a/core/src/context_menu.rs b/core/src/context_menu.rs index 079db347c..3dab2cff4 100644 --- a/core/src/context_menu.rs +++ b/core/src/context_menu.rs @@ -142,7 +142,7 @@ impl<'gc> ContextMenuState<'gc> { let language = &context.ui.language(); self.push( ContextMenuItem { - enabled: text.is_text_control_applicable(TextControlCode::Cut), + enabled: text.is_text_control_applicable(TextControlCode::Cut, context), separator_before: true, caption: core_text(language, "context-menu-cut"), checked: false, @@ -154,7 +154,7 @@ impl<'gc> ContextMenuState<'gc> { ); self.push( ContextMenuItem { - enabled: text.is_text_control_applicable(TextControlCode::Copy), + enabled: text.is_text_control_applicable(TextControlCode::Copy, context), separator_before: false, caption: core_text(language, "context-menu-copy"), checked: false, @@ -166,7 +166,7 @@ impl<'gc> ContextMenuState<'gc> { ); self.push( ContextMenuItem { - enabled: text.is_text_control_applicable(TextControlCode::Paste), + enabled: text.is_text_control_applicable(TextControlCode::Paste, context), separator_before: false, caption: core_text(language, "context-menu-paste"), checked: false, @@ -178,7 +178,7 @@ impl<'gc> ContextMenuState<'gc> { ); self.push( ContextMenuItem { - enabled: text.is_text_control_applicable(TextControlCode::Delete), + enabled: text.is_text_control_applicable(TextControlCode::Delete, context), separator_before: false, caption: core_text(language, "context-menu-delete"), checked: false, @@ -190,7 +190,7 @@ impl<'gc> ContextMenuState<'gc> { ); self.push( ContextMenuItem { - enabled: text.is_text_control_applicable(TextControlCode::SelectAll), + enabled: text.is_text_control_applicable(TextControlCode::SelectAll, context), separator_before: true, caption: core_text(language, "context-menu-select-all"), checked: false, diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index dedfacbcc..63c378093 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -1420,7 +1420,11 @@ impl<'gc> EditText<'gc> { } } - pub fn is_text_control_applicable(self, control_code: TextControlCode) -> bool { + pub fn is_text_control_applicable( + self, + control_code: TextControlCode, + context: &mut UpdateContext<'_, 'gc>, + ) -> bool { if !self.is_editable() && control_code.is_edit_input() { return false; } @@ -1439,7 +1443,10 @@ impl<'gc> EditText<'gc> { | TextControlCode::SelectRightLine | TextControlCode::SelectRightDocument | TextControlCode::SelectAll => self.is_selectable(), - TextControlCode::Copy | TextControlCode::Cut => !selection.is_caret(), + TextControlCode::Copy | TextControlCode::Cut => { + !selection.is_caret() + } + TextControlCode::Paste => context.ui.clipboard_available(), _ => true, } } @@ -1449,7 +1456,7 @@ impl<'gc> EditText<'gc> { control_code: TextControlCode, context: &mut UpdateContext<'_, 'gc>, ) { - if !self.is_text_control_applicable(control_code) { + if !self.is_text_control_applicable(control_code, context) { return; } @@ -1525,8 +1532,15 @@ impl<'gc> EditText<'gc> { let text = &self.text()[selection.start()..selection.end()]; context.ui.set_clipboard_content(text.to_string()); } - TextControlCode::Paste => { + TextControlCode::Paste => 'paste: { let text = context.ui.clipboard_content(); + if text.is_empty() { + // When the clipboard is empty, nothing is pasted + // and the already selected text is not removed. + // Note that if the clipboard is not empty, but does not have + // any allowed characters, the selected text is removed. + break 'paste; + } let mut text = self.0.read().restrict.filter_allowed(&text); diff --git a/web/src/ui.rs b/web/src/ui.rs index 4e2497379..12b56b983 100644 --- a/web/src/ui.rs +++ b/web/src/ui.rs @@ -222,6 +222,12 @@ impl UiBackend for WebUiBackend { self.clipboard_content.to_owned() } + fn clipboard_available(&mut self) -> bool { + // On web, we have to assume that the clipboard + // is available due to the JS `paste` event. + true + } + fn set_clipboard_content(&mut self, content: String) { self.clipboard_content = content.to_owned(); // We use `document.execCommand("copy")` as `navigator.clipboard.writeText("string")`