fix(plot,figure): re-index hot region, logical row floor, refuse live on a linked axis
Three smaller findings at the plot/figure seam: * `Plot::remove_region()` re-indexed `region_stats_` but left `hot_.region` naming the old index, so a frame after a removal highlighted the wrong region's bound and an open drag edited a different region. The published handle (and its zone) is now cleared or decremented with the cache, and the contract documents that a host must not remove a region mid-drag. * `Figure`'s `kMinRowHeightPx` was applied as a *device*-pixel floor while chrome is logical, so at 2x the guarantee was half as strong exactly where the chrome is twice as large. It is now a logical constant converted through the row's own `ui_scale()`, 1:1 for a non-positive scale as `Style::device()` does. * `Figure::link_x()` refused a member whose axis 0 was `XFollow::Live`, but nothing refused `set_x_follow(0u, XFollow::Live)` *after* linking. The group then wrote its range into an axis that reinterpreted it against its own live edge, read it back as an external change, and tracked one member instead of the union. `Plot` now carries a linkage flag (`set_x_link_member()`), set by `link_x()` and cleared by `unlink_row()`, and `set_x_follow()` refuses `Live` on a linked axis. Tests: arming `Live` on a linked member leaves it `Off` and unlinking restores the ability to arm it; the row-height floor is 24 and 48 device pixels at 1x and 2x respectively.
This commit is contained in:
1 parent
2fbd1de7d4
commit
1f5ce0d4a5
5 files changed
+123
-12
No files matched your search
+29
-12
@@ -10,16 +10,17 @@
|
||||
namespace plot {
|
||||
namespace {
|
||||
|
||||
/// The height a row never drops below, in device pixels: the floor that keeps a
|
||||
/// plot's chrome from erasing it in a figure with many rows or a small viewport. It
|
||||
/// yields to the figure -- it is capped at an equal share of the height there is --
|
||||
/// rather than pushing rows past the viewport.
|
||||
/// The height a row never drops below, in **logical** pixels -- the same unit as every other
|
||||
/// chrome size, converted to device pixels by the row's own `ui_scale()` where the floor is
|
||||
/// applied. The floor that keeps a plot's chrome from erasing it in a figure with many rows
|
||||
/// or a small viewport. It yields to the figure -- it is capped at an equal share of the
|
||||
/// height there is -- rather than pushing rows past the viewport.
|
||||
///
|
||||
/// Device pixels, like every rect a figure computes: a `Figure` carries no `Style`
|
||||
/// -- the renderer's is the one its rows are laid out with -- so there is no logical
|
||||
/// unit here to convert from. A host that wants a taller floor gives the figure a
|
||||
/// taller viewport, or gives the rows weights.
|
||||
constexpr f32 kMinRowHeightPx = 24.0f;
|
||||
/// A `Figure` carries no `Style` -- the renderer's is the one its rows are laid out with --
|
||||
/// but every row's `Plot` has the `ui_scale` the renderer installed on it, so the conversion
|
||||
/// is the row's own and the guarantee is the same *physical* height at any DPI. A host that
|
||||
/// wants a taller floor gives the figure a taller viewport, or gives the rows weights.
|
||||
constexpr f32 kMinRowHeightLogical = 24.0f;
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -72,8 +73,12 @@ u32 Figure::x_link(u32 row) const noexcept {
|
||||
void Figure::unlink_row(u32 row) noexcept {
|
||||
// A row that leaves its group stops following anything, so the hint a following group
|
||||
// published to it goes with the membership: leaving `has_live` set behind would leave a
|
||||
// former member anchoring a zoom on an edge it no longer follows.
|
||||
if (row < plots_.size()) plots_[row].set_x_live_owner(0u, false, 0.0);
|
||||
// former member anchoring a zoom on an edge it no longer follows. Its linkage flag goes
|
||||
// with it too: an unlinked row is an ordinary plot again, free to arm `Live` itself.
|
||||
if (row < plots_.size()) {
|
||||
plots_[row].set_x_live_owner(0u, false, 0.0);
|
||||
plots_[row].set_x_link_member(0u, false);
|
||||
}
|
||||
for (XLink& group : links_) {
|
||||
for (std::size_t i = 0; i < group.rows.size(); ++i) {
|
||||
if (group.rows[i] != row) continue;
|
||||
@@ -114,6 +119,10 @@ Status Figure::link_x(u32 row, u32 group) {
|
||||
const std::size_t index = static_cast<std::size_t>(at - g.rows.begin());
|
||||
g.rows.insert(at, row);
|
||||
g.pushed.insert(g.pushed.begin() + static_cast<std::ptrdiff_t>(index), Range{});
|
||||
// A member's axis 0 is now owned by the group (its window and its follow state), so
|
||||
// `set_x_follow(Live)` on it is refused from here on -- the same rule the refusal above
|
||||
// applies at the door.
|
||||
p.set_x_link_member(0u, true);
|
||||
if (!g.has_range) {
|
||||
// The first row into a group is the one that frames it: there is no shared range
|
||||
// to apply yet, and adopting this row's is what makes the next joiner land on a
|
||||
@@ -356,7 +365,15 @@ const FigureLayout& Figure::layout() {
|
||||
// heights still sum to exactly the usable height, so the rows tile the figure and
|
||||
// the last one ends on its bottom edge.
|
||||
const bool equal = row_weights_.empty();
|
||||
const f32 floor_px = std::min(kMinRowHeightPx, usable / static_cast<f32>(rows));
|
||||
// The floor is a logical size like every other chrome metric, so it is converted through
|
||||
// the row's own `ui_scale()` -- the one the renderer installed on every row -- which is
|
||||
// what makes the guarantee the same *physical* height at any DPI instead of half as strong
|
||||
// at 2x exactly where the chrome it protects is twice as large. A scale that is not
|
||||
// positive is not a scale and converts 1:1, exactly as `Style::device()` documents.
|
||||
const f32 floor_scale = plots_[0].ui_scale();
|
||||
const f32 floor_device = floor_scale > 0.0f ? kMinRowHeightLogical * floor_scale
|
||||
: kMinRowHeightLogical;
|
||||
const f32 floor_px = std::min(floor_device, usable / static_cast<f32>(rows));
|
||||
const f32 extra = usable - floor_px * static_cast<f32>(rows);
|
||||
f32 total_w = 0.0f;
|
||||
for (const f32 w : row_weights_) total_w += w;
|
||||
|
||||
Reference in new issue
Block a user