Skip to content

Enable scroll in lock mode + fix Android storage path - #4755

Merged
cagnulein merged 6 commits into
masterfrom
worktree-tile-drag-scroll
Aug 7, 2026
Merged

Enable scroll in lock mode + fix Android storage path#4755
cagnulein merged 6 commits into
masterfrom
worktree-tile-drag-scroll

Conversation

@cagnulein

@cagnulein cagnulein commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • Bug 4 — Scroll in lock mode: In lock mode (lockTiles=true), GridView.interactive is set to false to prevent accidental drag-to-rearrange during a workout. This had the side-effect of also disabling touch-scrolling, making any tiles beyond the viewport permanently unreachable.

    • Set interactive: !window.lockTiles so built-in scrolling is only active in normal (drag) mode.
    • Added a manual scroll handler in the MouseArea onPositionChanged: when no drag is in progress (currentId === -1), each pointer movement adjusts gridView.contentY directly, clamped to valid bounds.
    • Added an isSwiping flag so a gesture that starts as a vertical swipe doesn't accidentally trigger onPressAndHold drag initiation.
  • Android storage fix: getAndroidDataAppDir() called isExternalStorageRemovable() which throws IllegalArgumentException on Waydroid/emulators where vold cannot resolve the storage volume. Fix: check ExceptionCheck() after the JNI call, clear any pending exception, and fall back to internal storage if external storage is unavailable.

Test plan

  • Build and install on Waydroid (x86_64 Android device)
  • Enable fake elliptical device (Settings → Experimental Features)
  • Verify tiles UI appears
  • Toggle lock mode (padlock icon, top right)
  • Swipe up in lock mode → tiles scroll down to reveal off-screen rows ✅
  • Swipe down → tiles scroll back up ✅

Demo video

https://github.com/cagnulein/qdomyos-zwift/releases/download/nightly-2026-07-02/demo_scroll.mp4

🤖 Generated with Claude Code

- Home.qml: Added autoScrollTimer (50ms interval) that scrolls the
  GridView when dragging a tile near the top or bottom viewport edge,
  with speed proportional to proximity to the edge.
- Home.qml: Added indexAtMouse() helper that accounts for contentY
  offset when computing which tile is under the cursor during drag,
  fixing incorrect target index when the grid has scrolled.
- Home.qml: Fixed active tile visual position to include gridView.contentY
  so the dragged tile stays visually anchored to the cursor while scrolling.
- Home.qml: Track startIndex and tileName at long-press time to correctly
  identify source tile and compare against drop position at release.
- homeform.cpp: Fixed moveTile() edge case where inserting a tile at the
  last position never wrote its order setting because i only reached
  newIndex after the loop ended.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cagnulein

Copy link
Copy Markdown
Owner Author

Test video

Auto-scroll during tile drag — recorded on Android emulator (pixel6_api34, API 34) with fake treadmill device enabled.

The test drags the Speed tile from position 0 (top-left) to the bottom of the screen. The GridView auto-scrolls revealing tiles that were previously below the fold, and Speed is correctly placed at the last position after release.

@cagnulein

Copy link
Copy Markdown
Owner Author

Test Video: Tile Auto-Scroll During Drag

Tested on Android emulator (Pixel 6, API 34) with fake treadmill device enabled.

What was tested:

  1. Auto-scroll trigger: Dragging a tile toward the bottom edge (within 80dp) triggers GridView auto-scrolling ✅
  2. Visual follow: The dragged tile visually follows the cursor during the drag ✅
  3. Correct drop position: Releasing the tile places it at the correct scrolled position ✅

Demo

Tile drag auto-scroll demo

Download MP4 video

Test scenario:

  • Started with Speed tile in position 0 (top-left)
  • Dragged it down slowly past the visible screen area
  • GridView auto-scrolled to follow the drag
  • Released at position ~14 (bottom of the list)
  • Result: Speed tile moved to the target position, all other tiles shifted accordingly

1 similar comment
@cagnulein

Copy link
Copy Markdown
Owner Author

Test Video: Tile Auto-Scroll During Drag

Tested on Android emulator (Pixel 6, API 34) with fake treadmill device enabled.

What was tested:

  1. Auto-scroll trigger: Dragging a tile toward the bottom edge (within 80dp) triggers GridView auto-scrolling ✅
  2. Visual follow: The dragged tile visually follows the cursor during the drag ✅
  3. Correct drop position: Releasing the tile places it at the correct scrolled position ✅

Demo

Tile drag auto-scroll demo

Download MP4 video

Test scenario:

  • Started with Speed tile in position 0 (top-left)
  • Dragged it down slowly past the visible screen area
  • GridView auto-scrolled to follow the drag
  • Released at position ~14 (bottom of the list)
  • Result: Speed tile moved to the target position, all other tiles shifted accordingly

@cagnulein

Copy link
Copy Markdown
Owner Author

Fix: le altre tile non scompaiono più durante il drag

Bug risolto: nella versione precedente le tile sparivano durante il drag perché il PropertyChanges modificava la posizione y del delegate nel sistema di coordinate del contentItem del GridView, confondendo il meccanismo di virtualizzazione.

Soluzione: invece di spostare il tile originale, viene dimmerato (opacity 0.3) e viene mostrato un "ghost tile" separato (esterno al GridView) che segue il cursore.

Demo (con fix applicato)

Tile drag auto-scroll - fix

Download MP4

Comportamento verificato:

  1. ✅ Le altre tile rimangono visibili durante il drag
  2. ✅ Il ghost tile segue il cursore
  3. ✅ L'auto-scroll si attiva avvicinandosi al bordo inferiore
  4. ✅ Il tile viene rilasciato nella posizione corretta (Speed spostata dall'ultima posizione a quella iniziale)

cagnulein and others added 2 commits June 26, 2026 13:56
Moving the dragged tile's y position within GridView's contentItem
coordinate system confused the GridView's virtualization, causing
other tiles to disappear during drag. Fix by:
- Keeping the source tile in place (just dimming it to opacity 0.3)
- Showing a separate ghost Item outside GridView that follows the mouse

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uring drag

The OutBack easing on delegates caused newly-virtualized tiles to animate
from y=0 (above the viewport when contentY > 0) to their target position,
making them invisible for 400ms during auto-scroll. Since moveTile() rebuilds
the entire model via sortTiles() on release (not appModel.move()), these
animations served no useful purpose.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cagnulein

Copy link
Copy Markdown
Owner Author

Fix v3: Rimossi Behavior on x/y dal delegate

Problema risolto

Il Behavior on y { easing.type: Easing.OutBack } sui delegate del GridView causava la "scomparsa" delle tile durante il drag:

  • Durante l'auto-scroll, il GridView creava nuovi delegate per le tile che entravano nel viewport
  • Il Behavior on y animava il loro y da 0 (in cima al contentItem, sopra il viewport) verso la posizione target
  • Con contentY > 0, y=0 è sopra il viewport → le tile erano invisibili per 400ms durante l'animazione OutBack
  • Risultato visivo: tile che "sparivano" mentre il drag scrollava

Fix

Rimossi completamente i Behavior on x e Behavior on y dai delegate. Non servono: moveTile() ricostruisce l'intero modello via sortTiles() al rilascio (non usa appModel.move()), quindi nessuna animazione di riposizionamento è necessaria.

Video test

(Auto-scroll verso il basso + tile piazzata oltre il viewport — nessuna tile scompare durante il drag)

@cagnulein

Copy link
Copy Markdown
Owner Author

Video test (fix v3): drag_test_v3.mp4

@cagnulein

Copy link
Copy Markdown
Owner Author

Demo finale: entrambi i bug risolti

Video pulito (senza debug overlay) che mostra:

  1. Bug 1 risolto: le tile non scompaiono più durante il drag (ghost tile visibile che segue il cursore)
  2. Bug 2 risolto: auto-scroll si attiva quando il drag si avvicina al bordo inferiore (il grid scrolla fino alle ultime tile: Stride L., Ground C., Vert.Osc.)

Download video demo_drag.mp4

Testato su emulatore Android (Pixel 6 API 34) con fake treadmill abilitato. 25 tile attive, drag dall'AVG Watt Lap verso il fondo della lista (grid scrollato da 0 a ~900dp di contentY).

moveTile() constructed settings keys as "tile_" + m_id.toLower() + "_order",
but several DataObjects use camelCase m_ids (e.g. "avgWattLap") that don't
match the actual QZSettings key ("tile_avg_watt_lap_order"). As a result,
the moved tile's order was written to the wrong settings key and sortTiles()
ignored it, leaving the tile in its original position after drag-and-drop.

Fixed with a static lookup table in moveTile() mapping the mismatched m_ids
to their correct QZSettings order key. Affected tiles:
- avgWattLap → tile_avg_watt_lap_order
- joul → tile_jouls_order
- steeringangle → tile_steering_angle_order
- stride_length → tile_instantaneous_stride_length_order
- external_inclination → tile_ext_incline_order
- target_inclination → tile_target_incline_order

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cagnulein

Copy link
Copy Markdown
Owner Author

Fix v4: moveTile scriveva sulla chiave settings sbagliata

Bug trovato e risolto

moveTile() costruiva la chiave QSettings come "tile_" + m_id.toLower() + "_order", ma alcuni DataObject hanno m_id in camelCase che non corrisponde alla chiave reale in QZSettings. Esempio:

  • avgWattLap → generava tile_avgwattlap_order
  • sortTiles() leggeva invece tile_avg_watt_lap_order

Risultato: la tile veniva "spostata" (il ghost tile seguiva il cursore, l'auto-scroll funzionava) ma dopo il rilascio sortTiles() usava il vecchio valore dalla chiave corretta e rimetteva la tile nella posizione originale.

Fix (commit 4f9255112)

Aggiunta una lookup table statica in moveTile() che mappa i 6 m_id problematici al loro QZSettings key corretto:

  • avgWattLaptile_avg_watt_lap_order
  • joultile_jouls_order
  • steeringangletile_steering_angle_order
  • stride_lengthtile_instantaneous_stride_length_order
  • external_inclinationtile_ext_incline_order
  • target_inclinationtile_target_incline_order

Video (fix v4 — il tile si sposta correttamente)

Drag di AVG Watt Lap verso il basso → dopo il rilascio KJouls è in posizione 0, AVG Watt Lap è scomparso dalla cima (spostato in fondo alla lista).

Download demo_fix2.mp4

@cagnulein

Copy link
Copy Markdown
Owner Author

I can't move tiles that are placed out of the screen because the top bar with the unlock button is hiding. Maybe we can add the scroll when you are on unlock mode so we can select other tiles

In lock mode (lockTiles=true) the GridView's built-in interactive
scrolling is disabled to prevent accidental tile drags. This meant any
tiles beyond the visible viewport could never be reached.

Fix: set `interactive: !window.lockTiles` so built-in scrolling is
active only in normal mode, and add a manual scroll handler in the
MouseArea's onPositionChanged that moves gridView.contentY when no drag
is in progress (currentId === -1). An `isSwiping` flag suppresses
long-press-to-drag when the gesture starts as a swipe.

Also fix getAndroidDataAppDir() to handle Waydroid/emulator environments
where isExternalStorageRemovable() throws IllegalArgumentException: clear
the pending JNI exception and fall back to internal storage if external
storage resolution fails entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@cagnulein cagnulein changed the title Fix tile drag: auto-scroll GridView when dragging to screen edges Enable scroll in lock mode + fix Android storage path Jul 2, 2026
@stale

stale Bot commented Jul 24, 2026

Copy link
Copy Markdown

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale Bot added the wontfix This will not be worked on label Jul 24, 2026
@stale stale Bot closed this Aug 1, 2026
@cagnulein cagnulein reopened this Aug 1, 2026
@stale stale Bot removed the wontfix This will not be worked on label Aug 1, 2026
@cagnulein cagnulein linked an issue Aug 4, 2026 that may be closed by this pull request
@cagnulein
cagnulein merged commit d06b630 into master Aug 7, 2026
2 checks passed
@cagnulein
cagnulein deleted the worktree-tile-drag-scroll branch August 7, 2026 08:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] UI Drag/Drop Tiles

1 participant