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.
This commit is contained in:
Christian Hergert
2023-07-05 15:26:36 -07:00
parent aa3df478bc
commit 98998eb868
6 changed files with 212 additions and 8 deletions

View File

@ -69,6 +69,7 @@ timeruler {
color: alpha(currentColor, .8);
}
tracks informative,
tracks timecode {
border-radius: 7px;
background: @accent_bg_color;

View File

@ -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

View File

@ -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;
}

View File

@ -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,

View File

@ -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);

View File

@ -165,9 +165,16 @@
<property name="single-line-mode">true</property>
<property name="width-chars">10</property>
<property name="max-width-chars">10</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
<child>
<object class="GtkLabel" id="informative">
<property name="css-name">informative</property>
<property name="can-focus">false</property>
<property name="can-target">false</property>
<property name="visible">false</property>
<property name="xalign">0</property>
<property name="single-line-mode">true</property>
</object>
</child>
</template>