diff --git a/src/ui.cpp b/src/ui.cpp index 3a11d5b..e098a24 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -753,6 +753,8 @@ void on_layout_drag_end(GtkGestureDrag*, double, double, gpointer user) { // --- column (cell) resize drag ----------------------------------------------- +// The two cells' widths here are *plot* widths (a cell's rect without the first cell's y gutter): the +// 96 px floor and the plotter's 64 px floor both apply to them, and the weights share them out. void layout_col_apply(App& app, int plot, double offset_px) { RowSet& rs = *app.rows; const int fr = rs.figure_row_of(plot); @@ -792,8 +794,18 @@ bool layout_col_drag_begin(App& app, int plot) { const plot::FigureLayout& fl = app.rows->figure().layout(); const double scale = app.area != nullptr && app.area->device_scale() > 0.0f ? app.area->device_scale() : 1.0; - app.layout_col_drag_left0 = static_cast(fl.cells[static_cast(plot - 1)].w) / scale; - app.layout_col_drag_right0 = static_cast(fl.cells[static_cast(plot)].w) / scale; + // The weights share out *plot* widths, so the pair is measured as plots: a cell's rect minus the y + // gutter (axis strip + title column) that only the first cell of a row has. The left cell of the pair + // is the first one when `plot - 1` starts the row; the right cell never has a gutter, and neither does + // a non-first left cell (its plot's strips read 0). + const auto plot_width = [&](int cell) { + const RowSet::Row& r = app.rows->row(cell); + const double gutter = + r.plot != nullptr ? double(r.plot->layout().y_axis_width + r.plot->layout().y_title_width) : 0.0; + return std::max(0.0, double(fl.cells[static_cast(cell)].w) - gutter); + }; + app.layout_col_drag_left0 = plot_width(plot - 1) / scale; + app.layout_col_drag_right0 = plot_width(plot) / scale; app.layout_col_drag_start = app.rows->cell_weights(fr); app.layout_col_drag_start.resize(static_cast(app.rows->cells_in_row(fr)), 1.0f); return true; diff --git a/src/ui_app.hpp b/src/ui_app.hpp index d64141c..be333a9 100644 --- a/src/ui_app.hpp +++ b/src/ui_app.hpp @@ -208,9 +208,10 @@ struct App { /// began, so the beginning has to be kept -- reading the weights while dragging would compound. int layout_drag_row = -1; std::vector layout_drag_start; - /// Column drag state: which plot (right-hand cell), the two starting widths in logical px, - /// and the cell weights snapshot at drag begin (`layout_col_apply()` redistributes the pair's - /// weight sum from these, so a drag stays relative to where it started). + /// Column drag state: which plot (right-hand cell), the two starting *plot* widths in logical px + /// (a cell's rect minus the y gutter only the first cell of a row has), and the cell weights + /// snapshot at drag begin (`layout_col_apply()` redistributes the pair's weight sum from these, so + /// a drag stays relative to where it started). int layout_col_drag_plot = -1; double layout_col_drag_left0 = 0.0; double layout_col_drag_right0 = 0.0; diff --git a/src/ui_probe.cpp b/src/ui_probe.cpp index 46ed21c..91381cb 100644 --- a/src/ui_probe.cpp +++ b/src/ui_probe.cpp @@ -4203,7 +4203,16 @@ gboolean split_probe_step(gpointer user) { const double scale2 = (a->area != nullptr && a->area->device_scale() > 0.0f) ? static_cast(a->area->device_scale()) : 1.0; const int plot1 = rs.first_plot_of_row(0) + 1; + // The cells compared are *plot* widths (`axes.w` plus the right gutter, i.e. the cell rect without + // the y gutter that only the first cell has): the weights apply to those, so the divider follows the + // pointer only when the drag is measured the same way. The rect widths carry the gutter and are + // reported separately (`rect=`), they are not what is checked. const auto width = [&](int cell) { + (void)rs.figure().layout(); // non-const: re-lays the figure and every plot out on the weights + const plot::PlotLayout& pl = rs.row(cell).plot->layout(); + return static_cast(pl.axes.w + pl.right_gutter) / scale2; + }; + const auto rect_width = [&](int cell) { return static_cast(rs.figure().layout().cells[static_cast(cell)].w) / scale2; }; // One drag of `dx` logical px from the arrangement the figure is in now, then the widths read @@ -4253,16 +4262,18 @@ gboolean split_probe_step(gpointer user) { w0_max >= kMinCellPx - 1.0 && w1_max >= kMinCellPx - 1.0) ? 1 : 0; const int ok = left_gt && left_grew && follows_80 && sum_80 && relative && right_floor && left_floor && sum_ext && both_above_min ? 1 : 0; + // What the rect carries over the plot: the first cell's y gutter (the second has none). + const double gutter0 = rect_width(0) - width(0); // Leave the drag ended, as the gesture's end does. a->layout_col_drag_plot = -1; a->layout_col_drag_start.clear(); std::fprintf(stderr, "[split-probe] col-drag ok=%d left_gt_right=%d follows_80=%d relative=%d " "sum_80=%d right_floor=%d left_floor=%d sum_ext=%d both_above_min=%d " - "tile=%d d80=[%.1f,%.1f]->[%.1f,%.1f] ext=[%.1f,%.1f|%.1f,%.1f]\n", + "tile=%d d80=[%.1f,%.1f]->[%.1f,%.1f] ext=[%.1f,%.1f|%.1f,%.1f] gutter0=%.1f\n", ok, left_gt, follows_80, relative, sum_80, right_floor, left_floor, sum_ext, both_above_min, split_probe_tile(*a), d80.l0, d80.r0, d80.l, d80.r, w0_max, - w1_min, w0_min, w1_max); + w1_min, w0_min, w1_max, gutter0); } split_probe_next(*a); break;