Feature/source plugins #1
No files matched your search
@@ -269,6 +269,7 @@ add_executable(udpscope
|
||||
src/ui_status.cpp
|
||||
src/ui_measure.cpp
|
||||
src/ui_cursors.cpp
|
||||
src/ui_dock.cpp
|
||||
src/ui_math.cpp
|
||||
src/ui_layout.cpp
|
||||
src/ui_source.cpp
|
||||
|
||||
+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