core: Prevent pasting empty string when the clipboard is empty

This commit is contained in:
Kamil Jarosz 2024-06-06 21:55:46 +02:00 committed by Nathan Adams
parent a2751c3c0f
commit c23ec0782a
4 changed files with 34 additions and 9 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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);

View File

@ -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")`