ui: the tour and the contextual help follow the badge bar, the slide-over and the dock
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
4b65197f56
commit
2826042156
5 files changed
+254
-119
No files matched your search
+67
-45
@@ -6657,8 +6657,8 @@ GtkWidget* helpui_menu_view(App* a) {
|
||||
}
|
||||
|
||||
/// One line: the spotlight, the widget it spotlights, and the card, all in **window coordinates** --
|
||||
/// the frame a screenshot is cropped in -- with the window's own record of the sidebar page beside
|
||||
/// them, so a stop that had to change the page to find its target is readable as such. `contains` is
|
||||
/// the frame a screenshot is cropped in -- with the slide-over's open page beside them (`none` when it
|
||||
/// is closed), so a stop that had to open a page to find its target is readable as such. `contains` is
|
||||
/// the spotlight covering its target (it is the target's own rectangle, grown three pixels) and
|
||||
/// `clear` is the card missing the target. `probe` is the tag it is printed under, so a window with
|
||||
/// more than one tour probe can tell its lines apart.
|
||||
@@ -6670,7 +6670,7 @@ void tour_geometry_line(App& a, const char* probe, const char* what) {
|
||||
const bool have_spot =
|
||||
a.tour.spot != nullptr && gtk_widget_compute_bounds(a.tour.spot, a.window, &spot);
|
||||
const bool have_target = t != nullptr && gtk_widget_get_mapped(t) &&
|
||||
gtk_widget_compute_bounds(t, a.window, &target);
|
||||
tour_target_bounds(t, a.window, &target);
|
||||
const bool have_card =
|
||||
a.tour.card != nullptr && gtk_widget_compute_bounds(a.tour.card, a.window, &card);
|
||||
const auto contains = [](const graphene_rect_t& outer, const graphene_rect_t& inner) {
|
||||
@@ -6859,6 +6859,10 @@ gboolean helpui_probe_step(gpointer user) {
|
||||
// The trigger pane's summary grid is on screen only in *locked* mode, and one of the help's
|
||||
// figures is that content: the probe reads `help figures:` in both states, so the geometry agent
|
||||
// has the locked pane's numbers without having to guess how to get there.
|
||||
// The trigger form is a slide-over page: it is open for both readings (and for the collapsed-search
|
||||
// one after them), because a closed panel leaves every one of its surfaces unmapped and reads
|
||||
// `-1,-1,0,0`. The 400 ms of the step is past the 120 ms reveal.
|
||||
if (step == 6) slideover_open(*a, "trigger");
|
||||
if (a->trigger_edit != nullptr) g_signal_emit_by_name(a->trigger_edit, "clicked");
|
||||
helpui_probe_next(*a, step == 6 ? 7 : 9, 400);
|
||||
return G_SOURCE_REMOVE;
|
||||
@@ -6891,6 +6895,7 @@ gboolean helpui_probe_step(gpointer user) {
|
||||
hint != nullptr && gtk_widget_get_mapped(hint) ? 1 : 0,
|
||||
gtk_widget_get_mapped(a->trigger_search) ? 1 : 0, sw, sh);
|
||||
}
|
||||
slideover_close(*a); // opened by step 6
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
@@ -6898,7 +6903,7 @@ gboolean helpui_probe_step(gpointer user) {
|
||||
// --- the tour's surface switch: a stop that stands on another stack page -------
|
||||
|
||||
/// Whether the walk's plan holds `target`. `steps=` alone cannot say a stop was *filtered out* rather
|
||||
/// than merely reordered, so the two cases the filter tells apart are read against the plan itself.
|
||||
/// than merely reordered, so the cases the filter tells apart are read against the plan itself.
|
||||
bool tour_plan_holds(const App& a, GtkWidget* target) {
|
||||
for (const TourStop& stop : a.tour.plan) {
|
||||
if (stop.target == target) return true;
|
||||
@@ -6912,27 +6917,34 @@ void tour_surface_probe_next(App& a, guint delay_ms) {
|
||||
g_timeout_add(delay_ms, tour_surface_probe_step, &a);
|
||||
}
|
||||
|
||||
/// `UDPSCOPE_PROBE_TOUR_SURFACE_MS`: the stop that stands on a page it is not on. It puts the sidebar
|
||||
/// on **Measure**, starts the window's tour, and walks it a stop per tick -- a tick apart on purpose,
|
||||
/// because a stack's new page is allocated 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 (and `update_tour()` places the hole
|
||||
/// from the frame that *has* been laid out). Every step prints the sidebar's page and whether the
|
||||
/// stop's target is mapped, and the stop whose target is the signal list prints the same geometry
|
||||
/// `[helpui]` does: **`side_tab=signals` with `target=signal_list` and `contains=1`** is the reading
|
||||
/// that the stop was not merely kept but *made* visible. It then puts the sidebar away --
|
||||
/// `sidebar_wanted` is the wish the tick re-asserts -- and starts the tour again: a *collapsed* pane
|
||||
/// leaves the stack unmapped, so the stops that need it are dropped and `steps=` falls. That pair is
|
||||
/// the filter's two cases, read rather than assumed.
|
||||
/// The slide-over's open page for a reading: its id, or `none`.
|
||||
const char* slideover_name(const App& a) {
|
||||
return slideover_page(a).empty() ? "none" : slideover_page(a).c_str();
|
||||
}
|
||||
|
||||
/// `UDPSCOPE_PROBE_TOUR_SURFACE_MS`: the stop that opens a surface it is not on. It opens the
|
||||
/// slide-over's `trigger` page, starts the window's tour from that state, and walks the whole table a
|
||||
/// stop per tick -- a tick apart on purpose (400 ms, longer than the panel's 120 ms slide), because a
|
||||
/// reading taken in the switch's own tick would be of the surface the walk has just left (and
|
||||
/// `update_tour()` places the hole from the frame that *has* been laid out). Every stop prints its
|
||||
/// title, the slide-over's open page and whether the target is mapped, with the spotlight geometry
|
||||
/// `[helpui]` prints: **`slideover=trigger` at the trigger stop and `slideover=none` at the one after
|
||||
/// it, with `contains=1`**, is the reading that the walk opened the panel and then closed it. The walk
|
||||
/// then runs again and is dismissed on the trigger stop (the Skip/Esc route), which must close the
|
||||
/// panel too. It finally puts the dock *away* -- `sidebar_wanted` is the wish the tick re-asserts --
|
||||
/// and starts the tour once more: the dock's stops are dropped (a collapsed pane is not walked over)
|
||||
/// while the trigger stop, which does not depend on the dock, is kept.
|
||||
gboolean tour_surface_probe_step(gpointer user) {
|
||||
App* a = static_cast<App*>(user);
|
||||
if (a->window == nullptr) return G_SOURCE_REMOVE;
|
||||
const auto mapped = [](GtkWidget* w) { return w != nullptr && gtk_widget_get_mapped(w) ? 1 : 0; };
|
||||
switch (a->dbg_tour_surface_step) {
|
||||
case 0: {
|
||||
// Where the run starts: a fresh config's sidebar is on Signals, so this is a *first* run's
|
||||
// state. The auto-start's walk (if one began) would only confuse the reading; it goes first.
|
||||
// Where the run starts: the panel closed and the dock on screen. The auto-start's walk (if one
|
||||
// began) would only confuse the reading; it goes first. The panel is then opened, so the walk's
|
||||
// first stop (which needs no page) has something to close.
|
||||
std::fprintf(stderr, "[toursurface] before: slideover=%s signal_list_mapped=%d running=%d\n",
|
||||
slideover_page(*a).empty() ? "none" : slideover_page(*a).c_str(), mapped(a->signal_list), tour_active(*a) ? 1 : 0);
|
||||
slideover_name(*a), mapped(a->signal_list), tour_active(*a) ? 1 : 0);
|
||||
tour_dismiss(*a);
|
||||
slideover_open(*a, "trigger");
|
||||
a->dbg_tour_surface_step = 1;
|
||||
@@ -6940,15 +6952,16 @@ gboolean tour_surface_probe_step(gpointer user) {
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
case 1: {
|
||||
// A frame after the switch, so the Measure page has been laid out and the list really is off
|
||||
// screen. The tour is started *here*, from this state.
|
||||
// A frame after the page opened, so the panel has slid in. The tour is started *here*, from this
|
||||
// state.
|
||||
std::fprintf(stderr,
|
||||
"[toursurface] on trigger: slideover=%s signal_list_mapped=%d side_box_mapped=%d\n",
|
||||
slideover_page(*a).empty() ? "none" : slideover_page(*a).c_str(),
|
||||
mapped(a->signal_list), mapped(a->side_box));
|
||||
"[toursurface] on trigger: slideover=%s trigger_body_mapped=%d side_box_mapped=%d\n",
|
||||
slideover_name(*a), mapped(a->trigger_body), mapped(a->side_box));
|
||||
start_tour(*a);
|
||||
std::fprintf(stderr, "[toursurface] tour: visible=%d steps=%d step=%d signals_stop=%d\n",
|
||||
std::fprintf(stderr,
|
||||
"[toursurface] tour: visible=%d steps=%d step=%d trigger_stop=%d signals_stop=%d\n",
|
||||
tour_active(*a) ? 1 : 0, tour_step_count(*a), tour_step_index(*a),
|
||||
tour_plan_holds(*a, a->trigger_body) ? 1 : 0,
|
||||
tour_plan_holds(*a, a->signal_list) ? 1 : 0);
|
||||
a->dbg_tour_surface_step = 2;
|
||||
tour_surface_probe_next(*a, 400);
|
||||
@@ -6956,44 +6969,53 @@ gboolean tour_surface_probe_step(gpointer user) {
|
||||
}
|
||||
case 2: {
|
||||
if (!tour_active(*a)) {
|
||||
std::fprintf(stderr, "[toursurface] walk ended before the signals stop\n");
|
||||
std::fflush(stderr);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
GtkWidget* target = tour_target(*a);
|
||||
const bool is_list = target != nullptr && target == a->signal_list;
|
||||
std::fprintf(stderr, "[toursurface] step=%d/%d slideover=%s target_mapped=%d%s\n",
|
||||
tour_step_index(*a) + 1, tour_step_count(*a),
|
||||
slideover_page(*a).empty() ? "none" : slideover_page(*a).c_str(), mapped(target),
|
||||
is_list ? " target=signal_list" : "");
|
||||
if (is_list) {
|
||||
// The frame after the switch: the page has been laid out and `update_tour()` has placed the
|
||||
// hole from it, so this is the reading that the spotlight is on the list.
|
||||
tour_geometry_line(*a, "toursurface", "signals stop");
|
||||
tour_dismiss(*a);
|
||||
// The last Next ended the walk: the panel must have gone with it.
|
||||
std::fprintf(stderr, "[toursurface] finished: running=0 slideover=%s\n", slideover_name(*a));
|
||||
start_tour(*a);
|
||||
while (tour_active(*a) && tour_target(*a) != a->trigger_body) tour_step(*a, 1);
|
||||
a->dbg_tour_surface_step = 3;
|
||||
tour_surface_probe_next(*a, 400);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
GtkWidget* target = tour_target(*a);
|
||||
const TourStop& stop = a->tour.plan[static_cast<std::size_t>(tour_step_index(*a))];
|
||||
std::fprintf(stderr, "[toursurface] step=%d/%d title='%s' slideover=%s target_mapped=%d\n",
|
||||
tour_step_index(*a) + 1, tour_step_count(*a), stop.title, slideover_name(*a),
|
||||
mapped(target));
|
||||
// The frame after the switch: the page has been laid out and `update_tour()` has placed the
|
||||
// hole from it, so this is the reading that the spotlight is on the stop's target.
|
||||
tour_geometry_line(*a, "toursurface", "stop");
|
||||
tour_step(*a, 1);
|
||||
tour_surface_probe_next(*a, 400);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
case 3: {
|
||||
// The other half of the filter. `sidebar_wanted` is the user's wish that the tick re-asserts
|
||||
// (`update_side_visibility`), so setting it here is how a probe puts the sidebar away for good;
|
||||
// the delay leaves the split view time to animate the column shut and unmap it.
|
||||
a->sidebar_wanted = false;
|
||||
// The walk again, parked on the trigger stop with the panel open: Skip (or Esc) closes it.
|
||||
std::fprintf(stderr, "[toursurface] restarted at trigger: running=%d slideover=%s\n",
|
||||
tour_active(*a) ? 1 : 0, slideover_name(*a));
|
||||
tour_dismiss(*a);
|
||||
std::fprintf(stderr, "[toursurface] skipped: running=%d slideover=%s\n",
|
||||
tour_active(*a) ? 1 : 0, slideover_name(*a));
|
||||
a->dbg_tour_surface_step = 4;
|
||||
tour_surface_probe_next(*a, 100);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
case 4: {
|
||||
// The other half of the filter. `sidebar_wanted` is the user's wish that the tick re-asserts
|
||||
// (`update_side_visibility`), so setting it here is how a probe puts the dock away for good; the
|
||||
// delay leaves the split view time to animate the column shut and unmap it.
|
||||
a->sidebar_wanted = false;
|
||||
a->dbg_tour_surface_step = 5;
|
||||
tour_surface_probe_next(*a, 700);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
default: {
|
||||
start_tour(*a);
|
||||
std::fprintf(stderr,
|
||||
"[toursurface] collapsed: side_box_mapped=%d steps=%d signals_stop=%d "
|
||||
"side_stop=%d\n",
|
||||
"[toursurface] collapsed: side_box_mapped=%d steps=%d trigger_stop=%d "
|
||||
"signals_stop=%d side_stop=%d\n",
|
||||
mapped(a->side_box), tour_step_count(*a),
|
||||
tour_plan_holds(*a, a->trigger_body) ? 1 : 0,
|
||||
tour_plan_holds(*a, a->signal_list) ? 1 : 0,
|
||||
tour_plan_holds(*a, a->side_box) ? 1 : 0);
|
||||
tour_dismiss(*a);
|
||||
|
||||
Reference in new issue
Block a user