ui: compact cursor and measurement readouts at the top of the dock
Both views are written from the same variable at the same call site, so the dock cannot disagree with the full panels. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
a7a1609d48
commit
a0fc0762d5
7 files changed
+350
-22
No files matched your search
+23
-16
@@ -1353,6 +1353,22 @@ bool visible_samples(App& app, std::size_t channel, std::vector<double>& time,
|
||||
|
||||
|
||||
|
||||
/// The chip of a measurement's signal, in the colour every other view names that signal with (the row and
|
||||
/// the dock's line both). The signal list is the producer's to rebuild, so it is read under the mutex.
|
||||
std::string measure_chip_markup(App& app, const std::string& signal) {
|
||||
std::lock_guard<std::mutex> lock(app.model->mutex());
|
||||
const std::vector<SignalInfo>& signals = app.model->signals();
|
||||
int color_index = -1;
|
||||
for (std::size_t i = 0; i < signals.size(); ++i) {
|
||||
if (signals[i].name == signal) {
|
||||
color_index = signal_color_index(signals, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return chip_markup(color_index >= 0 ? app.rows->signal_color(signal, color_index)
|
||||
: RowSet::unplotted_color());
|
||||
}
|
||||
|
||||
/// How often the measurements pane is recomputed while the window is still moving: a readout,
|
||||
/// where the honest currency is the window's own settling, not the frame rate. Ten a second is
|
||||
/// past the point a number can be read, and it makes the cost independent of the framerate.
|
||||
@@ -1380,22 +1396,7 @@ GtkWidget* measures_row(App& app, std::size_t index, const Measure& m) {
|
||||
// The signal's own chip beside it, in the palette colour every other view names that signal
|
||||
// with, so the row says *what* is being measured as well as by which measurement.
|
||||
GtkWidget* sig_chip = gtk_label_new("");
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(app.model->mutex());
|
||||
const std::vector<SignalInfo>& signals = app.model->signals();
|
||||
int color_index = -1;
|
||||
for (std::size_t i = 0; i < signals.size(); ++i) {
|
||||
if (signals[i].name == m.signal) {
|
||||
color_index = signal_color_index(signals, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
gtk_label_set_markup(GTK_LABEL(sig_chip),
|
||||
chip_markup(color_index >= 0
|
||||
? app.rows->signal_color(m.signal, color_index)
|
||||
: RowSet::unplotted_color())
|
||||
.c_str());
|
||||
}
|
||||
gtk_label_set_markup(GTK_LABEL(sig_chip), measure_chip_markup(app, m.signal).c_str());
|
||||
gtk_widget_set_tooltip_text(sig_chip, m.signal.c_str());
|
||||
gtk_box_append(GTK_BOX(box), sig_chip);
|
||||
GtkWidget* kind = gtk_label_new(measure_type_name(m.type));
|
||||
@@ -1618,6 +1619,7 @@ void rebuild_cursors_pane(App& app) {
|
||||
}
|
||||
}
|
||||
}
|
||||
dock_rebuild_cursors(app);
|
||||
}
|
||||
|
||||
|
||||
@@ -5161,12 +5163,17 @@ void on_activate(GtkApplication* application, gpointer user) {
|
||||
gtk_box_append(GTK_BOX(app->dock_page), app->cursors_panel);
|
||||
gtk_box_append(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
gtk_box_append(GTK_BOX(app->dock_page), measures_panel);
|
||||
// The compact readouts above the full panels (which move to slide-over pages later), with their own
|
||||
// separator: the first child of the dock.
|
||||
gtk_box_prepend(GTK_BOX(app->dock_page), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
|
||||
gtk_box_prepend(GTK_BOX(app->dock_page), build_readouts_dock(*app));
|
||||
|
||||
// The cursors start off, so their readout starts insensitive -- stated here by the one place that
|
||||
// knows the default, and the *body* and not the panel, because the panel's heading carries the
|
||||
// switch that turns it back on. The trigger's body is deliberately *not* disabled: a trigger is
|
||||
// configured before it is switched on, so its settings have to stay editable while it is off.
|
||||
gtk_widget_set_sensitive(app->cursors_body, FALSE);
|
||||
gtk_widget_set_sensitive(app->dock_cursor_grid, FALSE);
|
||||
// The arm controls are the one part of the trigger pane that needs trigger mode running: both of them,
|
||||
// and the compact line's visibility, are stated from the toggle by `sync_arm_buttons()`.
|
||||
sync_arm_buttons(*app);
|
||||
|
||||
@@ -590,6 +590,24 @@ struct App {
|
||||
int slot = -1;
|
||||
};
|
||||
std::vector<CursorLine> cursors_lines;
|
||||
/// The dock's compact copy of the cursors table (`src/ui_dock.cpp`): one line per `cursors_lines`
|
||||
/// entry, same order, written from the same strings by `update_cursors_pane()`.
|
||||
struct DockCursorLine {
|
||||
GtkWidget* label = nullptr;
|
||||
GtkWidget* a = nullptr;
|
||||
GtkWidget* b = nullptr;
|
||||
GtkWidget* d = nullptr;
|
||||
};
|
||||
std::vector<DockCursorLine> dock_cursor_lines;
|
||||
/// The Readouts section at the top of the dock, and the parts of it the rebuilds and the Edit buttons
|
||||
/// reach.
|
||||
GtkWidget* dock_readouts = nullptr;
|
||||
GtkWidget* dock_cursor_grid = nullptr;
|
||||
GtkWidget* dock_measures_box = nullptr;
|
||||
GtkWidget* dock_cursors_edit = nullptr;
|
||||
GtkWidget* dock_measures_edit = nullptr;
|
||||
/// One value label per measurement, in `measures` order (the dock's copy of `measure_labels`).
|
||||
std::vector<GtkWidget*> dock_measure_labels;
|
||||
/// The measurements the user set up, and what they last computed. The list is the state;
|
||||
/// the results are recomputed on a throttle (the numbers are window statistics, and the
|
||||
/// window scrolls every frame -- 60 recomputations a second of every sample is not a
|
||||
@@ -1756,6 +1774,16 @@ void rebuild_measures_pane(App& app);
|
||||
void on_cursor_mode_changed(GObject* obj, GParamSpec*, gpointer user);
|
||||
App::CursorLine cursors_line(App& app, const char* who, plot::Color chip, bool has_chip);
|
||||
GtkWidget* cursors_header(App& app);
|
||||
/// The compact readouts at the top of the dock (`src/ui_dock.cpp`). The strings are the pages' own: the
|
||||
/// pages' writers call the `dock_*` setters from the same variable, so a number is formatted once.
|
||||
GtkWidget* build_readouts_dock(App& app);
|
||||
void dock_rebuild_cursors(App& app);
|
||||
void dock_rebuild_measures(App& app);
|
||||
void dock_cursor_cell(App& app, std::size_t line, const std::string& a, const std::string& b,
|
||||
const std::string& d);
|
||||
void dock_measure_value(App& app, std::size_t i, const std::string& text);
|
||||
/// The signal chip of a measurement's row, in the colour every other view names that signal with.
|
||||
std::string measure_chip_markup(App& app, const std::string& signal);
|
||||
std::string row_unit(App& app, int row);
|
||||
std::string slot_unit(App& app, int row, int slot);
|
||||
std::uint32_t bound_slot_mask(App& app);
|
||||
|
||||
+14
-5
@@ -19,10 +19,14 @@ void update_cursors_pane(App& app) {
|
||||
// A readout quotes a stored sample; with the instruments off there is no sample it quotes, so
|
||||
// the cells blank rather than keeping the last ones. `"--"` is the same spelling an
|
||||
// unavailable cell already uses -- two spellings of one state in one grid would be the defect.
|
||||
for (const App::CursorLine& line : app.cursors_lines) {
|
||||
if (line.a != nullptr) gtk_label_set_text(GTK_LABEL(line.a), "--");
|
||||
if (line.b != nullptr) gtk_label_set_text(GTK_LABEL(line.b), "--");
|
||||
if (line.d != nullptr) gtk_label_set_text(GTK_LABEL(line.d), "--");
|
||||
// The dock's copy is written from the same literal, in the same loop.
|
||||
const std::string blank = "--";
|
||||
for (std::size_t i = 0; i < app.cursors_lines.size(); ++i) {
|
||||
const App::CursorLine& line = app.cursors_lines[i];
|
||||
if (line.a != nullptr) gtk_label_set_text(GTK_LABEL(line.a), blank.c_str());
|
||||
if (line.b != nullptr) gtk_label_set_text(GTK_LABEL(line.b), blank.c_str());
|
||||
if (line.d != nullptr) gtk_label_set_text(GTK_LABEL(line.d), blank.c_str());
|
||||
dock_cursor_cell(app, i, blank, blank, blank);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +45,8 @@ void update_cursors_pane(App& app) {
|
||||
const RowSet::CursorMode mode = app.rows->cursor_mode();
|
||||
const bool rulers = mode == RowSet::CursorMode::Horizontal || mode == RowSet::CursorMode::Both;
|
||||
const bool crossings = mode == RowSet::CursorMode::Mix;
|
||||
for (const App::CursorLine& line : app.cursors_lines) {
|
||||
for (std::size_t index = 0; index < app.cursors_lines.size(); ++index) {
|
||||
const App::CursorLine& line = app.cursors_lines[index];
|
||||
if (line.a == nullptr) continue;
|
||||
// One measurement per cell, each with the unit its own axis declares: the pane is a
|
||||
// readout beside a plot, where the values are the point.
|
||||
@@ -85,6 +90,8 @@ void update_cursors_pane(App& app) {
|
||||
put(line.a, va);
|
||||
put(line.b, vb);
|
||||
put(line.d, vd);
|
||||
// The dock's compact line takes the same three strings, from these variables.
|
||||
dock_cursor_cell(app, index, va, vb, vd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +151,8 @@ void on_cursors_toggled(GtkToggleButton* button, gpointer user) {
|
||||
// cursors back on -- and a control inside an insensitive parent cannot be clicked.
|
||||
gtk_widget_set_sensitive(app->cursors_reset, on);
|
||||
if (app->cursors_body != nullptr) gtk_widget_set_sensitive(app->cursors_body, on);
|
||||
// The dock's copy follows: switched off it stays on screen, insensitive, reading `--`.
|
||||
if (app->dock_cursor_grid != nullptr) gtk_widget_set_sensitive(app->dock_cursor_grid, on);
|
||||
if (!on) update_cursors_pane(*app);
|
||||
if (on) {
|
||||
app->rows->reset_cursors();
|
||||
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||
|
||||
// The compact readouts at the top of the dock: a read-only copy of the cursors table and of the
|
||||
// measurement values, in a fixed width, with an Edit button for each that opens the full panel.
|
||||
//
|
||||
// The strings are not formatted here. `update_cursors_pane()` and `update_measures()` format each value
|
||||
// once and hand the same variable to the page's label and to `dock_cursor_cell()` / `dock_measure_value()`,
|
||||
// so the two views cannot disagree. Every label is ellipsized at a fixed `width-chars`, so the dock's
|
||||
// layout does not depend on what a value reads; the full text is the label's tooltip.
|
||||
|
||||
#include "ui_app.hpp"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
namespace udpscope {
|
||||
namespace ui_impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/// The width, in characters, of a value cell and of a `who` / type cell. Fixed, so the dock is as wide
|
||||
/// with `--` as with `-123.456 mV`.
|
||||
constexpr int kValueChars = 9;
|
||||
constexpr int kNameChars = 8;
|
||||
|
||||
GtkWidget* fixed_label(const char* text, int chars, GtkAlign halign) {
|
||||
GtkWidget* l = gtk_label_new(text);
|
||||
gtk_widget_set_halign(l, halign);
|
||||
gtk_label_set_ellipsize(GTK_LABEL(l), PANGO_ELLIPSIZE_END);
|
||||
gtk_label_set_width_chars(GTK_LABEL(l), chars);
|
||||
gtk_label_set_max_width_chars(GTK_LABEL(l), chars);
|
||||
gtk_widget_add_css_class(l, "caption");
|
||||
return l;
|
||||
}
|
||||
|
||||
/// Sets a value label's text and, with it, the tooltip that carries what the ellipsis cut. The text is
|
||||
/// compared first so the per-frame write of an unchanged value costs nothing.
|
||||
void set_value_text(GtkWidget* label, const std::string& text) {
|
||||
if (label == nullptr) return;
|
||||
if (std::strcmp(gtk_label_get_text(GTK_LABEL(label)), text.c_str()) == 0) return;
|
||||
gtk_label_set_text(GTK_LABEL(label), text.c_str());
|
||||
gtk_widget_set_tooltip_text(label, text.c_str());
|
||||
}
|
||||
|
||||
void on_edit_cursors(GtkButton*, gpointer user) {
|
||||
slideover_open(*static_cast<App*>(user), "cursors");
|
||||
}
|
||||
|
||||
void on_edit_measures(GtkButton*, gpointer user) {
|
||||
slideover_open(*static_cast<App*>(user), "measure");
|
||||
}
|
||||
|
||||
/// A click on a measurement's line opens that measurement's dialog, as the page row's `edit` button
|
||||
/// does. The line carries its index (plus one, so that zero is "none") in its data.
|
||||
void on_measure_line_pressed(GtkGestureClick* gesture, int n_press, double, double, gpointer user) {
|
||||
if (n_press != 1) return;
|
||||
GtkWidget* line = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
|
||||
const int idx = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(line), "udpscope-measure-index")) - 1;
|
||||
if (idx < 0) return;
|
||||
open_measure_dialog(*static_cast<App*>(user), idx);
|
||||
}
|
||||
|
||||
/// A section's heading row: its title at the left, its Edit button at the right.
|
||||
GtkWidget* section_head(const char* title, GtkWidget** edit_out, GCallback on_edit, App* app,
|
||||
const char* tip) {
|
||||
GtkWidget* row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
GtkWidget* t = gtk_label_new(title);
|
||||
gtk_widget_set_halign(t, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand(t, true);
|
||||
gtk_widget_add_css_class(t, "caption-heading");
|
||||
gtk_box_append(GTK_BOX(row), t);
|
||||
GtkWidget* edit = gtk_button_new_with_label("Edit");
|
||||
gtk_widget_add_css_class(edit, "flat");
|
||||
gtk_widget_set_tooltip_text(edit, tip);
|
||||
g_signal_connect(edit, "clicked", on_edit, app);
|
||||
gtk_box_append(GTK_BOX(row), edit);
|
||||
*edit_out = edit;
|
||||
return row;
|
||||
}
|
||||
|
||||
/// The same header `cursors_header()` writes into the page's table, small.
|
||||
void dock_cursors_header(GtkGrid* grid) {
|
||||
const char* texts[4] = {"", "A", "B", "delta"};
|
||||
for (int c = 0; c < 4; ++c) {
|
||||
GtkWidget* l = fixed_label(texts[c], c == 0 ? kNameChars : kValueChars,
|
||||
c == 0 ? GTK_ALIGN_START : GTK_ALIGN_END);
|
||||
gtk_widget_add_css_class(l, "dim-label");
|
||||
gtk_grid_attach(grid, l, c, 0, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
GtkWidget* build_readouts_dock(App& app) {
|
||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_widget_set_margin_top(box, 6);
|
||||
gtk_widget_set_margin_bottom(box, 6);
|
||||
gtk_widget_set_margin_start(box, 6);
|
||||
gtk_widget_set_margin_end(box, 6);
|
||||
|
||||
GtkWidget* heading = gtk_label_new("Readouts");
|
||||
gtk_widget_set_halign(heading, GTK_ALIGN_START);
|
||||
gtk_widget_add_css_class(heading, "heading");
|
||||
gtk_box_append(GTK_BOX(box), heading);
|
||||
|
||||
gtk_box_append(GTK_BOX(box), section_head("Cursors", &app.dock_cursors_edit,
|
||||
G_CALLBACK(on_edit_cursors), &app,
|
||||
"the cursors' full panel"));
|
||||
app.dock_cursor_grid = gtk_grid_new();
|
||||
gtk_grid_set_row_spacing(GTK_GRID(app.dock_cursor_grid), 2);
|
||||
gtk_grid_set_column_spacing(GTK_GRID(app.dock_cursor_grid), 4);
|
||||
gtk_box_append(GTK_BOX(box), app.dock_cursor_grid);
|
||||
|
||||
gtk_box_append(GTK_BOX(box), section_head("Measurements", &app.dock_measures_edit,
|
||||
G_CALLBACK(on_edit_measures), &app,
|
||||
"the measurements' full panel"));
|
||||
app.dock_measures_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2);
|
||||
gtk_box_append(GTK_BOX(box), app.dock_measures_box);
|
||||
|
||||
app.dock_readouts = box;
|
||||
// The tables are built from the pages' current lines, which exist already when the pages were built
|
||||
// first; otherwise the pages' own rebuild fills them.
|
||||
dock_rebuild_cursors(app);
|
||||
dock_rebuild_measures(app);
|
||||
return box;
|
||||
}
|
||||
|
||||
void dock_rebuild_cursors(App& app) {
|
||||
if (app.dock_cursor_grid == nullptr) return;
|
||||
GtkGrid* grid = GTK_GRID(app.dock_cursor_grid);
|
||||
GtkWidget* child = gtk_widget_get_first_child(app.dock_cursor_grid);
|
||||
while (child != nullptr) {
|
||||
GtkWidget* next = gtk_widget_get_next_sibling(child);
|
||||
gtk_grid_remove(grid, child);
|
||||
child = next;
|
||||
}
|
||||
app.dock_cursor_lines.clear();
|
||||
dock_cursors_header(grid);
|
||||
int at = 1;
|
||||
for (const App::CursorLine& src : app.cursors_lines) {
|
||||
App::DockCursorLine line;
|
||||
line.label = fixed_label("", kNameChars, GTK_ALIGN_START);
|
||||
if (src.label != nullptr) {
|
||||
// The page's `who` cell, verbatim: a chip and a name as markup, or plain text.
|
||||
const char* text = gtk_label_get_label(GTK_LABEL(src.label));
|
||||
if (gtk_label_get_use_markup(GTK_LABEL(src.label))) {
|
||||
gtk_label_set_markup(GTK_LABEL(line.label), text);
|
||||
} else {
|
||||
gtk_label_set_text(GTK_LABEL(line.label), text);
|
||||
}
|
||||
const char* tip = gtk_widget_get_tooltip_text(src.label);
|
||||
gtk_widget_set_tooltip_text(line.label, tip != nullptr ? tip : gtk_label_get_text(GTK_LABEL(src.label)));
|
||||
}
|
||||
gtk_grid_attach(grid, line.label, 0, at, 1, 1);
|
||||
GtkWidget** cells[3] = {&line.a, &line.b, &line.d};
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
GtkWidget* v = fixed_label("", kValueChars, GTK_ALIGN_END);
|
||||
gtk_widget_add_css_class(v, "numeric");
|
||||
gtk_label_set_selectable(GTK_LABEL(v), true);
|
||||
gtk_grid_attach(grid, v, c + 1, at, 1, 1);
|
||||
*cells[c] = v;
|
||||
}
|
||||
app.dock_cursor_lines.push_back(line);
|
||||
++at;
|
||||
}
|
||||
}
|
||||
|
||||
void dock_rebuild_measures(App& app) {
|
||||
if (app.dock_measures_box == nullptr) return;
|
||||
GtkWidget* child = gtk_widget_get_first_child(app.dock_measures_box);
|
||||
while (child != nullptr) {
|
||||
GtkWidget* next = gtk_widget_get_next_sibling(child);
|
||||
gtk_box_remove(GTK_BOX(app.dock_measures_box), child);
|
||||
child = next;
|
||||
}
|
||||
app.dock_measure_labels.clear();
|
||||
if (app.measures.empty()) {
|
||||
GtkWidget* none = gtk_label_new("no measurements");
|
||||
gtk_widget_set_halign(none, GTK_ALIGN_START);
|
||||
gtk_widget_add_css_class(none, "caption");
|
||||
gtk_widget_add_css_class(none, "dim-label");
|
||||
gtk_box_append(GTK_BOX(app.dock_measures_box), none);
|
||||
return;
|
||||
}
|
||||
for (std::size_t i = 0; i < app.measures.size(); ++i) {
|
||||
const Measure& m = app.measures[i];
|
||||
GtkWidget* line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
g_object_set_data(G_OBJECT(line), "udpscope-measure-index",
|
||||
GINT_TO_POINTER(static_cast<int>(i) + 1));
|
||||
GtkWidget* chip = gtk_label_new("");
|
||||
gtk_label_set_markup(GTK_LABEL(chip), measure_chip_markup(app, m.signal).c_str());
|
||||
gtk_widget_set_tooltip_text(chip, m.signal.c_str());
|
||||
gtk_box_append(GTK_BOX(line), chip);
|
||||
GtkWidget* kind = fixed_label(measure_type_name(m.type), 12, GTK_ALIGN_START);
|
||||
gtk_widget_set_hexpand(kind, true);
|
||||
gtk_widget_set_tooltip_text(kind, (m.signal + "\n" + measure_type_name(m.type)).c_str());
|
||||
gtk_box_append(GTK_BOX(line), kind);
|
||||
GtkWidget* value = fixed_label("--", kValueChars, GTK_ALIGN_END);
|
||||
gtk_widget_add_css_class(value, "numeric");
|
||||
gtk_label_set_selectable(GTK_LABEL(value), true);
|
||||
gtk_box_append(GTK_BOX(line), value);
|
||||
// On the line and not on the value label, which is selectable and so keeps its own clicks.
|
||||
GtkGesture* click = gtk_gesture_click_new();
|
||||
g_signal_connect(click, "pressed", G_CALLBACK(on_measure_line_pressed), &app);
|
||||
gtk_widget_add_controller(line, GTK_EVENT_CONTROLLER(click));
|
||||
gtk_box_append(GTK_BOX(app.dock_measures_box), line);
|
||||
app.dock_measure_labels.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
void dock_cursor_cell(App& app, std::size_t line, const std::string& a, const std::string& b,
|
||||
const std::string& d) {
|
||||
if (line >= app.dock_cursor_lines.size()) return; // mid-rebuild
|
||||
const App::DockCursorLine& l = app.dock_cursor_lines[line];
|
||||
set_value_text(l.a, a);
|
||||
set_value_text(l.b, b);
|
||||
set_value_text(l.d, d);
|
||||
}
|
||||
|
||||
void dock_measure_value(App& app, std::size_t i, const std::string& text) {
|
||||
if (i >= app.dock_measure_labels.size()) return; // mid-rebuild
|
||||
set_value_text(app.dock_measure_labels[i], text);
|
||||
}
|
||||
|
||||
} // namespace ui_impl
|
||||
} // namespace udpscope
|
||||
+4
-1
@@ -111,6 +111,7 @@ void rebuild_measures_pane(App& app) {
|
||||
gtk_box_append(GTK_BOX(app.measures_list), measures_row(app, i, app.measures[i]));
|
||||
app.measure_text.emplace_back();
|
||||
}
|
||||
dock_rebuild_measures(app);
|
||||
update_measures(app, /*force=*/true, /*accumulate=*/true);
|
||||
}
|
||||
|
||||
@@ -581,7 +582,8 @@ void update_measures(App& app, bool force, bool accumulate) {
|
||||
// to measure *this* pass. The marks still fall back (below); `continue`ing out of the marks
|
||||
// was the other half of the rulers blinking off.
|
||||
app.measure_text[i] = "--";
|
||||
if (label != nullptr) gtk_label_set_text(GTK_LABEL(label), "--");
|
||||
if (label != nullptr) gtk_label_set_text(GTK_LABEL(label), app.measure_text[i].c_str());
|
||||
dock_measure_value(app, i, app.measure_text[i]);
|
||||
continue;
|
||||
}
|
||||
const App::MeasureWindow& w = *wp;
|
||||
@@ -615,6 +617,7 @@ void update_measures(App& app, bool force, bool accumulate) {
|
||||
}
|
||||
app.measure_text[i] = text;
|
||||
gtk_label_set_text(GTK_LABEL(label), text.c_str());
|
||||
dock_measure_value(app, i, text);
|
||||
}
|
||||
// One observation per recompute, and only when the pass produced one and the caller
|
||||
// asked for it. An invalid pass contributes nothing -- the marks' `measure_last_valid`
|
||||
|
||||
@@ -4339,6 +4339,53 @@ gboolean exit_probe_step1(gpointer user) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// The dock's readouts against the pages they copy: every dock label's text must equal the page
|
||||
/// label's (they are written from the same variable), and the Readouts box must fit the sidebar.
|
||||
static int dock_width_fits(App& a, int* dock_w, int* side_w) {
|
||||
*dock_w = a.dock_readouts != nullptr ? gtk_widget_get_width(a.dock_readouts) : -1;
|
||||
*side_w = a.dock_scroller != nullptr ? gtk_widget_get_width(a.dock_scroller) : -1;
|
||||
return *dock_w >= 0 && *side_w > 0 && *dock_w <= *side_w ? 1 : 0;
|
||||
}
|
||||
|
||||
static void dock_cursors_report(App& a, const char* what) {
|
||||
int same = a.dock_cursor_lines.size() == a.cursors_lines.size() ? 1 : 0;
|
||||
for (std::size_t i = 0; same != 0 && i < a.cursors_lines.size(); ++i) {
|
||||
const App::CursorLine& p = a.cursors_lines[i];
|
||||
const App::DockCursorLine& d = a.dock_cursor_lines[i];
|
||||
const auto text = [](GtkWidget* w) { return std::string(gtk_label_get_text(GTK_LABEL(w))); };
|
||||
if (text(p.label) != text(d.label) || text(p.a) != text(d.a) || text(p.b) != text(d.b) ||
|
||||
text(p.d) != text(d.d)) {
|
||||
same = 0;
|
||||
}
|
||||
}
|
||||
int dock_w = 0;
|
||||
int side_w = 0;
|
||||
const int fits = dock_width_fits(a, &dock_w, &side_w);
|
||||
std::fprintf(stderr,
|
||||
"[cursors-probe] %-14s dock cursors: lines=%zu page_lines=%zu same_text=%d dock_w=%d "
|
||||
"side_w=%d fits=%d dock_sensitive=%d\n",
|
||||
what, a.dock_cursor_lines.size(), a.cursors_lines.size(), same, dock_w, side_w, fits,
|
||||
a.dock_cursor_grid != nullptr && gtk_widget_get_sensitive(a.dock_cursor_grid) ? 1 : 0);
|
||||
}
|
||||
|
||||
static void dock_measures_report(App& a, const char* what) {
|
||||
int same = a.dock_measure_labels.size() == a.measure_labels.size() ? 1 : 0;
|
||||
for (std::size_t i = 0; same != 0 && i < a.measure_labels.size(); ++i) {
|
||||
if (std::strcmp(gtk_label_get_text(GTK_LABEL(a.measure_labels[i])),
|
||||
gtk_label_get_text(GTK_LABEL(a.dock_measure_labels[i]))) != 0) {
|
||||
same = 0;
|
||||
}
|
||||
}
|
||||
int dock_w = 0;
|
||||
int side_w = 0;
|
||||
const int fits = dock_width_fits(a, &dock_w, &side_w);
|
||||
std::fprintf(stderr,
|
||||
"[measures-probe] %-14s dock measures: lines=%zu page_rows=%zu same_text=%d dock_w=%d "
|
||||
"side_w=%d fits=%d\n",
|
||||
what, a.dock_measure_labels.size(), a.measure_labels.size(), same, dock_w, side_w,
|
||||
fits);
|
||||
}
|
||||
|
||||
void cursors_probe_report(App& a, const char* what) {
|
||||
static const char* const kModes[] = {"vertical", "horizontal", "both", "mix"};
|
||||
const int mode = static_cast<int>(a.rows->cursor_mode());
|
||||
@@ -4381,6 +4428,7 @@ void cursors_probe_report(App& a, const char* what) {
|
||||
if (have_mark) {
|
||||
std::fprintf(stderr, "[cursors-probe] %-14s first mark at x=%.6f y=%.6g\n", what, mx, my);
|
||||
}
|
||||
dock_cursors_report(a, what);
|
||||
std::fflush(stderr);
|
||||
}
|
||||
|
||||
@@ -4649,10 +4697,12 @@ gboolean measures_probe_step(gpointer user) {
|
||||
// the count with nothing to attribute it to -- which is how a window that lagged the screen
|
||||
// read `0/6` with no way to tell a stale recompute from a mark placed correctly.
|
||||
g_timeout_add(250, measure_trace, a);
|
||||
dock_measures_report(*a, "added");
|
||||
measures_probe_next(*a, 1, 400);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
dock_measures_report(*a, "settled");
|
||||
open_measure_dialog(*a, 0); // the edit path: this and the delete are what aborted
|
||||
measures_probe_next(*a, 2, 400);
|
||||
break;
|
||||
@@ -4717,6 +4767,7 @@ gboolean measures_probe_step(gpointer user) {
|
||||
std::fabs(restored - want) < 1.0e-9 ? 1 : 0);
|
||||
std::fflush(stderr);
|
||||
measure_dialog_report(*a, "final");
|
||||
dock_measures_report(*a, "final");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user