LVGL Long Presses: Cancel Repeating Actions When Touch Ownership Is Lost

A hold-to-adjust control should have an explicit action session that can be canceled without waiting for an ordinary release. Admit repeat commands only while that session still owns the current view and input interaction. Treat press loss, input reset and navigation as cancellation paths, and ensure the eventual release cannot add a surprise final step.

Applicability: this article uses LVGL v9.3.0, one timer-driven pointer, a non-scrolling adjustment control and LVGL’s long-press repeat events. It excludes animation management, nested scroll arbitration and encoder-specific behavior. The action policy and replay sequences are hypothetical and have not been run on a target.

A press creates a session and one step, long press arms repeating, each repeat checks ownership, and all exit paths retire or cancel the session.
Hypothetical immediate-step interaction policy. The figure describes command eligibility, not measured timing or automatic cancellation of external work.

1. Choose one increment policy

For a hypothetical service setting, define a tap as one immediate increment on the initial press. A sustained hold enters repeat mode after the long-press threshold and then increments on each eligible repeat event. Releasing adds nothing. Leaving the control cancels further increments; it does not undo the initial step already applied.

This policy is useful because it makes the release path purely terminal. A product that requires commit-on-release needs a different matrix and must preserve its pending value separately. Do not mix immediate press increments with an inherited click handler that also increments.

The tagged event definitions distinguish PRESSED, LONG_PRESSED, LONG_PRESSED_REPEAT, PRESS_LOST and RELEASED. CLICKED can follow a long press when the interaction was not scrolled. Therefore using both repeat events and an unconditional click increment can produce an extra step at the end of a hold.

2. Make the action matrix explicit

Input or owner signal Session action Value change
Eligible fresh PRESSED Create current session; arm One bounded increment
LONG_PRESSED for armed session Enter repeating state None in this policy
LONG_PRESSED_REPEAT for current repeating session Retain ownership One bounded increment
RELEASED Retire session None
PRESS_LOST, INDEV_RESET, navigation or disable Cancel session; require fresh interaction None
CLICKED or SHORT_CLICKED No adjustment action None

Give each session an input identity, control identity and view generation. Every increment checks that these still describe the active adjustment. Clamp or reject at the model’s permitted limit using arithmetic that cannot overflow before the limit check. Reaching a limit must not wrap to the opposite endpoint merely because the user keeps holding.

If commands cross into another task, include the session generation in the queued command and validate it at the execution boundary. Canceling the visible control cannot recall a command that has already executed. Define whether queued but unexecuted repeat requests are discarded on cancellation and test that rule explicitly.

3. Verify what losing the press means in this build

In the v9.3.0 pointer implementation, an unlocked, non-scrolling active object can be replaced by a new hit-test result, and the old object receives PRESS_LOST. The PRESS_LOCK flag instead retains press ownership after the pointer slides away. Inspect the actual flags; do not assume every control reports loss whenever a finger crosses its visible edge.

If the product requires leave-to-cancel, remove incompatible press-lock behavior and qualify the real hit region. If it intentionally retains ownership outside the visible shape, document that interaction and provide another cancellation rule. A drawing boundary and an extended click area can differ, so use the input geometry rather than estimating from a screenshot.

After loss, do not rearm merely because a later event has a familiar target pointer. Require the new interaction boundary chosen by the input policy. For a conservative control, the owner can suppress further interaction until a physical release has been observed, then allow a fresh press. Test leaving, moving back inside while still down, releasing, and pressing again.

4. Cancel before changing the view

The navigation owner should retire the adjustment session before replacing the screen or disabling its controls. That ordering prevents a late repeat from changing the newly displayed setting. An input reset is a separate signal: the pinned reset path sends LV_EVENT_INDEV_RESET to its active pointer object and clears tracking; it should not be treated as a promise of normal RELEASED delivery.

The input API provides reset and wait-for-release operations, but these do not retire application-owned commands by themselves. When wait-for-release is used, cancel the action session immediately rather than postponing cleanup until some later widget event. Keep cancellation idempotent so navigation, reset and loss can converge without generating another command.

Prefer the built-in repeat events for this simple policy instead of adding an independent application repeat timer. If a separate scheduler is genuinely needed, its callback must use the same session eligibility check. Stopping the visual pressed state alone does not prove that a separate scheduler has stopped issuing changes.

5. Test timing as observed events, not a stopwatch promise

The input documentation exposes long-press and repeat timing configuration. Record those values with the test, along with read scheduling and tick behavior. Do not require an exact number of increments from wall-clock hold duration without accounting for when input processing actually runs.

For boundary qualification, replay samples just below, at and above the configured thresholds using the exact build. The v9.3.0 pointer code tests elapsed time with a strict greater-than comparison. A delayed UI iteration is not guaranteed to emit every repeat that a continuously serviced loop would have produced. Count the events observed and the commands admitted separately.

  1. Perform a short press/release. Expect one initial increment and no release increment.
  2. Hold through several repeat events. Expect one initial step plus one per admitted repeat, with no extra step when released.
  3. Move outside before and after repeating starts. Expect immediate session cancellation and no later command from that session.
  4. While still down, replace the view, disable the control and reset input in separate runs. Verify cancellation even if no normal release callback arrives.
  5. Test leave-and-return, queue delay, repeated cancellation and the maximum model value. Then release and perform a new press to prove recovery.

Save the event trace, session generation, cancellation cause and command count. The acceptance condition is not simply that repeating starts. It is that every termination path stops further work from the old session while a genuinely fresh interaction remains usable.

Similar Posts