fix: the guided tour selects the page a stop's target lives on
A stop whose target is on another page of a stack was entered without switching to that page, so the spotlight aimed at a widget that was not on screen. A stop now declares the page it stands on (`TourStop::page`) and the tour selects it as the step is entered -- the window's trigger and signals stops put the sidebar on Signals, the chooser's tabs stop shows the Waveforms page and stepping off it brings Sources back. The stop filter moved into `tour_begin()` and now runs on `gtk_widget_get_mapped()`: GTK 4.24's `is_visible()` ignores `child_visible`, so it kept a stop on another page *and* one in a collapsed pane alike. A wrong page is kept and selected; a collapsed pane, whose stack is unmapped too, is still dropped.
This commit is contained in:
1 parent
829176248a
commit
d549223889
9 files changed
+385
-89
No files matched your search
+58
-26
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -752,26 +753,30 @@ void chooser_help_sync(Chooser& c) {
|
||||
/// does -- which is the whole reason the stops cannot live in the window's table.
|
||||
struct ChooserStop {
|
||||
GtkWidget* Chooser::* target;
|
||||
/// The page of the chooser's own stack the stop stands on. The tabs stop is the one about the two
|
||||
/// kinds of input, so it shows the *waveforms* page while it is up; every other stop stands on the
|
||||
/// sources page, and naming it is what brings the page back when the walk steps off the tabs stop.
|
||||
const char* page;
|
||||
const char* title;
|
||||
const char* body;
|
||||
};
|
||||
|
||||
constexpr ChooserStop kChooserStops[] = {
|
||||
{&Chooser::list, "Choosing a source",
|
||||
{&Chooser::list, "sources", "Choosing a source",
|
||||
"Every source you have saved, newest first. Clicking one opens it -- that is the choice this "
|
||||
"dialog exists to make -- and the arrows on a row edit or forget its configuration."},
|
||||
{&Chooser::switcher, "Two kinds of input",
|
||||
{&Chooser::switcher, "waveforms", "Two kinds of input",
|
||||
"A *source* is a stream to receive; a *waveform* is a recording an earlier run exported. They "
|
||||
"are the two answers to \u201cwhat should be on the screen\u201d, and these are the tabs "
|
||||
"between them."},
|
||||
{&Chooser::new_host, "A source that is not listed",
|
||||
{&Chooser::new_host, "sources", "A source that is not listed",
|
||||
"Opens the form for an address that has no configuration yet: host, port, the settings a source "
|
||||
"can only be given before it is opened, and the name its session will be saved under."},
|
||||
{&Chooser::help_button, "The manual, and this tour",
|
||||
{&Chooser::help_button, "sources", "The manual, and this tour",
|
||||
"Documentation opens the manual in the desktop help viewer. This tour walks this window and "
|
||||
"then the application's own window. Contextual help is the switch below it: on, every surface "
|
||||
"carries a small \u201c(i)\u201d saying what it is for."},
|
||||
{&Chooser::prefs_button, "Preferences",
|
||||
{&Chooser::prefs_button, "sources", "Preferences",
|
||||
"The event cache, relative time and the interface theme. This is the last moment relative time "
|
||||
"can still change this run -- the buffers are planned as soon as a source is picked -- which is "
|
||||
"why the button is here and not only in the window's menu."},
|
||||
@@ -806,16 +811,16 @@ void chooser_tour_mark_seen() {
|
||||
void chooser_tour_begin(Chooser& c) {
|
||||
if (c.tour.card == nullptr) return;
|
||||
chooser_tour_mark_seen();
|
||||
// The stops whose target is on screen, in table order, the same filter the window's tour uses: a
|
||||
// surface that is not there is not walked over.
|
||||
// The chooser's own stack, so a stop can name the page it stands on (the window does the same with
|
||||
// its sidebar in `start_tour()`).
|
||||
c.tour.page_stack = c.page_stack;
|
||||
// The whole table, in walk order: `tour_begin()` drops the stops whose surface is not on screen.
|
||||
std::vector<ui_impl::TourStop> stops;
|
||||
stops.reserve(kChooserStopCount);
|
||||
for (int i = 0; i < kChooserStopCount; ++i) {
|
||||
GtkWidget* target = c.*(kChooserStops[i].target);
|
||||
if (target != nullptr && gtk_widget_is_visible(target)) {
|
||||
stops.push_back({target, kChooserStops[i].title, kChooserStops[i].body});
|
||||
}
|
||||
stops.push_back({target, kChooserStops[i].page, kChooserStops[i].title, kChooserStops[i].body});
|
||||
}
|
||||
if (stops.empty()) return;
|
||||
ui_impl::tour_begin(c.tour, std::move(stops), chooser_tour_done, &c);
|
||||
}
|
||||
|
||||
@@ -851,26 +856,53 @@ gboolean chooser_tour_autostart(gpointer user) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/// `UDPSCOPE_PROBE_CHOOSER_TOUR_MS`: the chooser's tour, read and walked the way the window's is --
|
||||
/// where it stands, then every stop, then the handoff. It reports the walk by *stepping* it (the
|
||||
/// button the user presses), so what it reads is what a first run sees, not the state the probe set.
|
||||
/// Whether the probe has done its one Back step. One chooser runs per process, so a file-static is
|
||||
/// enough and simpler than a field on `Chooser` that exists only for the probe.
|
||||
bool g_chooser_tour_backed = false;
|
||||
|
||||
/// `UDPSCOPE_PROBE_CHOOSER_TOUR_MS`: the chooser's tour, read and walked a stop per tick -- the
|
||||
/// cadence the window's own tour probe walks at, and for its reason: a page a stop selects is laid
|
||||
/// out on the frame *after* the switch, so a reading taken in the switch's own tick would be of the
|
||||
/// page the walk has just left. It starts the walk if one is not already running, so it does not
|
||||
/// depend on the throwaway config being a first run; it reports the page the stack shows at every
|
||||
/// stop; it steps **Back** off the stop that stands on the waveforms page and reads the page again
|
||||
/// -- the other direction; then it finishes the walk. `page=waveforms` on that stop and `page=sources`
|
||||
/// after the Back step is the pair the surface switch is read by.
|
||||
gboolean chooser_tour_probe(gpointer user) {
|
||||
Chooser& c = *static_cast<Chooser*>(user);
|
||||
c.tour_probe_source = 0;
|
||||
std::fprintf(stderr, "[sources] chooser tour: visible=%d steps=%d step=%d\n",
|
||||
c.tour.running ? 1 : 0, static_cast<int>(c.tour.plan.size()), c.tour.index);
|
||||
while (c.tour.running) {
|
||||
const int before = c.tour.index;
|
||||
const auto page = [&c]() {
|
||||
const char* name = adw_view_stack_get_visible_child_name(ADW_VIEW_STACK(c.page_stack));
|
||||
return name != nullptr ? name : "?";
|
||||
};
|
||||
if (!c.tour.running) chooser_tour_begin(c);
|
||||
if (!c.tour.running) {
|
||||
std::fprintf(stderr, "[sources] chooser tour: nothing to walk (page=%s)\n", page());
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
const std::size_t idx = static_cast<std::size_t>(c.tour.index);
|
||||
const int n = static_cast<int>(c.tour.plan.size());
|
||||
const ui_impl::TourStop& stop = c.tour.plan[idx];
|
||||
std::fprintf(stderr, "[sources] chooser tour: step=%d/%d page=%s stop_page=%s target_mapped=%d\n",
|
||||
c.tour.index + 1, n, page(), stop.page != nullptr ? stop.page : "-",
|
||||
stop.target != nullptr && gtk_widget_get_mapped(stop.target) ? 1 : 0);
|
||||
if (stop.page != nullptr && std::strcmp(stop.page, "waveforms") == 0 && !g_chooser_tour_backed) {
|
||||
// Off it, Back: the previous stop stands on the sources page, and the page has to come back with
|
||||
// it -- the other direction a surface switch has to work in.
|
||||
g_chooser_tour_backed = true;
|
||||
ui_impl::tour_advance(c.tour, -1);
|
||||
std::fprintf(stderr, "[sources] chooser tour: back off the waveforms stop: step=%d/%d page=%s\n",
|
||||
c.tour.index + 1, n, page());
|
||||
} else {
|
||||
ui_impl::tour_advance(c.tour, 1);
|
||||
if (c.tour.running) {
|
||||
std::fprintf(stderr, "[sources] chooser tour: next -> step=%d/%d\n", c.tour.index,
|
||||
static_cast<int>(c.tour.plan.size()));
|
||||
} else {
|
||||
std::fprintf(stderr, "[sources] chooser tour: Next past stop %d finished the walk\n", before + 1);
|
||||
}
|
||||
}
|
||||
if (c.tour.running) {
|
||||
c.tour_probe_source = g_timeout_add(250, chooser_tour_probe, &c);
|
||||
} else {
|
||||
std::fprintf(stderr, "[sources] chooser tour: after finish visible=0 page=%s tour_pending=%d\n",
|
||||
page(), c.cfg->tour_pending ? 1 : 0);
|
||||
}
|
||||
std::fprintf(stderr, "[sources] chooser tour: after finish visible=%d tour_pending=%d\n",
|
||||
c.tour.running ? 1 : 0, c.cfg->tour_pending ? 1 : 0);
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user