adapters: route pointer events by cell rect
This commit is contained in:
1 parent
7d8c453232
commit
e5b7faadec
6 files changed
+145
-93
No files matched your search
+25
-21
@@ -455,9 +455,11 @@ void Host::set_figure(Figure* figure) noexcept {
|
||||
row_rects_.clear(); // no figure, no rows: the handlers fall back to `controller_`
|
||||
return;
|
||||
}
|
||||
const u32 rows = figure_->row_count();
|
||||
row_controllers_.reserve(rows);
|
||||
for (u32 i = 0; i < rows; ++i) {
|
||||
// One controller per **plot** (a row of several cells has one for each), indexed by the
|
||||
// flattened plot index the figure uses.
|
||||
const u32 plots = figure_->plot_count();
|
||||
row_controllers_.reserve(plots);
|
||||
for (u32 i = 0; i < plots; ++i) {
|
||||
row_controllers_.push_back(std::make_unique<input::Controller>(figure_->plot(i)));
|
||||
}
|
||||
// The new figure's rects go into the cache now, not at the next frame: an event
|
||||
@@ -469,8 +471,10 @@ void Host::set_figure(Figure* figure) noexcept {
|
||||
|
||||
void Host::refresh_row_rects() noexcept {
|
||||
// The one `Figure::layout()` per call: every event handler reads `row_rects_`
|
||||
// instead of paying this again, which is the whole point of the cache (R7).
|
||||
row_rects_ = figure_ != nullptr ? figure_->layout().rows : std::vector<Rect>{};
|
||||
// instead of paying this again, which is the whole point of the cache (R7). The rects
|
||||
// are the figure's **cells** (one per plot, flattened), which for a figure with one
|
||||
// cell per row are exactly its row rects.
|
||||
row_rects_ = figure_ != nullptr ? figure_->layout().cells : std::vector<Rect>{};
|
||||
}
|
||||
|
||||
input::Controller* Host::row_controller(u32 row) noexcept {
|
||||
@@ -486,8 +490,7 @@ input::Controller& Host::controller_at(f32 x, f32 y) noexcept {
|
||||
// frame stale, which is the documented worst case.
|
||||
const std::vector<Rect>& rows = row_rects_;
|
||||
if (rows.empty() || rows.size() != row_controllers_.size()) return controller_;
|
||||
(void)x; // the rows tile the figure vertically, so y alone picks one -- see row_in()
|
||||
const u32 row = row_in(rows, y);
|
||||
const u32 row = row_in(rows, x, y);
|
||||
last_row_ = row;
|
||||
input::Controller& controller = *row_controllers_[row];
|
||||
// The row's own viewport, so the geometry this controller hit-tests is the geometry
|
||||
@@ -506,7 +509,6 @@ void Host::feed_pointer_event(const input::PointerEvent& e) noexcept {
|
||||
|
||||
input::Controller& Host::pointer_target(f32 x, f32 y, input::Button button, bool pressed,
|
||||
bool released) noexcept {
|
||||
(void)x; // the rows tile the figure vertically, so y alone picks one -- see row_in()
|
||||
if (figure_ == nullptr || row_controllers_.empty()) return controller_;
|
||||
const std::vector<Rect>& rows = row_rects_;
|
||||
if (rows.empty() || rows.size() != row_controllers_.size()) return controller_;
|
||||
@@ -523,7 +525,7 @@ input::Controller& Host::pointer_target(f32 x, f32 y, input::Button button, bool
|
||||
// drag, and its press must not steal the capture to the row it landed in.
|
||||
if (released && button == captured_button_) captured_ = false;
|
||||
} else {
|
||||
row = row_in(rows, y);
|
||||
row = row_in(rows, x, y);
|
||||
if (pressed) {
|
||||
captured_ = true;
|
||||
captured_row_ = row;
|
||||
@@ -541,27 +543,29 @@ input::Controller& Host::pointer_target(f32 x, f32 y, input::Button button, bool
|
||||
|
||||
u32 Host::pointer_row(const std::vector<Rect>& rows) noexcept {
|
||||
if (captured_ && captured_row_ < rows.size()) return captured_row_;
|
||||
return row_in(rows, last_pointer_y_);
|
||||
return row_in(rows, last_pointer_x_, last_pointer_y_);
|
||||
}
|
||||
|
||||
u32 Host::row_in(const std::vector<Rect>& rows, f32 y) noexcept {
|
||||
u32 Host::row_in(const std::vector<Rect>& rows, f32 x, f32 y) noexcept {
|
||||
if (rows.empty()) return 0u;
|
||||
// The row the pixel is in, or -- when it is in none of them, which is the row gap a
|
||||
// host has asked for with `Figure::set_row_gap()` or the figure's own margins -- the
|
||||
// row whose rect is nearest. Selection by distance rather than by "the first that
|
||||
// matches" is what makes a pixel in a gap belong to the row it is *near*: the rows
|
||||
// tile the figure at the default gap of 0, so this is unreachable until a host asks
|
||||
// for one, and a gesture in a visible gap must not be routed to row 0 by default.
|
||||
// The cell the pixel is in, or -- when it is in none of them, which is the row gap or
|
||||
// column gap a host has asked for with `Figure::set_row_gap()`/`set_col_gap()` or the
|
||||
// figure's own margins -- the cell whose rect is nearest (squared distance from the
|
||||
// pixel to the rect). Selection by distance rather than by "the first that matches" is
|
||||
// what makes a pixel in a gap belong to the cell it is *near*, and a gesture in a
|
||||
// visible gap must not be routed to plot 0 by default. For a figure with one cell per
|
||||
// row every cell spans the figure's width, so this reduces to the nearest row by y.
|
||||
u32 row = 0u;
|
||||
f32 best = std::numeric_limits<f32>::infinity();
|
||||
for (u32 i = 0; i < rows.size(); ++i) {
|
||||
const f32 top = rows[i].y;
|
||||
const f32 bottom = rows[i].y + rows[i].h;
|
||||
const f32 distance = y < top ? top - y : (y >= bottom ? y - bottom : 0.0f);
|
||||
const Rect& r = rows[i];
|
||||
const f32 dx = x < r.x ? r.x - x : (x >= r.right() ? x - r.right() : 0.0f);
|
||||
const f32 dy = y < r.y ? r.y - y : (y >= r.bottom() ? y - r.bottom() : 0.0f);
|
||||
const f32 distance = dx * dx + dy * dy;
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
row = i;
|
||||
if (distance == 0.0f) break; // inside this row: nothing can be nearer
|
||||
if (distance == 0.0f) break; // inside this cell: nothing can be nearer
|
||||
}
|
||||
}
|
||||
return row;
|
||||
|
||||
+19
-15
@@ -121,18 +121,21 @@ class Host {
|
||||
/// `plot_` is then untouched by any of this.
|
||||
void set_figure(Figure* figure) noexcept;
|
||||
Figure* figure() noexcept { return figure_; }
|
||||
/// One figure row's controller -- the one a gesture on that row reaches -- or
|
||||
/// null while no figure is set or the row does not exist. A host reads a drag
|
||||
/// One figure plot's controller -- the one a gesture on that plot reaches -- or null
|
||||
/// while no figure is set or it does not exist. `row` is the **flattened plot index**
|
||||
/// (`Figure::plot_count()` of them: the row count when no row holds several cells). A host reads a drag
|
||||
/// mode or a hover out of it (`controller()` is the single-plot loop's, and is
|
||||
/// not the one figure gestures go to).
|
||||
input::Controller* row_controller(u32 row) noexcept;
|
||||
/// The controller an event at framebuffer pixel (x, y) belongs to: `controller_` --
|
||||
/// the single-plot loop -- while no figure is set, and the controller of the figure
|
||||
/// row that pixel lands in otherwise. The row rects come from the cache
|
||||
/// plot (cell) that pixel lands in otherwise -- x and y both pick it, as a row may hold
|
||||
/// several cells side by side. The cell rects come from the cache
|
||||
/// `refresh_row_rects()` fills -- the same ones the renderer draws -- so a gesture
|
||||
/// lands on the row it was aimed at and no event lays the figure out; a pixel
|
||||
/// inside no row -- the row gap a host asked for with `Figure::set_row_gap()`,
|
||||
/// or a margin -- is the *nearest* row rather than dropped or defaulted, so a drag
|
||||
/// inside no cell -- the row or column gap a host asked for with
|
||||
/// `Figure::set_row_gap()`/`set_col_gap()`, or a margin -- is the *nearest* cell rather than
|
||||
/// dropped or defaulted, so a drag
|
||||
/// that leaves the data area still ends where it should.
|
||||
///
|
||||
/// It also hands that row its own viewport, which is what keeps the controller's
|
||||
@@ -217,11 +220,12 @@ class Host {
|
||||
void on_scroll(f64 dx, f64 dy) noexcept;
|
||||
void on_key(int key, int action, int mods) noexcept;
|
||||
|
||||
/// The index of the row of `rows` a framebuffer y belongs to: the row it is in, or the nearest
|
||||
/// row when it is in none (a row gap, a margin outside the figure). One rule, shared by the event
|
||||
/// routing and the crosshair sync -- and taking the rects rather than reading them, so a caller
|
||||
/// that has just laid the figure out does not make it lay out again for this.
|
||||
static u32 row_in(const std::vector<Rect>& rows, f32 y) noexcept;
|
||||
/// The index of the cell of `rows` (the figure's flattened cell rects) a framebuffer pixel
|
||||
/// belongs to: the cell it is in, or the nearest cell when it is in none (a row or column gap,
|
||||
/// a margin outside the figure). One rule, shared by the event routing and the crosshair sync
|
||||
/// -- and taking the rects rather than reading them, so a caller that has just laid the figure
|
||||
/// out does not make it lay out again for this.
|
||||
static u32 row_in(const std::vector<Rect>& rows, f32 x, f32 y) noexcept;
|
||||
|
||||
/// The controller a pointer event at (x, y) belongs to, applying the button capture:
|
||||
/// a press claims the row it landed in (`captured_row_` + `captured_button_`) and
|
||||
@@ -231,10 +235,10 @@ class Host {
|
||||
input::Controller& pointer_target(f32 x, f32 y, input::Button button, bool pressed,
|
||||
bool released) noexcept;
|
||||
|
||||
/// The row the pointer's events currently go to: the captured row while a drag owns
|
||||
/// the button, the row under the pointer otherwise. The run loop uses it to clear
|
||||
/// every *other* row's hot handles, so a row the pointer left cannot keep drawing a
|
||||
/// hover or a grab it last published.
|
||||
/// The plot (flattened index) the pointer's events currently go to: the captured one
|
||||
/// while a drag owns the button, the one under the pointer otherwise. The run loop uses
|
||||
/// it to clear every *other* plot's hot handles, so one the pointer left cannot keep
|
||||
/// drawing a hover or a grab it last published.
|
||||
u32 pointer_row(const std::vector<Rect>& rows) noexcept;
|
||||
|
||||
/// Spreads the hover over a whole `Figure` (spec §15).
|
||||
@@ -292,7 +296,7 @@ class Host {
|
||||
u32 captured_row_ = 0u;
|
||||
input::Button captured_button_ = input::Button::Left;
|
||||
|
||||
/// The figure's row rects as of the last `refresh_row_rects()` -- the cache the
|
||||
/// The figure's cell rects (one per plot, flattened; the row rects when no row is split) as of the last `refresh_row_rects()` -- the cache the
|
||||
/// event routing reads instead of laying the figure out per event (R7). Filled by
|
||||
/// `set_figure()` and once per frame by the run loop; empty while no figure is
|
||||
/// set, which is the state `controller_at()`/`pointer_target()` fall back to the
|
||||
|
||||
@@ -245,9 +245,11 @@ void PlotArea::set_figure(Figure* figure) noexcept {
|
||||
row_rects_.clear(); // no figure, no rows: the routing falls back to `controller_`
|
||||
return;
|
||||
}
|
||||
const u32 rows = figure_->row_count();
|
||||
row_controllers_.reserve(rows);
|
||||
for (u32 i = 0; i < rows; ++i) {
|
||||
// One controller per **plot** (a row of several cells has one for each), indexed by the
|
||||
// flattened plot index the figure uses.
|
||||
const u32 plots = figure_->plot_count();
|
||||
row_controllers_.reserve(plots);
|
||||
for (u32 i = 0; i < plots; ++i) {
|
||||
row_controllers_.push_back(std::make_unique<input::Controller>(figure_->plot(i)));
|
||||
}
|
||||
// The new figure's geometry goes into the cache now, not at the next frame: an event
|
||||
@@ -269,8 +271,10 @@ void PlotArea::lay_figure_out(Rect px) noexcept {
|
||||
|
||||
void PlotArea::refresh_row_rects() noexcept {
|
||||
// The one `Figure::layout()` per call: every event handler reads `row_rects_`
|
||||
// instead of paying this again, which is the whole point of the cache.
|
||||
row_rects_ = figure_ != nullptr ? figure_->layout().rows : std::vector<Rect>{};
|
||||
// instead of paying this again, which is the whole point of the cache. The rects are
|
||||
// the figure's **cells** (one per plot, flattened), which for a figure with one cell
|
||||
// per row are exactly its row rects.
|
||||
row_rects_ = figure_ != nullptr ? figure_->layout().cells : std::vector<Rect>{};
|
||||
}
|
||||
|
||||
input::Controller* PlotArea::row_controller(u32 row) noexcept {
|
||||
@@ -283,8 +287,7 @@ input::Controller& PlotArea::controller_at(f32 x, f32 y) noexcept {
|
||||
// -- and not a fresh `Figure::layout()`: an event pays no layout, or a burst of
|
||||
// pointer motion over a multi-row figure would re-lay every row out per move.
|
||||
if (row_rects_.empty() || row_rects_.size() != row_controllers_.size()) return controller_;
|
||||
(void)x; // the rows tile the figure vertically, so y alone picks one -- see row_in()
|
||||
const u32 row = row_in(row_rects_, y);
|
||||
const u32 row = row_in(row_rects_, x, y);
|
||||
input::Controller& controller = *row_controllers_[row];
|
||||
controller.set_viewport(row_rects_[row]);
|
||||
return controller;
|
||||
@@ -292,11 +295,11 @@ input::Controller& PlotArea::controller_at(f32 x, f32 y) noexcept {
|
||||
|
||||
u32 PlotArea::row_at(double x, double y) const noexcept {
|
||||
if (figure_ == nullptr || row_rects_.empty()) return 0;
|
||||
(void)x;
|
||||
return row_in(row_rects_, to_device(x, y).y);
|
||||
const Vec2 p = to_device(x, y);
|
||||
return row_in(row_rects_, p.x, p.y);
|
||||
}
|
||||
|
||||
input::Controller& PlotArea::target_controller(f32 y) noexcept {
|
||||
input::Controller& PlotArea::target_controller(f32 x, f32 y) noexcept {
|
||||
if (figure_ == nullptr || row_controllers_.empty()) return controller_;
|
||||
if (row_rects_.empty() || row_rects_.size() != row_controllers_.size()) return controller_;
|
||||
// The capture: while a press is open the gesture belongs to the row that press
|
||||
@@ -304,7 +307,7 @@ input::Controller& PlotArea::target_controller(f32 y) noexcept {
|
||||
// the release to the row the pointer is over *now* would leave the first row's drag
|
||||
// open for ever -- its controller never sees the release that ends it -- and let a
|
||||
// later gesture resume it from the stale anchor.
|
||||
u32 row = press_open_ ? press_row_ : row_in(row_rects_, y);
|
||||
u32 row = press_open_ ? press_row_ : row_in(row_rects_, x, y);
|
||||
// Re-read against this frame's rects: the widget can be resized mid-drag, and the
|
||||
// guard keeps a stale index from reaching past the controller list.
|
||||
if (row >= row_controllers_.size()) row = 0;
|
||||
@@ -314,23 +317,25 @@ input::Controller& PlotArea::target_controller(f32 y) noexcept {
|
||||
return controller;
|
||||
}
|
||||
|
||||
u32 PlotArea::row_in(const std::vector<Rect>& rows, f32 y) noexcept {
|
||||
u32 PlotArea::row_in(const std::vector<Rect>& rows, f32 x, f32 y) noexcept {
|
||||
if (rows.empty()) return 0;
|
||||
// The row the pixel is in, or -- when it is in none of them, which is the row gap a
|
||||
// host asked for with `Figure::set_row_gap()` or the figure's own margins -- the row
|
||||
// whose rect is nearest. By distance rather than by "the first that matches": the
|
||||
// rows tile the figure at the default gap of 0, so this is unreachable until a host
|
||||
// asks for a gap, and a gesture in a visible gap must not default to row 0.
|
||||
// The cell the pixel is in, or -- when it is in none of them, which is the row or
|
||||
// column gap a host asked for with `Figure::set_row_gap()`/`set_col_gap()` or the
|
||||
// figure's own margins -- the cell whose rect is nearest (squared distance from the
|
||||
// pixel to the rect). By distance rather than by "the first that matches": a gesture
|
||||
// in a visible gap must not default to plot 0. For a figure with one cell per row
|
||||
// every cell spans the figure's width, so this reduces to the nearest row by y.
|
||||
u32 row = 0;
|
||||
f32 best = std::numeric_limits<f32>::infinity();
|
||||
for (u32 i = 0; i < rows.size(); ++i) {
|
||||
const f32 top = rows[i].y;
|
||||
const f32 bottom = rows[i].y + rows[i].h;
|
||||
const f32 distance = y < top ? top - y : (y >= bottom ? y - bottom : 0.0f);
|
||||
const Rect& r = rows[i];
|
||||
const f32 dx = x < r.x ? r.x - x : (x >= r.right() ? x - r.right() : 0.0f);
|
||||
const f32 dy = y < r.y ? r.y - y : (y >= r.bottom() ? y - r.bottom() : 0.0f);
|
||||
const f32 distance = dx * dx + dy * dy;
|
||||
if (distance < best) {
|
||||
best = distance;
|
||||
row = i;
|
||||
if (distance == 0.0f) break; // inside this row: nothing can be nearer
|
||||
if (distance == 0.0f) break; // inside this cell: nothing can be nearer
|
||||
}
|
||||
}
|
||||
return row;
|
||||
@@ -341,7 +346,7 @@ u32 PlotArea::pointer_row() const noexcept {
|
||||
// The captured row while a press owns the gesture, so an in-flight drag keeps its
|
||||
// highlight even after the pointer has left the row it began in.
|
||||
if (press_open_ && press_row_ < row_rects_.size()) return press_row_;
|
||||
return row_in(row_rects_, pointer_.y);
|
||||
return row_in(row_rects_, pointer_.x, pointer_.y);
|
||||
}
|
||||
|
||||
void PlotArea::sync_selection_frames() noexcept {
|
||||
@@ -616,7 +621,7 @@ void PlotArea::pointer_press(input::Button button, u32 mods, double x, double y,
|
||||
// The figure row this gesture belongs to for its whole life, claimed from the press's
|
||||
// own position *before* the event is dispatched (see `target_controller()`). Harmless
|
||||
// and unread while no figure is set.
|
||||
press_row_ = figure_ != nullptr ? row_in(row_rects_, p.y) : 0;
|
||||
press_row_ = figure_ != nullptr ? row_in(row_rects_, p.x, p.y) : 0;
|
||||
press_open_ = true;
|
||||
press_button_ = button;
|
||||
press_mods_ = mods;
|
||||
@@ -634,7 +639,7 @@ void PlotArea::pointer_press(input::Button button, u32 mods, double x, double y,
|
||||
e.down = true;
|
||||
e.pressed = true;
|
||||
e.time_s = pointer_time_s(time_ms); // the double-click detector differences these
|
||||
target_controller(p.y).on_pointer(e);
|
||||
target_controller(p.x, p.y).on_pointer(e);
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(widget_));
|
||||
}
|
||||
|
||||
@@ -643,7 +648,7 @@ void PlotArea::pointer_release(Vec2 position, u32 time_ms) noexcept {
|
||||
// Resolved while the press is still open, so the release reaches the row the press
|
||||
// claimed rather than the row the pointer ended over -- the controller that owns the
|
||||
// drag is the only one that can close it.
|
||||
input::Controller& target = target_controller(position.y);
|
||||
input::Controller& target = target_controller(position.x, position.y);
|
||||
press_open_ = false;
|
||||
|
||||
input::PointerEvent e;
|
||||
@@ -748,7 +753,7 @@ void PlotArea::on_drag_update(GtkGestureDrag* gesture, double offset_x, double o
|
||||
gtk_event_controller_get_current_event_time(GTK_EVENT_CONTROLLER(gesture)));
|
||||
// The press's row, not the row the pointer has reached: `press_open_` is true here,
|
||||
// so a drag that crosses a row boundary stays with the controller that began it.
|
||||
area->target_controller(sample.position.y).on_pointer(e);
|
||||
area->target_controller(sample.position.x, sample.position.y).on_pointer(e);
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(area->widget_));
|
||||
}
|
||||
|
||||
@@ -795,7 +800,7 @@ void PlotArea::on_zoom_scale_changed(GtkGestureZoom* gesture, double scale,
|
||||
static_cast<f32>(scale), Vec2{static_cast<f32>(cx), static_cast<f32>(cy)}, area->mapping_);
|
||||
// The folded event carries the focal point, which is the position this pinch belongs
|
||||
// to; while a press is open the capture overrides it, as it does for a drag.
|
||||
area->target_controller(g.y).on_gesture(g);
|
||||
area->target_controller(g.x, g.y).on_gesture(g);
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(area->widget_));
|
||||
}
|
||||
|
||||
@@ -834,7 +839,7 @@ int PlotArea::on_scroll(GtkEventControllerScroll* scroll, double dx, double dy,
|
||||
e.dx = static_cast<f32>(scroll_notches(dx, unit));
|
||||
e.dy = static_cast<f32>(notches);
|
||||
e.mods = gesture_mods(GTK_EVENT_CONTROLLER(scroll));
|
||||
area->target_controller(at.y).on_wheel(e);
|
||||
area->target_controller(at.x, at.y).on_wheel(e);
|
||||
gtk_gl_area_queue_render(GTK_GL_AREA(area->widget_));
|
||||
return TRUE; // consumed: the plot zoomed
|
||||
}
|
||||
@@ -858,7 +863,7 @@ void PlotArea::on_motion(GtkEventControllerMotion* motion, double x, double y, v
|
||||
e.down = false;
|
||||
e.time_s = PlotArea::pointer_time_s(
|
||||
gtk_event_controller_get_current_event_time(GTK_EVENT_CONTROLLER(motion)));
|
||||
area->target_controller(p.y).on_pointer(e);
|
||||
area->target_controller(p.x, p.y).on_pointer(e);
|
||||
}
|
||||
|
||||
// --- keyboard -------------------------------------------------------------
|
||||
|
||||
@@ -260,15 +260,17 @@ class PlotArea {
|
||||
Figure* figure() noexcept { return figure_; }
|
||||
const Figure* figure() const noexcept { return figure_; }
|
||||
|
||||
/// One figure row's controller -- the one a gesture on that row reaches -- or null
|
||||
/// while no figure is set or the row does not exist. A host reads a drag mode or a
|
||||
/// One figure plot's controller -- the one a gesture on that plot reaches -- or null
|
||||
/// while no figure is set or it does not exist. `row` is the **flattened plot index**
|
||||
/// (`Figure::plot_count()` of them: the row count when no row holds several cells). A host reads a drag mode or a
|
||||
/// hover out of it.
|
||||
input::Controller* row_controller(u32 row) noexcept;
|
||||
|
||||
/// The controller an event at device pixel `(x, y)` belongs to: `controller()`
|
||||
/// while no figure is set, and otherwise the controller of the figure row that
|
||||
/// pixel lands in -- a pixel inside no row (a `Figure::set_row_gap()` gap, a
|
||||
/// margin) is the *nearest* row rather than dropped, so a drag that leaves the data
|
||||
/// while no figure is set, and otherwise the controller of the figure plot (cell)
|
||||
/// that pixel lands in -- x and y both pick it, as a row may hold several cells side
|
||||
/// by side. A pixel inside no cell (a `Figure::set_row_gap()`/`set_col_gap()` gap, a
|
||||
/// margin) is the *nearest* cell rather than dropped, so a drag that leaves the data
|
||||
/// area still ends where it should. It also hands that row its own viewport, which
|
||||
/// is what keeps the controller's hit-test geometry in step with the layout between
|
||||
/// two frames.
|
||||
@@ -277,10 +279,10 @@ class PlotArea {
|
||||
/// press capture, so a host may call it freely.
|
||||
input::Controller& controller_at(f32 x, f32 y) noexcept;
|
||||
|
||||
/// The figure row a point in the widget's **logical** coordinates lands in -- the
|
||||
/// The flattened plot index a point in the widget's **logical** coordinates lands in -- the
|
||||
/// coordinates every GTK event controller reports, so a host's own drop target or
|
||||
/// context menu can name the row a pointer is over without converting anything. The
|
||||
/// same nearest-row rule as `controller_at()`, and 0 while no figure is set.
|
||||
/// context menu can name the plot a pointer is over without converting anything. The
|
||||
/// same nearest-cell rule (x and y) as `controller_at()`, and 0 while no figure is set.
|
||||
u32 row_at(double x, double y) const noexcept;
|
||||
|
||||
/// Re-reads the figure's row rects into the cache the event routing answers from --
|
||||
@@ -376,20 +378,21 @@ class PlotArea {
|
||||
/// into device pixels. The one place that mapping exists.
|
||||
Vec2 to_device(double x, double y) const noexcept;
|
||||
|
||||
/// The controller one event of this area's goes to, given the event's device y.
|
||||
/// The controller one event of this area's goes to, given the event's device (x, y).
|
||||
/// `controller_` while no figure is set; otherwise the row the gesture *belongs*
|
||||
/// to: the row the open press claimed while a press is open, and the row under `y`
|
||||
/// between gestures. That is the capture `controller_at()` deliberately omits, and
|
||||
/// between gestures (x and y pick the cell). That is the capture `controller_at()` deliberately omits, and
|
||||
/// it is why this -- not `controller_at()` -- is what every handler below calls.
|
||||
///
|
||||
/// Hands the chosen row its own viewport, so the geometry it hit-tests is the
|
||||
/// geometry the last frame drew.
|
||||
input::Controller& target_controller(f32 y) noexcept;
|
||||
/// The row of `row_rects_` a device y belongs to: the row it is in, or the nearest
|
||||
/// one when it is in none (a row gap, a margin outside the figure). Selection by
|
||||
/// distance rather than "the first that matches" is what makes a pixel in a visible
|
||||
/// gap belong to the row it is *near* instead of to row 0.
|
||||
static u32 row_in(const std::vector<Rect>& rows, f32 y) noexcept;
|
||||
input::Controller& target_controller(f32 x, f32 y) noexcept;
|
||||
/// The cell of `row_rects_` (the figure's flattened cell rects) a device pixel belongs
|
||||
/// to: the cell it is in, or the nearest one when it is in none (a row or column gap,
|
||||
/// a margin outside the figure). Selection by distance rather than "the first that
|
||||
/// matches" is what makes a pixel in a visible gap belong to the cell it is *near*
|
||||
/// instead of to plot 0.
|
||||
static u32 row_in(const std::vector<Rect>& rows, f32 x, f32 y) noexcept;
|
||||
/// The row this area's events currently go to: the press's row while one is open,
|
||||
/// so an in-flight gesture keeps its highlight, and the row under the last pointer
|
||||
/// position otherwise. 0 with no rows.
|
||||
@@ -457,7 +460,8 @@ class PlotArea {
|
||||
/// `set_figure()` is called, and the single-plot path reads neither.
|
||||
Figure* figure_ = nullptr;
|
||||
std::vector<std::unique_ptr<input::Controller>> row_controllers_;
|
||||
/// The figure's row rects as of the last `refresh_row_rects()`: the cache the event
|
||||
/// The figure's cell rects (one per plot, flattened; the row rects when no row is split)
|
||||
/// as of the last `refresh_row_rects()`: the cache the event
|
||||
/// routing reads instead of laying the figure out per event. Empty while no figure
|
||||
/// is set, which is the state the routing falls back to `controller_` on.
|
||||
std::vector<Rect> row_rects_;
|
||||
|
||||
Reference in new issue
Block a user