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:
#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:
///
/// 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:
///
/// 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:
///
/// 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:
///
/// 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::Allowlets 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.