fix(input): refuse non-finite region bounds, keep narrow regions movable, republish hot handles
This commit is contained in:
1 parent
4681d7015b
commit
7cc06f07f5
7 files changed
+263
-19
No files matched your search
@@ -83,16 +83,22 @@ using plot::RegionGrab;
|
||||
/// dragging either of the cursors that bound it, because its edges *are* those
|
||||
/// cursors -- no second path, no second tolerance.
|
||||
/// 2. **Regions next**, resolved nearest-first against the geometry `Plot::layout()`
|
||||
/// measured: a bound inside the grab radius beats the body (so the bound of a
|
||||
/// measured: a bound within the grab radius beats the body (so the bound of a
|
||||
/// wide region is grabbable even though the pointer is inside the fill), the
|
||||
/// nearer of the two bounds wins, and with no bound in range the topmost region
|
||||
/// whose interior holds the pointer is the body.
|
||||
/// whose interior holds the pointer is the body. The radius is the one above, cut
|
||||
/// back when a region is too narrow to hold a band at each end *and* an interior:
|
||||
/// a region narrower than twice it would otherwise have every interior point
|
||||
/// within a bound's reach, and could be resized but never moved.
|
||||
/// 3. **Nothing hot**, and the press pans.
|
||||
///
|
||||
/// A region narrower than a few logical pixels is a click rather than an edge grab:
|
||||
/// its two bounds are under the pointer together, so an edge would always win and
|
||||
/// the region could never be moved again once collapsed. Such a region is
|
||||
/// grabbed by its body instead, widened to that same minimum, and it stays movable.
|
||||
/// grabbed by its body instead, widened to that same minimum, and it stays movable --
|
||||
/// and between that minimum and twice the grab radius, where the two bounds would
|
||||
/// still cover every interior point, the edge bands are cut back until the middle is
|
||||
/// left, so a region in that band of widths can be moved as well as resized.
|
||||
/// The *body* drag translates both bounds by one delta -- the width is preserved
|
||||
/// exactly -- while a bound drag moves one end and leaves the other where it was,
|
||||
/// so a drag that crosses one bound over the other swaps them rather than inverting
|
||||
@@ -103,7 +109,8 @@ using plot::RegionGrab;
|
||||
/// both ends back to where they started and never move at all.
|
||||
///
|
||||
/// The hovered and grabbed handles are published to the plot
|
||||
/// (`Plot::set_hot_handles()`) after every pointer event, so the renderer draws them
|
||||
/// (`Plot::set_hot_handles()`) after every event that can move the geometry under the
|
||||
/// pointer -- a pointer event, a wheel notch, a key stroke -- so the renderer draws them
|
||||
/// highlighted without knowing what a pointer is; `hovered_cursor()`,
|
||||
/// `grabbed_cursor()`, `hovered_region()`, `grabbed_region()` and `region_grab()` are
|
||||
/// the same answer for a host that wants it directly -- a resize cursor, a tooltip,
|
||||
@@ -190,7 +197,8 @@ class Controller {
|
||||
/// The region the pointer is on, or `kInvalid`. `region_grab()` says which zone.
|
||||
u64 hovered_region() const;
|
||||
/// Which zone of `region` the pointer is on: the bound nearest within the grab
|
||||
/// radius (`Left`/`Right`), else `Body` when the pointer is inside the interval --
|
||||
/// radius (`Left`/`Right`) -- cut back for a narrow region, see `zone_hit()` -- else
|
||||
/// `Body` when the pointer is inside the interval --
|
||||
/// widened to the minimum a collapsed region keeps -- and `None` when it is on
|
||||
/// neither, when the pointer is outside the plot area, or when no pointer has been
|
||||
/// seen yet. What a host sets its resize cursor from; an id that names no region
|
||||
@@ -229,6 +237,10 @@ class Controller {
|
||||
RegionGrab zone = RegionGrab::None;
|
||||
f32 distance = 0.0f;
|
||||
};
|
||||
/// The band a bound answers to is the controller's grab radius, cut back so that a
|
||||
/// narrow region keeps an interior a body drag can reach (see the class comment):
|
||||
/// `min(radius, (width - minimum interior) / 2)`, and no band at all when the two
|
||||
/// bounds are closer together than the minimum width that separates them.
|
||||
ZoneHit zone_hit(const RegionLayout& region, f32 px, f32 py) const;
|
||||
/// Resolves that hit against the geometry `Plot::layout()` measured -- the same
|
||||
/// rects the renderer paints, so what is drawn and what can be grabbed cannot
|
||||
@@ -242,8 +254,10 @@ class Controller {
|
||||
/// the width -- so a changed pointer motion never accumulates error.
|
||||
bool begin_handle_drag(const PointerEvent& e);
|
||||
/// Publishes what the pointer is on to the plot, for the renderer to highlight
|
||||
/// (see HotHandles). Called after every pointer event, so hover and grab are the
|
||||
/// same answer the hit test gave.
|
||||
/// (see HotHandles). Called after every event that can move the geometry under the
|
||||
/// pointer -- a pointer event, a wheel notch, a key stroke -- so hover and grab are
|
||||
/// always the answer the hit test gives for the geometry of this frame rather than
|
||||
/// the geometry of the last time the pointer moved.
|
||||
void publish_hot();
|
||||
|
||||
/// The data x a zoom of `axis` holds fixed, for a gesture at (px, py). The
|
||||
|
||||
@@ -161,6 +161,12 @@ class Plot {
|
||||
/// onto the sample grid as it is added -- exactly as `add_cursor()` places a
|
||||
/// snapping cursor -- so a region handed to a plot that already holds samples is on
|
||||
/// the grid from its first frame.
|
||||
///
|
||||
/// A region whose bounds are not both finite is *refused*: nothing is added, and the
|
||||
/// returned id names no region (`region()` reads a default for it and `region_count()`
|
||||
/// does not count it). Storing one would be worse than useless, because a `Free` region
|
||||
/// holding a bound that is not a number could never be dragged back -- every motion
|
||||
/// re-derives both bounds from the stored ones.
|
||||
u32 add_region(const Region& region);
|
||||
|
||||
// --- accessors --------------------------------------------------------
|
||||
@@ -340,6 +346,11 @@ class Plot {
|
||||
/// bounds under the pointer and put them on the sample grid once, on release,
|
||||
/// instead of both ends sticking as the pointer moves. Also drops the region's
|
||||
/// cached statistics.
|
||||
///
|
||||
/// A pair of bounds that is not both finite is *refused*: `std::min`/`std::max` would
|
||||
/// propagate a NaN through both stored bounds and a finite partner would be lost with
|
||||
/// it, so a rejected call changes nothing at all -- the interval stays as it was, and
|
||||
/// so do the statistics cached for it.
|
||||
void set_region(u32 id, f64 x0, f64 x1);
|
||||
|
||||
/// Pulls a snapping region's bounds onto the sample grid: onto the nearest sample
|
||||
|
||||
@@ -20,9 +20,11 @@ enum class RegionSnap {
|
||||
/// The bounds are **ordered**: `Plot::set_region()` stores their min and max, so
|
||||
/// `x0 > x1` cannot be held however the bounds arrived -- which is what keeps a
|
||||
/// drag that crosses one bound over the other a *swap* (the region stays draggable
|
||||
/// and never turns inside out) rather than a degenerate interval. The interval is
|
||||
/// inclusive of both ends: the samples a region covers are those whose x is at or
|
||||
/// after `x0` and at or before `x1`.
|
||||
/// and never turns inside out) rather than a degenerate interval. A pair that is not
|
||||
/// both finite is not a pair of bounds and is refused rather than stored (see
|
||||
/// `Plot::set_region()`); the bounds below are finite once the region is in a plot.
|
||||
/// The interval is inclusive of both ends: the samples a region covers are those whose x
|
||||
/// is at or after `x0` and at or before `x1`.
|
||||
///
|
||||
/// A region names no x axis, unlike a cursor: it shades an interval of the plot's
|
||||
/// data area as a whole -- the rect `PlotLayout::axes` and the grid are built from
|
||||
|
||||
Reference in new issue
Block a user