For the complete documentation index, see llms.txt. This page is also available as Markdown.

Mouse and Touch Screen (Cursor)

Introduction

Gum supports reading from the mouse and touch screen for events. Both the mouse and touch screen report their actions through the Cursor class. Controls which respond to click events (such as Button and CheckBox) automatically read from the Cursor.

Runtime Compatibility

Touch support varies by runtime. Where a runtime has no real touch API, touch is mouse-emulated instead.

Runtime
Touch Support
Notes

raylib

Partial

Single tap may not register as a click; a longer tap or a second tap works. Selection-style interactions (e.g. list items) work fine.

Silk.NET

Full

โœ…

MonoGame / KNI (DesktopGL)

Mouse-emulated

No real touch API on Windows; see note below.

raylib's default desktop backend (GLFW) has no real touch input. See raylib's own source comment acknowledging this. raylib's SDL backend does support real touch, but isn't what Gum's raylib runtime currently uses.

Code Example: Accessing the Cursor

GumService contains an instance of the Cursor class. The following code shows how to access the Cursor class and create rectangles when the Cursor detects a click.

protected override void Update(GameTime gameTime)
{
    GumUI.Update(gameTime);

    Cursor cursor = GumUI.Cursor;

    if(cursor.PrimaryClick)
    {
        ColoredRectangleRuntime rectangle = new ();
        rectangle.AddToRoot();
        rectangle.X = cursor.XRespectingGumZoomAndBounds();
        rectangle.Y = cursor.YRespectingGumZoomAndBounds();
        rectangle.Color = Color.Red;
    }

    base.Update(gameTime);
}
Rectangles created in response to Cursor clicks

Checking Control Over

The Cursor class reports information about what it is over which can be checked in events or in an Update call.

FrameworkElementOver

The FrameworkElementOver property returns the FrameworkElement (control) that the Cursor is over. The following code shows how to detect and display the control that the button is over.

Cursor.WindowOver displaying the element that the cursor is over

Try on XnaFiddle.NET

VisualOver

The Cursor class also provides a VisualOver property. This can be used to determine the specific Visual that the cursor is over. A Visual will only be returned if the following are true:

  1. The Visual's HasEvents is set to true

  2. The Visual inherits from InteractiveGue

If a VisualOver is not null, then that visual consumes cursor events. If that visual happens to be the Visual for a FrameworkElement, then it passes those events to FrameworkElement. If the Visual is either not part of a FrameworkElement, or if it is a child of a FrameworkElement, then it will consume events, preventing the FrameworkElement itself from receiving events.

For information on which controls support events, see the Visual Events page.

HasCursorOver

GraphicalUiElement instances can be manually checked for whether the cursor is overlapping them. HasCursorOver method is a pure bounds check - it does not check if the GraphicalUiElement inherits from InteractiveGue, nor does it perform overlapping tests. This can be used for pure hit-tests.

The following code creates a rectangle and checks if the cursor is overlapping the rectangle:

Try on XnaFiddle.NET

Adjusting the Cursor for Scaled or Offset Rendering

If your game draws Gum's output through a RenderTarget2D, scales it with a SpriteBatch matrix, or otherwise transforms the rendered UI, the raw cursor position no longer lines up with what the player sees. Gum offers two ways to compensate, depending on whether all of your UI shares one coordinate space or several coexist in the same frame.

One coordinate space: Cursor.TransformMatrix

When every Gum visual is transformed the same way (for example, the entire UI is drawn into one render target and scaled to fit the window), set a single Cursor.TransformMatrix. It is applied once to the cursor position before any hit-testing, so clicks land on the visual under the mouse. See TransformMatrix for a full example.

Several coordinate spaces at once: HitTestTransformMatrix

A single cursor transform cannot serve two coordinate spaces at the same time. A common case is game UI drawn into a scaled RenderTarget2D while a separate editor or debug UI is drawn straight to the window at 1:1, with both clickable in the same frame. For this, set HitTestTransformMatrix on the root of each subtree that needs its own mapping.

HitTestTransformMatrix is a System.Numerics.Matrix3x2? on GraphicalUiElement. It is resolved by climbing to the nearest ancestor that set one, so setting it on a container covers that whole subtree, and content drawn at 1:1 simply leaves it unset (the default). Set it to the matrix that maps a raw window pixel back into the space the subtree was drawn in, which is the inverse of the scale and offset used to blit the render target. It affects hit-testing only and never changes rendering or layout.

Draw gameUi into the render target and blit it to the window just as in the TransformMatrix example, using blitOffset and blitScale for the blit destination. Any UI drawn straight to the window stays at 1:1 with no transform, so both are clickable in the same frame. Check hits with the HasCursorOver(ICursor) overload, for example gameUi.HasCursorOver(GumUI.Cursor).

Only the HasCursorOver(ICursor) path applies HitTestTransformMatrix, and this is the path normal Forms interaction uses. The pure-bounds HasCursorOver(float, float) overload shown earlier on this page does not.

Combining Root with additional interactive roots

The HasCursorOver check above is a raw bounds test โ€” fine for reacting to a click yourself, but it does not run hover, push, click, or drag for Forms controls (Button, Window, etc.) inside gameUi. Those only run through GumService.Default.Update(gameTime, roots). If gameUi is not part of Root but still needs real Forms interactivity, pass it alongside Root in the same call:

For a runnable project with both coordinate spaces interactive in the same frame, see the RenderTarget screen in the Gum immediate-mode sample:

Disabling the Cursor Globally

The Cursor instance reported by GumService can be replaced with a custom implementation of the ICursor interface. A custom ICursor class can be created to modify its behavior. For example, the following implementation disables all behavior:

This DisabledCursor class can be assigned to disable all Cursor actions as shown in the following code:

DisabledCursor used to prevent UI interaction

Last updated

Was this helpful?