From 98998eb8683be99cbff913ee6c31cd89ae47c6c4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Wed, 5 Jul 2023 15:26:36 -0700 Subject: [PATCH] libsysprof-gtk: add informative description of events under pointer We are not using Tooltips for these because they 1) introduce additional delay, 2) slow things down to a hault do to protocol overhead/compositor work/etc. There may be a desire to do this differently for a11y purposes at some point, but I'd want to see how that use case plays out before trying to shove text description updates every frame motion to a screen reader. --- src/libsysprof-gtk/style.css | 1 + src/libsysprof-gtk/sysprof-session-private.h | 8 ++- src/libsysprof-gtk/sysprof-session.c | 74 ++++++++++++++++++++ src/libsysprof-gtk/sysprof-time-span-layer.c | 63 +++++++++++++++++ src/libsysprof-gtk/sysprof-tracks-view.c | 61 +++++++++++++++- src/libsysprof-gtk/sysprof-tracks-view.ui | 13 +++- 6 files changed, 212 insertions(+), 8 deletions(-) diff --git a/src/libsysprof-gtk/style.css b/src/libsysprof-gtk/style.css index 672858a3..09108324 100644 --- a/src/libsysprof-gtk/style.css +++ b/src/libsysprof-gtk/style.css @@ -69,6 +69,7 @@ timeruler { color: alpha(currentColor, .8); } +tracks informative, tracks timecode { border-radius: 7px; background: @accent_bg_color; diff --git a/src/libsysprof-gtk/sysprof-session-private.h b/src/libsysprof-gtk/sysprof-session-private.h index 8eee33d8..561f1cc5 100644 --- a/src/libsysprof-gtk/sysprof-session-private.h +++ b/src/libsysprof-gtk/sysprof-session-private.h @@ -24,8 +24,10 @@ G_BEGIN_DECLS -void _sysprof_session_discover_tracks (SysprofSession *session, - SysprofDocument *document, - GListStore *tracks); +void _sysprof_session_discover_tracks (SysprofSession *session, + SysprofDocument *document, + GListStore *tracks); +char *_sysprof_session_describe (SysprofSession *self, + gpointer item); G_END_DECLS diff --git a/src/libsysprof-gtk/sysprof-session.c b/src/libsysprof-gtk/sysprof-session.c index d4cea732..bcf83054 100644 --- a/src/libsysprof-gtk/sysprof-session.c +++ b/src/libsysprof-gtk/sysprof-session.c @@ -379,3 +379,77 @@ sysprof_session_zoom_to_selection (SysprofSession *self) sysprof_session_update_axis (self); } + +static char * +get_time_str (gint64 o) +{ + char str[32]; + + if (o == 0) + g_snprintf (str, sizeof str, "%.3lfs", .0); + else if (o < 1000000) + g_snprintf (str, sizeof str, "%.3lfμs", o/1000.); + else if (o < SYSPROF_NSEC_PER_SEC) + g_snprintf (str, sizeof str, "%.3lfms", o/1000000.); + else + g_snprintf (str, sizeof str, "%.3lfs", o/(double)SYSPROF_NSEC_PER_SEC); + + return g_strdup (str); +} + +static void +append_time_string (GString *str, + const SysprofTimeSpan *span) +{ + g_autofree char *begin_str = NULL; + + g_assert (str != NULL); + g_assert (span != NULL); + + begin_str = get_time_str (span->begin_nsec); + + g_string_append (str, begin_str); + + if (span->begin_nsec != span->end_nsec) + { + g_autofree char *end_str = get_time_str (span->end_nsec - span->begin_nsec); + + g_string_append_printf (str, " (%s)", end_str); + } +} + +char * +_sysprof_session_describe (SysprofSession *self, + gpointer item) +{ + g_return_val_if_fail (SYSPROF_IS_SESSION (self), NULL); + + if (self->document == NULL) + return NULL; + + if (SYSPROF_IS_DOCUMENT_MARK (item)) + { + SysprofDocumentMark *mark = item; + const SysprofTimeSpan *begin = sysprof_document_get_time_span (self->document); + GString *str = g_string_new (NULL); + const char *group = sysprof_document_mark_get_group (mark); + const char *name = sysprof_document_mark_get_name (mark); + const char *message = sysprof_document_mark_get_message (mark); + SysprofTimeSpan span = { + .begin_nsec = sysprof_document_frame_get_time (item), + .end_nsec = sysprof_document_frame_get_time (item) + sysprof_document_mark_get_duration (mark), + }; + + span = sysprof_time_span_relative_to (span, begin->begin_nsec); + + append_time_string (str, &span); + g_string_append_printf (str, ": %s / %s", group, name); + + if (message && message[0]) + g_string_append_printf (str, ": %s", message); + + return g_string_free (str, FALSE); + } + + return NULL; +} diff --git a/src/libsysprof-gtk/sysprof-time-span-layer.c b/src/libsysprof-gtk/sysprof-time-span-layer.c index b009df39..897b97af 100644 --- a/src/libsysprof-gtk/sysprof-time-span-layer.c +++ b/src/libsysprof-gtk/sysprof-time-span-layer.c @@ -241,6 +241,66 @@ sysprof_time_span_layer_snapshot (GtkWidget *widget, } } +static gpointer +sysprof_time_span_layer_lookup_item (SysprofChartLayer *layer, + double x, + double y) +{ + SysprofTimeSpanLayer *self = (SysprofTimeSpanLayer *)layer; + const float *x_values; + const float *x2_values; + GListModel *model; + guint n_x_values = 0; + guint n_x2_values = 0; + guint n_values; + int width; + int height; + + g_assert (SYSPROF_IS_TIME_SPAN_LAYER (self)); + + width = gtk_widget_get_width (GTK_WIDGET (self)); + height = gtk_widget_get_height (GTK_WIDGET (self)); + + if (width == 0 || height == 0 || self->series == NULL) + return NULL; + + if (!(model = sysprof_series_get_model (SYSPROF_SERIES (self->series)))) + return NULL; + + if (!(x_values = sysprof_normalized_series_get_values (self->normal_x, &n_x_values)) || + !(x2_values = sysprof_normalized_series_get_values (self->normal_x2, &n_x2_values)) || + !(n_values = MIN (n_x_values, n_x2_values))) + return NULL; + + /* First match our non-duration marks (diamonds) */ + for (guint i = 0; i < n_values; i++) + { + int begin = x_values[i] * width - 3; + int end = x2_values[i] * width + 3; + + if (x_values[i] != x2_values[i]) + continue; + + if (x >= begin && x < end) + return g_list_model_get_item (model, i); + } + + /* Then match regular duration events */ + for (guint i = 0; i < n_values; i++) + { + float begin = x_values[i] * width; + float end = x2_values[i] * width; + + if (x_values[i] == x2_values[i]) + continue; + + if (x >= begin && x < end) + return g_list_model_get_item (model, i); + } + + return NULL; +} + static void sysprof_time_span_layer_finalize (GObject *object) { @@ -321,6 +381,7 @@ sysprof_time_span_layer_class_init (SysprofTimeSpanLayerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofChartLayerClass *chart_layer_class = SYSPROF_CHART_LAYER_CLASS (klass); object_class->finalize = sysprof_time_span_layer_finalize; object_class->get_property = sysprof_time_span_layer_get_property; @@ -328,6 +389,8 @@ sysprof_time_span_layer_class_init (SysprofTimeSpanLayerClass *klass) widget_class->snapshot = sysprof_time_span_layer_snapshot; + chart_layer_class->lookup_item = sysprof_time_span_layer_lookup_item; + properties[PROP_AXIS] = g_param_spec_object ("axis", NULL, NULL, SYSPROF_TYPE_AXIS, diff --git a/src/libsysprof-gtk/sysprof-tracks-view.c b/src/libsysprof-gtk/sysprof-tracks-view.c index 73d358ce..1f2fe29b 100644 --- a/src/libsysprof-gtk/sysprof-tracks-view.c +++ b/src/libsysprof-gtk/sysprof-tracks-view.c @@ -20,7 +20,9 @@ #include "config.h" +#include "sysprof-chart-layer.h" #include "sysprof-css-private.h" +#include "sysprof-session-private.h" #include "sysprof-track-view.h" #include "sysprof-tracks-view.h" #include "sysprof-time-ruler.h" @@ -32,6 +34,7 @@ struct _SysprofTracksView SysprofSession *session; GtkBox *box; + GtkLabel *informative; GtkWidget *top_left; GtkListView *list_view; SysprofTimeRuler *ruler; @@ -65,6 +68,8 @@ set_motion (SysprofTracksView *self, double y) { gboolean timecode_visible = FALSE; + gboolean informative_visible = FALSE; + GtkWidget *pick; int ruler_start; g_assert (SYSPROF_IS_TRACKS_VIEW (self)); @@ -76,15 +81,42 @@ set_motion (SysprofTracksView *self, self->motion_y = y; ruler_start = gtk_widget_get_width (self->top_left); + timecode_visible = x >= ruler_start; - if (x >= ruler_start) + if (timecode_visible) { g_autofree char *str = sysprof_time_ruler_get_label_at_point (self->ruler, x - ruler_start); gtk_label_set_label (self->timecode, str); - timecode_visible = TRUE; + } + + pick = gtk_widget_pick (GTK_WIDGET (self), x, y, 0); + + if (pick != NULL) + { + SysprofChartLayer *layer = SYSPROF_CHART_LAYER (gtk_widget_get_ancestor (pick, SYSPROF_TYPE_CHART_LAYER)); + + if (layer != NULL) + { + g_autoptr(GObject) item = NULL; + g_autofree char *text = NULL; + double layer_x; + double layer_y; + + gtk_widget_translate_coordinates (GTK_WIDGET (self), + GTK_WIDGET (layer), + x, y, &layer_x, &layer_y); + + if ((item = sysprof_chart_layer_lookup_item (layer, layer_x, layer_y))) + text = _sysprof_session_describe (self->session, item); + + gtk_label_set_label (self->informative, text); + + informative_visible = text != NULL; + } } gtk_widget_set_visible (GTK_WIDGET (self->timecode), timecode_visible); + gtk_widget_set_visible (GTK_WIDGET (self->informative), informative_visible); gtk_widget_queue_draw (GTK_WIDGET (self)); } @@ -357,6 +389,9 @@ G_GNUC_END_IGNORE_DEPRECATIONS if (gtk_widget_get_visible (GTK_WIDGET (self->timecode))) gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->timecode), snapshot); + + if (gtk_widget_get_visible (GTK_WIDGET (self->informative))) + gtk_widget_snapshot_child (GTK_WIDGET (self), GTK_WIDGET (self->informative), snapshot); } static void @@ -448,6 +483,27 @@ sysprof_tracks_view_size_allocate (GtkWidget *widget, min_req.width, min_req.height }, -1); } + + if (gtk_widget_get_visible (GTK_WIDGET (self->informative))) + { + GtkRequisition min_req; + GtkRequisition nat_req; + + gtk_widget_get_preferred_size (GTK_WIDGET (self->informative), &min_req, &nat_req); + + if (self->motion_x + min_req.width < gtk_widget_get_width (GTK_WIDGET (self))) + gtk_widget_size_allocate (GTK_WIDGET (self->informative), + &(GtkAllocation) { + self->motion_x, self->motion_y, + min_req.width, min_req.height + }, -1); + else + gtk_widget_size_allocate (GTK_WIDGET (self->informative), + &(GtkAllocation) { + self->motion_x - min_req.width, self->motion_y, + min_req.width, min_req.height + }, -1); + } } static void @@ -529,6 +585,7 @@ sysprof_tracks_view_class_init (SysprofTracksViewClass *klass) gtk_widget_class_set_css_name (widget_class, "tracks"); gtk_widget_class_bind_template_child (widget_class, SysprofTracksView, box); + gtk_widget_class_bind_template_child (widget_class, SysprofTracksView, informative); gtk_widget_class_bind_template_child (widget_class, SysprofTracksView, list_view); gtk_widget_class_bind_template_child (widget_class, SysprofTracksView, ruler); gtk_widget_class_bind_template_child (widget_class, SysprofTracksView, timecode); diff --git a/src/libsysprof-gtk/sysprof-tracks-view.ui b/src/libsysprof-gtk/sysprof-tracks-view.ui index 6e8c3bcf..5e4a0240 100644 --- a/src/libsysprof-gtk/sysprof-tracks-view.ui +++ b/src/libsysprof-gtk/sysprof-tracks-view.ui @@ -165,9 +165,16 @@ true 10 10 - + + + + + informative + false + false + false + 0 + true