Input Handling integration

Cohtml accepts input events from the application and, in turn, causes your UI to respond to them by calling script handlers, executing gestures, following links etc.

In other words, your application has to capture input events from the OS and notify Cohtml about them via the set of methods for input:

  • cohtml::View::MouseEvent
  • cohtml::View::KeyEvent,
  • cohtml::View::TouchEvent
  • cohtml::View::GestureEvent
  • cohtml::System::UpdateGamepadState

Refer to each of the methods for more information on how to use them or look them up in the samples. During the handling of all events but the gamepad ones, Cohtml will:

  • Execute script handlers on the elements
  • Bubble the event upwards as per the HTML standard
  • Call default handlers on elements. Default handlers include scrolling elements and following touched links.
  • Call your C++ handlers as described in the InputHandlingControl section below

Controlling input handling

Often the user wants to know whether the input event was handled by the UI and when the event should be passed down to the game. Imagine a strategy game where clicking the unit “actions” is bound to execute some logic, but clicking the health-bar of a unit has to pass that event down to the game to select it.

The user can conditionally disable the handling of an event in the UI. All input event methods minus the gamepad (e.g. cohtml::View::MouseEvent) take a void* “userData” value as an optional parameter. Each time an event is about to be handled by an element in the View, the corresponding method on the cohtml::IViewListener will be called. For example, for mouse events cohtml::IViewListener::OnNodeMouseEvent will be invoked. Keep in mind that for a single mouse event the callback may be invoked multiple times as multiple JS events may take place. For example, for a single MouseMove input event the JS events MouseLeave, MouseEnter, and MouseMove may be fired.

The method receives as arguments the UI element that wants to handle the event, the event itself, the user data and the event phase. The return value of OnNodeMouseEvent determines whether the event will be processed by the UI or any further evaluation will be interrupted. Users can assign specific IDs (or CSS classes) to elements and use those to decide if a certain element should process the input or interrupt it. The user data is the same optional pointer passed to cohtml::View::MouseEvent. The event phase gives information about the path of the event. Interruption during the capturing phase or the target phase guarantees that the event won’t be handled by UI and should be passed to the game.

The userData parameter can be used to track whether the event has handled. The simplest implementation could have this parameter be a pointer to a boolean that records if any element caused input interruption. If it did - the event should probably get handled by the application as well.

void Application::SendMouseEvent(const cohtml::MouseEventData& event)
{
    bool eventHandled = false;
    cohtmlView->MouseEvent(event, nullptr, &eventHandled);
    if (!eventHandled)
    {
        // Pass to the game
    }
}

cohtml::EventAction::Actions ViewListener::OnNodeMouseEvent(
    const cohtml::INodeProxy* node,
    const cohtml::MouseEventData* event,
    void* userData,
    const cohtml::InputEventPhase::PhaseType phase)
{
    if (<EventHandlingBooleanExpression>)
    {
        *((bool*)userData) = true;
        return cohtml::EventAction::ContinueHandling;
    }
    else
    {
        return cohtml::EventAction::InterruptHandling;
    }
}

More complex logic can be implemented using the userData pointer as well. For example, even the complete history of the event handling can be recorded. The userData can be any type the user needs and can record the decisions taken by the ViewListener.

Often users will need to implement more complex input handling decisions - for instance letting just some events go to the UI depending on the element that were clicked or touched, or focused. The easiest way to achieve this is to mark some elements in the page with pre-determined CSS classes, for instance ui-transparent. When the input reaches such an element, the event handling family of functions in the ViewListener can inspect the classes and interrupt the handling.

The MouseLeave event

The View::MouseLeave API is used to notify the View that the mouse has left the View bounds. There is also a Windows-specific event called WM_MOUSELEAVE, which is sent by the OS when the mouse leaves the application window. In the Cohtml Player app, this event is used to send a MouseLeave event through the View API. When the View is inside a game world, the engine should should detect when the mouse leaves the bounds of the View and send a MouseLeave event.

The View::MouseLeave API is different from the mouseleave event in JavaScript, which fires when the mouse leaves the bounds of an element.

Cursor control

Cohtml supports the CSS “cursor” property that allows customizing the cursor pointer. Cohtml itself will not modify the OS-controller cursor in order not to interfere with the embedding program. When the cursor has to change, the ViewListener::OnCursorChanged callback will get called. The user has to inspect the values passed in the callback and use the OS-specific API to change the cursor. For instance on Windows, the user can switch over the cursor pointer types and use the LoadCursor and SetCursor Win32 API to set the required cursor.

Often the user can just select one of the pre-defined types of cursors that CSS supports like “cursor: pointer”, “cursor: text” etc.

Cohtml also supports setting an image/animated cursor from a file with a custom hotspot. To do that just use the “cursor: url(myCursor.ani)” or “cursor: url(myCursor.ani) 5 10” syntax. The URL of the cursor and the hotspot values will be passed to ViewListener::OnCursorChanged. Note that the cursor file will not be loaded by cohtml, it’s the application that has to load the file and use it. This allows developers to use platform-specific cursor formats like .cur/.ani on Windows.

Clipboard control

Cohtml will detect events linked to the Clipboard like Copy/Cut/Paste. When the user initiates a Clipboard interaction by pressing Ctrl(Command on Mac OS X) + C/X/V, cohtml will ask the application to handle the interaction with the OS. On Copy and Cut the ViewListener::OnClipboardTextSet method will be called and it’s up to the embedder to put the passed text (in UTF-8) format in the Clipboard. cohtml itself does not interact directly with the OS. For instance on Windows the developer will have to use the Win32 OpenClipboard/SetClipboardData/CloseClipboard etc. family of functions to put the selected text in the Clipboard. You can consult the samples provided in the package to see an implementation of the methods.

The same principle is followed when the user initiates a Paste action. cohtml will call the ViewListener::OnClipboardTextGet method that the embedder must implement and extract the current text from the Clipboard. On Windows it can be done with GetClipboardData and family.