Loading...
Searching...
No Matches
DragList.h
Go to the documentation of this file.
1// Copyright (c) 2025 Dave R. Smith.
2// Malena Framework — Licensed under PolyForm Noncommercial 1.0.0; commercial use requires a paid license. See LICENSE.
3
4//
5// DragList — a reusable drag-to-reorder + tap controller for a vertical list of
6// fixed-height rows. It owns ONLY the interaction state machine; it draws nothing.
7// Each frame the owner sets the list geometry + callbacks, then feeds the cursor
8// and button state to update(); the owner renders the rows using displayOrder()
9// (the dragged row floats to its drop slot), isDragging()/dragIndex() for the gap,
10// and ghostY() for the floating row. On release it fires onReorder(from,to) after
11// a real drag, or onTap(index) after a click.
12//
13// This replaces the hand-rolled per-hook drag bookkeeping (press edge tracking,
14// threshold, drop-slot math, order permutation).
15//
16#ifndef MALENA_DRAGLIST_H
17#define MALENA_DRAGLIST_H
18
19#include <algorithm>
20#include <cmath>
21#include <functional>
22#include <vector>
23
24namespace ml {
25
27{
28public:
29 static constexpr float kDragThreshold = 6.f; // px before a press counts as a drag
30
34 void setGeometry(float hitX0, float hitX1, float rowTop, int count, float rowHeight)
35 {
36 _hitX0 = hitX0; _hitX1 = hitX1; _rowTop = rowTop; _count = count; _rowH = rowHeight;
37 }
38 void onReorder(std::function<void(int from, int to)> cb) { _onReorder = std::move(cb); }
39 void onTap (std::function<void(int index)> cb) { _onTap = std::move(cb); }
40
44 bool update(float localX, float localY, bool down, bool live)
45 {
46 bool changed = false;
47 if (live && _count > 0 && _onReorder)
48 {
49 if (down && !_prevDown) // press → maybe grab a row
50 {
51 if (localX >= _hitX0 && localX <= _hitX1
52 && localY >= _rowTop && localY < _rowTop + _count * _rowH)
53 { _dragIdx = (int)((localY - _rowTop) / _rowH); _startY = localY; _dragging = false; }
54 }
55 else if (down && _dragIdx >= 0) // held → drag
56 {
57 if (!_dragging && std::abs(localY - _startY) > kDragThreshold) { _dragging = true; _dropIdx = _dragIdx; changed = true; }
58 if (_dragging)
59 {
60 _mouseY = localY;
61 _dropIdx = std::max(0, std::min((int)((localY - _rowTop + _rowH * 0.5f) / _rowH), _count - 1));
62 changed = true; // ghost tracks the cursor
63 }
64 }
65 else if (!down && _prevDown && _dragIdx >= 0) // release → reorder or tap
66 {
67 if (_dragging) { if (_dropIdx != _dragIdx && _dropIdx >= 0) _onReorder(_dragIdx, _dropIdx); }
68 else if (_onTap) _onTap(_dragIdx);
69 _dragIdx = _dropIdx = -1; _dragging = false; changed = true;
70 }
71 }
72 else if (!down) { _dragIdx = _dropIdx = -1; _dragging = false; }
73
74 _prevDown = down;
75 return changed;
76 }
77
80 std::vector<int> displayOrder(int count) const
81 {
82 std::vector<int> order(count);
83 for (int i = 0; i < count; ++i) order[i] = i;
84 if (_dragging && _dragIdx >= 0 && _dragIdx < count
85 && _dropIdx >= 0 && _dropIdx < count && _dropIdx != _dragIdx)
86 {
87 order.erase(order.begin() + _dragIdx);
88 order.insert(order.begin() + _dropIdx, _dragIdx);
89 }
90 return order;
91 }
92
93 bool isDragging() const { return _dragging; }
94 int dragIndex() const { return _dragIdx; }
95 int dropIndex() const { return _dropIdx; }
96
98 float ghostY() const
99 {
100 const float gy = _mouseY - _rowH * 0.45f;
101 return std::max(_rowTop - 6.f, std::min(gy, _rowTop + (_count - 1) * _rowH + 6.f));
102 }
103
104private:
105 float _hitX0 = 0.f, _hitX1 = 0.f, _rowTop = 0.f, _rowH = 44.f;
106 int _count = 0;
107 int _dragIdx = -1, _dropIdx = -1;
108 bool _dragging = false, _prevDown = false;
109 float _startY = 0.f, _mouseY = 0.f;
110 std::function<void(int, int)> _onReorder;
111 std::function<void(int)> _onTap;
112};
113
114} // namespace ml
115
116#endif // MALENA_DRAGLIST_H
void setGeometry(float hitX0, float hitX1, float rowTop, int count, float rowHeight)
Definition DragList.h:34
float ghostY() const
Clamped Y for the floating ghost row (owner draws the dragged row here).
Definition DragList.h:98
void onReorder(std::function< void(int from, int to)> cb)
Definition DragList.h:38
void onTap(std::function< void(int index)> cb)
Definition DragList.h:39
static constexpr float kDragThreshold
Definition DragList.h:29
int dragIndex() const
Definition DragList.h:94
bool update(float localX, float localY, bool down, bool live)
Definition DragList.h:44
bool isDragging() const
Definition DragList.h:93
int dropIndex() const
Definition DragList.h:95
std::vector< int > displayOrder(int count) const
Definition DragList.h:80
Definition Animate.h:25