Keyboard Focus and Editable State
Manage keyboard focus on a View and track when editable elements accept text.
On this page
You can track keyboard focus on a View and detect when elements on the page accept typed text.
This tells you when to forward keyboard events to the page, when to reserve keys for game shortcuts, and when to display an on-screen keyboard.
Managing Focus in AppCore
AppCore windows manage focus across their panels automatically.
Call Panel::Focus() to give keyboard focus to a panel, and Window::ClearFocus() to clear focus from every panel in the window.
The window runs the OS input method on its own. To display an on-screen keyboard, register a callback with Window::OnEditableStateChange().
đźš§ Do Not Replace the Editor Listener
AppCore installs an editor listener on each panel's View to run the window's input method. Never call
View::set_editor_listener()on a panel's View— doing so turns off input method support and stopsWindow::OnEditableStateChange()callbacks for that panel.
The rest of this page covers Views you host directly.
Focusing a View
Call View::Focus() when the native window gains keyboard focus, and View::Unfocus() when it loses focus.
#include <Ultralight/Ultralight.h>
using namespace ultralight;
RefPtr<View> view;
///
/// Called by our window when it gains or loses the keyboard.
///
void OnWindowFocusChanged(bool focused) {
if (focused)
view->Focus();
else
view->Unfocus();
}
A new View starts with focus by default (ViewConfig::initial_focus). The caret blinks only while the View has focus— without it, text selections remain visible in an inactive gray.
Calling View::Unfocus() fires a blur event on the active element while keeping it focused in the document— calling View::Focus() later brings its caret back.
Checking for Text Input Focus
You can call View::HasInputFocus() to check whether an element on the page is actively waiting for typed text.
It returns true while a text field, multi-line text area, or editable element holds keyboard focus and accepts text entry.
When the Call Returns False
The call returns false for elements that don't accept typed characters, including buttons, checkboxes, dropdowns, and read-only text fields.
It also returns false whenever the View itself is unfocused— even if an editable element remains focused in the document.
Routing Keyboard Input
In games with a heads-up display, you can use View::HasInputFocus() to decide whether the page or the game receives keystrokes.
Forward keyboard events to the View only while View::HasInputFocus() is true— this lets players type into chat boxes without triggering game movement or hotkeys.
When a menu or dialog opens, forward every key to the View— buttons and dropdowns still need keys like Tab, Space, Enter, and the arrows.
For a full keyboard routing walkthrough, see Integrating into a Game Engine.
Listening for Editable State Changes
To track editable state changes, implement EditorListener and register an instance with View::set_editor_listener().
#include <Ultralight/Ultralight.h>
using namespace ultralight;
class MyApp : public EditorListener {
public:
///
/// Show or hide our on-screen keyboard as the page's editable state changes.
///
void OnChangeEditableState(View* caller,
const EditableState& state) override {
///
/// A keyboard belongs up only while the View has focus and the focused
/// element accepts typed input.
///
bool wants_keyboard = state.view_has_focus && state.is_mutable;
if (!wants_keyboard) {
// Pseudo-code, hide your on-screen keyboard here.
HideOnScreenKeyboard();
return;
}
///
/// Convert the element's bounds to device pixels and park the keyboard
/// just below it, with the layout the page asked for.
///
double scale = caller->device_scale();
double x = state.bounds.left * scale;
double y = state.bounds.bottom * scale;
// Pseudo-code, show your on-screen keyboard here.
ShowOnScreenKeyboard(x, y, state.input_mode, state.enter_key_hint);
}
};
MyApp app;
void AttachListener(View* view) {
///
/// Hand the View a pointer to our listener.
/// (Ownership stays with us, so 'app' must outlive its use by the View.)
///
view->set_editor_listener(&app);
}
When the Callback Fires
The library calls EditorListener::OnChangeEditableState() whenever keyboard focus moves to another element, when the View gains or loses focus, and when the focused element's inputmode attribute changes.
The callback does not fire when an element moves or resizes on screen— use Editor::GetCaretBounds() to follow the caret.
đźš§ Avoid State Changes in Callbacks
The library can call
OnChangeEditableState()from insideView::Focus()orView::Unfocus(). Inspect the state during the callback, but wait until it returns before modifying focus, navigating, or altering page content.
Reading the Editable State
The EditableState struct describes whether the focused element accepts text, what keyboard layout it requests, and where it appears on screen.
Showing or Hiding a Keyboard
An on-screen keyboard or input method should stay active only while view_has_focus and is_mutable are both true. When either property is false, you should hide the keyboard.
Reading Input Hints
The page provides hints that help you configure the on-screen keyboard.
type— the kind of element holding focus (eg,EditableType::Password).input_mode— the keyboard layout requested by the page (eg, numeric or email).enter_key_hint— the action label or icon requested for the Enter key (eg, done or send).
Locating the Element
The bounds property gives the focused element's rectangle in viewport CSS pixels— the same coordinate space used by mouse events. When no element has focus, all coordinates in bounds are zero.
To convert these coordinates to device pixels for native UI, multiply the values by View::device_scale().
If you support an OS input method rather than an on-screen keyboard, see Input Method Editors.