docs

Text Editing and the Clipboard

Execute editing commands, manage clipboard access, and position context menus at the caret.

On this page

You can control text editing, selections, and clipboard actions on a page using the View's editor. This lets you connect native Edit menus, context menus, toolbar buttons, and on-screen keyboards directly to the focused element.

Standard keyboard shortcuts like Ctrl+C and Ctrl+V already run when you forward key events (see Keyboard Input). The editor provides programmatic control over the same operations, with or without AppCore.

Running Editing Commands

Call View::editor() to obtain the View's Editor instance. Calling Editor::Execute() with an EditorCommand runs that command on the focused frame— it returns false if the command cannot run (such as copying without a selection or undoing with an empty history).

To copy the current selection:

C++
#include <Ultralight/Ultralight.h>

using namespace ultralight;

RefPtr<Renderer> renderer;
RefPtr<View> view;

///
/// Copy the page's selection, the way the Edit menu's Copy item would.
///
void CopySelection() {
  Editor* editor = view->editor();
  editor->Execute(EditorCommand::Copy);
}

Available Commands

The EditorCommand enum groups editing actions by function.

Group Commands
Clipboard and history Cut, Copy, Paste, PasteAsPlainText, SelectAll, Undo, Redo
Caret motion MoveLeft, MoveWordRight, MoveToEndOfLine, MovePageDown
Selection SelectWord, SelectLine, SelectParagraph, Unselect
Deletion DeleteBackward, DeleteForward, DeleteWordBackward, DeleteToEndOfLine
Insertion InsertNewline, InsertLineBreak, InsertTab, InsertBacktab

Every caret motion also provides an AndModifySelection variant (such as MoveLeftAndModifySelection) that extends the current selection, matching Shift-key behavior.

Inserting Text

To insert text at the caret, call Editor::InsertText(). The string replaces any active selection, exactly as if the user had typed it.

To forward text from an on-screen keyboard:

C++
///
/// Called by our on-screen keyboard when the player taps a key.
///
void OnScreenKeyTapped(const String& text) {
  view->editor()->InsertText(text);
}

Building Edit and Context Menus

Call Editor::CanExecute() before displaying a menu to determine whether to enable or disable individual items.

To update menu item states and dispatch the selected command:

C++
///
/// Enable or grey out each item just before the menu opens.
///
void UpdateEditMenu() {
  Editor* editor = view->editor();

  // Pseudo-code, enable or disable the items in your own menu here.
  SetMenuItemEnabled("Cut", editor->CanExecute(EditorCommand::Cut));
  SetMenuItemEnabled("Copy", editor->CanExecute(EditorCommand::Copy));
  SetMenuItemEnabled("Paste", editor->CanExecute(EditorCommand::Paste));
  SetMenuItemEnabled("Undo", editor->CanExecute(EditorCommand::Undo));
  SetMenuItemEnabled("Redo", editor->CanExecute(EditorCommand::Redo));
}

///
/// Run the command behind the item the user picked.
///
void OnEditMenuCommand(EditorCommand command) {
  view->editor()->Execute(command);
}

Positioning Menus at the Caret

To position a context menu at the caret, retrieve its bounds from the editor:

C++
///
/// Open a right-click menu just below the caret.
///
void ShowContextMenu() {
  Rect caret = view->editor()->GetCaretBounds();
  double scale = view->device_scale();

  // Pseudo-code, open your menu at this position within the View.
  OpenContextMenu(caret.left * scale, caret.bottom * scale);
}

Editor::GetCaretBounds() returns the caret's rectangle in viewport CSS pixels rather than screen coordinates— the same coordinate system used for mouse events (see Mouse and Scroll Input).

When the page has no active caret, all fields in the rectangle are zero.

📘 Avoid Polling Every Frame

Calling Editor::GetCaretBounds() updates any pending layout first. Query the bounds when opening a menu rather than polling every frame.

Managing the Clipboard

Editing commands like Cut, Copy, and Paste use the platform clipboard. App::Create() sets up the OS clipboard automatically— without AppCore, you must provide one by calling Platform::set_clipboard() (see Clipboard Integration).

Controlling Script Clipboard Access

The ViewConfig::clipboard_read_policy setting restricts which pages can read the clipboard from script (such as a page button calling document.execCommand('paste')). The library only evaluates these requests during user gestures like clicks or key presses. Pastes triggered directly by the user or through Editor::Execute() are never affected.

Policy Grants Reads From
ClipboardReadPolicy::AllowForAppContent (default) Local file:/// pages and content you supply directly (eg, View::LoadHTML())
ClipboardReadPolicy::Allow Any page
ClipboardReadPolicy::Deny No page

To allow script on any page to read the clipboard during a user gesture:

C++
///
/// Let any page read the clipboard during a click or key press.
///
void CreateView() {
  ViewConfig config;
  config.clipboard_read_policy = ClipboardReadPolicy::Allow;
  view = renderer->CreateView(800, 600, config, nullptr);
}

🚧 Restrict Untrusted Content

Setting ClipboardReadPolicy::Allow lets any loaded page read clipboard contents, including remote web pages. Use it only when the View displays trusted content.

Matching Native Editing Behavior

By default, ViewConfig::match_native_editing_behavior is false, providing identical text selection and editing behavior across all platforms. Setting this field to true adopts the host OS's native editing conventions (such as macOS-style Shift-click selection extension).

Views created automatically by AppCore for AddPanel() enable this setting through Settings::match_native_editing_behavior. If you pass an explicit ViewConfig, the View uses the value specified in that configuration instead.