Skip to content

Commit e79a36f

Browse files
committed
DragTrackModel: parkedProgress, keyboard, arc jumpOnTap docs, track.focused
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VxYXX43tySRcAP7c1j29w
1 parent 9b8f9d9 commit e79a36f

2 files changed

Lines changed: 71 additions & 9 deletions

File tree

‎lib/src/api/models/drag_track.dart‎

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,16 @@ enum DragTrackInput {
6363
/// changes, like [MotionModel]); dragging it writes the variable, so
6464
/// everything bound to the variable follows, and changing the variable
6565
/// elsewhere moves the node. Double tap resets to [defaultValue], the mouse
66-
/// wheel steps the value.
66+
/// wheel and the arrow keys (see [keyboard]) step the value.
6767
///
6868
/// Positions are in logical pixels relative to where the node is laid out:
6969
/// a fader cap laid out at the bottom of its slot has `y2: -250` for a
7070
/// 250px throw. Angles are in degrees, clockwise, 0 pointing up.
7171
///
7272
/// While dragged, the node and its children read `${track.value}`,
73-
/// `${track.progress}` (0 to 1) and `${track.dragging}`. Each change runs
74-
/// the node's `changed` reactions with `${result.value}`; letting go runs
75-
/// its `dragEnd` reactions.
73+
/// `${track.progress}` (0 to 1), `${track.dragging}` and
74+
/// `${track.focused}`. Each change runs the node's `changed` reactions with
75+
/// `${result.value}`; letting go runs its `dragEnd` reactions.
7676
@JsonSerializable()
7777
class DragTrackModel with Equatable, SerializableMixin {
7878
/// The track's geometry.
@@ -140,8 +140,11 @@ class DragTrackModel with Equatable, SerializableMixin {
140140
/// For a path: turn the node to follow the path's direction.
141141
final bool orientToPath;
142142

143-
/// Tapping the node's parent anywhere along the track moves the node
144-
/// there (a fader cap jumps to where its slot was tapped). Only for
143+
/// Tapping anywhere along the track moves the node there, then the drag
144+
/// goes on from there: a fader cap jumps to where its slot was tapped
145+
/// (the nearest point of a line or path), a dial to the angle tapped
146+
/// around its pivot, anywhere within the circle the node sweeps (the
147+
/// nearest valid value, snapped to [step]). Only for
145148
/// [DragTrackInput.follow].
146149
final bool jumpOnTap;
147150

@@ -172,6 +175,28 @@ class DragTrackModel with Equatable, SerializableMixin {
172175
/// physics and ignores [settleMs] for its length.
173176
final MotionCurve settleCurve;
174177

178+
/// Where the node is parked while the variable is outside the range
179+
/// (below [min], above [max]) or empty or not a number, as a progress
180+
/// along the track that may lie outside 0 to 1: an unrated 1 to 5 dial
181+
/// over -120..120 degrees parks its knob at -150 degrees with -0.125.
182+
/// Lines and arcs go on past their ends; a path stops at its ends. Null
183+
/// (the default) draws such a value at the nearest end.
184+
///
185+
/// Dragging a parked node starts from the nearest end, and the node
186+
/// glides ([settleMs]) between its parked place and the track. While
187+
/// parked, `${track.progress}` reads this progress.
188+
final double? parkedProgress;
189+
190+
/// Whether the track takes keyboard focus (tapping or dragging it
191+
/// focuses it, Tab reaches it) and the keys move it while focused: the
192+
/// right and up arrows step the value up by [step] (1% of the range
193+
/// without one), left and down step it down, Page Up and Page Down by 10
194+
/// steps, Home to [min] and End to [max]. Each key runs the node's
195+
/// `changed` reactions like a drag. On by default; `${track.focused}`
196+
/// tells whether it has focus, for a node to draw its own focus ring.
197+
/// The track is a slider to screen readers either way.
198+
final bool keyboard;
199+
175200
/// Creates a [DragTrackModel].
176201
const DragTrackModel({
177202
this.shape = DragTrackShape.line,
@@ -199,10 +224,17 @@ class DragTrackModel with Equatable, SerializableMixin {
199224
this.overscroll = 0,
200225
this.settleMs = 0,
201226
this.settleCurve = MotionCurve.easeOutCubic,
227+
this.parkedProgress,
228+
this.keyboard = true,
202229
});
203230

204231
/// The names of the live values the `track` variable has.
205-
static const List<String> liveValues = ['value', 'progress', 'dragging'];
232+
static const List<String> liveValues = [
233+
'value',
234+
'progress',
235+
'dragging',
236+
'focused',
237+
];
206238

207239
/// The names of the values a `dragEnd` reaction gets in `${result}`:
208240
/// the final `value`, the `startValue` the drag began at, and the
@@ -217,8 +249,24 @@ class DragTrackModel with Equatable, SerializableMixin {
217249
return ((value - min) / (max - min)).clamp(0.0, 1.0);
218250
}
219251

252+
/// Whether [value] is drawn at [parkedProgress]: outside the range or not
253+
/// a number (NaN stands for an empty variable), when the track has one.
254+
bool isParked(double value) {
255+
if (parkedProgress == null) return false;
256+
if (value.isNaN) return true;
257+
final double lo = min < max ? min : max;
258+
final double hi = min < max ? max : min;
259+
return value < lo || value > hi;
260+
}
261+
262+
/// Where the node is drawn for [value]: its [progressOf], or
263+
/// [parkedProgress] when [isParked].
264+
double restProgressOf(double value) =>
265+
isParked(value) ? parkedProgress! : (value.isNaN ? 0 : progressOf(value));
266+
220267
/// The value at [progress] along the track, snapped to [step].
221-
double valueAt(double progress) => snap(min + (max - min) * progress.clamp(0.0, 1.0));
268+
double valueAt(double progress) =>
269+
snap(min + (max - min) * progress.clamp(0.0, 1.0));
222270

223271
/// [value] snapped to [step] (counted from [min]) and kept in range.
224272
double snap(double value) {
@@ -234,7 +282,7 @@ class DragTrackModel with Equatable, SerializableMixin {
234282
return v;
235283
}
236284

237-
/// How far one wheel notch moves the value.
285+
/// How far one wheel notch (or one arrow key) moves the value.
238286
double get wheelStep => step > 0 ? step : (max - min).abs() / 100;
239287

240288
/// The rotation of an arc at [progress], in degrees.
@@ -269,6 +317,9 @@ class DragTrackModel with Equatable, SerializableMixin {
269317
double? overscroll,
270318
int? settleMs,
271319
MotionCurve? settleCurve,
320+
double? parkedProgress,
321+
bool clearParked = false,
322+
bool? keyboard,
272323
}) => DragTrackModel(
273324
shape: shape ?? this.shape,
274325
variable: variable ?? this.variable,
@@ -295,6 +346,10 @@ class DragTrackModel with Equatable, SerializableMixin {
295346
overscroll: overscroll ?? this.overscroll,
296347
settleMs: settleMs ?? this.settleMs,
297348
settleCurve: settleCurve ?? this.settleCurve,
349+
parkedProgress: clearParked
350+
? null
351+
: (parkedProgress ?? this.parkedProgress),
352+
keyboard: keyboard ?? this.keyboard,
298353
);
299354

300355
@override
@@ -324,6 +379,8 @@ class DragTrackModel with Equatable, SerializableMixin {
324379
overscroll,
325380
settleMs,
326381
settleCurve,
382+
parkedProgress,
383+
keyboard,
327384
];
328385

329386
/// Creates a [DragTrackModel] from JSON.

‎lib/src/api/models/drag_track.g.dart‎

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)