From 122c4906b8c98ddd1d25ee534c4b0dfbefec8ce7 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Tue, 20 Jun 2023 16:39:30 -0700 Subject: [PATCH] libsysprof-gtk: add a split layer for top/bottom rows This can be used to recreate our old duplex visualizer which draws in opposite directions from a common middle Y point. --- src/libsysprof-gtk/meson.build | 2 + src/libsysprof-gtk/sysprof-gtk.h | 1 + src/libsysprof-gtk/sysprof-split-layer.c | 261 +++++++++++++++++++++++ src/libsysprof-gtk/sysprof-split-layer.h | 48 +++++ src/libsysprof-gtk/tests/test-charts.c | 16 +- 5 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 src/libsysprof-gtk/sysprof-split-layer.c create mode 100644 src/libsysprof-gtk/sysprof-split-layer.h diff --git a/src/libsysprof-gtk/meson.build b/src/libsysprof-gtk/meson.build index 02a1031f..dc9cd218 100644 --- a/src/libsysprof-gtk/meson.build +++ b/src/libsysprof-gtk/meson.build @@ -7,6 +7,7 @@ libsysprof_gtk_public_sources = [ 'sysprof-mark-chart.c', 'sysprof-mark-table.c', 'sysprof-session.c', + 'sysprof-split-layer.c', 'sysprof-weighted-callgraph-view.c', ] @@ -21,6 +22,7 @@ libsysprof_gtk_public_headers = [ 'sysprof-mark-chart.h', 'sysprof-mark-table.h', 'sysprof-session.h', + 'sysprof-split-layer.h', 'sysprof-weighted-callgraph-view.h', ] diff --git a/src/libsysprof-gtk/sysprof-gtk.h b/src/libsysprof-gtk/sysprof-gtk.h index 4f0e06de..aadea18b 100644 --- a/src/libsysprof-gtk/sysprof-gtk.h +++ b/src/libsysprof-gtk/sysprof-gtk.h @@ -31,6 +31,7 @@ G_BEGIN_DECLS # include "sysprof-mark-chart.h" # include "sysprof-mark-table.h" # include "sysprof-session.h" +# include "sysprof-split-layer.h" # include "sysprof-weighted-callgraph-view.h" #undef SYSPROF_GTK_INSIDE diff --git a/src/libsysprof-gtk/sysprof-split-layer.c b/src/libsysprof-gtk/sysprof-split-layer.c new file mode 100644 index 00000000..e20d6982 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-split-layer.c @@ -0,0 +1,261 @@ +/* + * sysprof-split-layer.c + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-split-layer.h" + +struct _SysprofSplitLayer +{ + SysprofChartLayer parent_instance; + GtkWidget *top; + GtkWidget *bottom; +}; + +G_DEFINE_FINAL_TYPE (SysprofSplitLayer, sysprof_split_layer, SYSPROF_TYPE_CHART_LAYER) + +enum { + PROP_0, + PROP_BOTTOM, + PROP_TOP, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_split_layer_measure (GtkWidget *widget, + GtkOrientation orientation, + int for_size, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + g_assert (SYSPROF_IS_SPLIT_LAYER (widget)); + + *minimum = *natural = 0; + *minimum_baseline = *natural_baseline = -1; + + for (GtkWidget *child = gtk_widget_get_first_child (widget); + child != NULL; + child = gtk_widget_get_next_sibling (child)) + { + int m, n; + + gtk_widget_measure (child, orientation, for_size, &m, &n, NULL, NULL); + + if (m > *minimum) + *minimum = m; + + if (n > *natural) + *natural = n; + } + + if (orientation == GTK_ORIENTATION_VERTICAL) + { + *minimum *= 2; + *natural *= 2; + } +} + +static void +sysprof_split_layer_size_allocate (GtkWidget *widget, + int width, + int height, + int baseline) +{ + SysprofSplitLayer *self = (SysprofSplitLayer *)widget; + + g_assert (SYSPROF_IS_SPLIT_LAYER (self)); + + if (self->top != NULL) + gtk_widget_size_allocate (self->top, + &(GtkAllocation) { 0, 0, width, floor (height / 2.) }, + -1); + + if (self->bottom != NULL) + gtk_widget_size_allocate (self->bottom, + &(GtkAllocation) { 0, ceil (height / 2.), width, floor (height / 2.) }, + -1); +} + +static void +sysprof_split_layer_finalize (GObject *object) +{ + SysprofSplitLayer *self = (SysprofSplitLayer *)object; + + g_clear_pointer (&self->top, gtk_widget_unparent); + g_clear_pointer (&self->bottom, gtk_widget_unparent); + + G_OBJECT_CLASS (sysprof_split_layer_parent_class)->finalize (object); +} + +static void +sysprof_split_layer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofSplitLayer *self = SYSPROF_SPLIT_LAYER (object); + + switch (prop_id) + { + case PROP_TOP: + g_value_set_object (value, sysprof_split_layer_get_top (self)); + break; + + case PROP_BOTTOM: + g_value_set_object (value, sysprof_split_layer_get_bottom (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_split_layer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofSplitLayer *self = SYSPROF_SPLIT_LAYER (object); + + switch (prop_id) + { + case PROP_TOP: + sysprof_split_layer_set_top (self, g_value_get_object (value)); + break; + + case PROP_BOTTOM: + sysprof_split_layer_set_bottom (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_split_layer_class_init (SysprofSplitLayerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sysprof_split_layer_finalize; + object_class->get_property = sysprof_split_layer_get_property; + object_class->set_property = sysprof_split_layer_set_property; + + widget_class->measure = sysprof_split_layer_measure; + widget_class->size_allocate = sysprof_split_layer_size_allocate; + + properties [PROP_TOP] = + g_param_spec_object ("top", NULL, NULL, + SYSPROF_TYPE_CHART_LAYER, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_BOTTOM] = + g_param_spec_object ("bottom", NULL, NULL, + SYSPROF_TYPE_CHART_LAYER, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_split_layer_init (SysprofSplitLayer *self) +{ +} + +SysprofChartLayer * +sysprof_split_layer_new (void) +{ + return g_object_new (SYSPROF_TYPE_SPLIT_LAYER, NULL); +} + +/** + * sysprof_split_layer_get_top: + * + * Returns: (transfer none) (nullable): a #SysprofChartLayer or %NULL + */ +SysprofChartLayer * +sysprof_split_layer_get_top (SysprofSplitLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_SPLIT_LAYER (self), NULL); + + return SYSPROF_CHART_LAYER (self->top); +} + +void +sysprof_split_layer_set_top (SysprofSplitLayer *self, + SysprofChartLayer *top) +{ + g_return_if_fail (SYSPROF_IS_SPLIT_LAYER (self)); + g_return_if_fail (!top || SYSPROF_IS_CHART_LAYER (top)); + + if (top == SYSPROF_CHART_LAYER (self->top)) + return; + + if (self->top) + gtk_widget_unparent (self->top); + + self->top = GTK_WIDGET (top); + + if (self->top) + gtk_widget_set_parent (self->top, GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TOP]); +} + +/** + * sysprof_split_layer_get_bottom: + * + * Returns: (transfer none) (nullable): a #SysprofChartLayer or %NULL + */ +SysprofChartLayer * +sysprof_split_layer_get_bottom (SysprofSplitLayer *self) +{ + g_return_val_if_fail (SYSPROF_IS_SPLIT_LAYER (self), NULL); + + return SYSPROF_CHART_LAYER (self->bottom); +} + +void +sysprof_split_layer_set_bottom (SysprofSplitLayer *self, + SysprofChartLayer *bottom) +{ + g_return_if_fail (SYSPROF_IS_SPLIT_LAYER (self)); + g_return_if_fail (!bottom || SYSPROF_IS_CHART_LAYER (bottom)); + + if (bottom == SYSPROF_CHART_LAYER (self->bottom)) + return; + + if (self->bottom) + gtk_widget_unparent (self->bottom); + + self->bottom = GTK_WIDGET (bottom); + + if (self->bottom) + gtk_widget_set_parent (self->bottom, GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_BOTTOM]); +} diff --git a/src/libsysprof-gtk/sysprof-split-layer.h b/src/libsysprof-gtk/sysprof-split-layer.h new file mode 100644 index 00000000..757a99f1 --- /dev/null +++ b/src/libsysprof-gtk/sysprof-split-layer.h @@ -0,0 +1,48 @@ +/* + * sysprof-split-layer.h + * + * Copyright 2023 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-chart-layer.h" + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_SPLIT_LAYER (sysprof_split_layer_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_FINAL_TYPE (SysprofSplitLayer, sysprof_split_layer, SYSPROF, SPLIT_LAYER, SysprofChartLayer) + +SYSPROF_AVAILABLE_IN_ALL +SysprofChartLayer *sysprof_split_layer_new (void); +SYSPROF_AVAILABLE_IN_ALL +SysprofChartLayer *sysprof_split_layer_get_bottom (SysprofSplitLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_split_layer_set_bottom (SysprofSplitLayer *self, + SysprofChartLayer *top); +SYSPROF_AVAILABLE_IN_ALL +SysprofChartLayer *sysprof_split_layer_get_top (SysprofSplitLayer *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_split_layer_set_top (SysprofSplitLayer *self, + SysprofChartLayer *bottom); + +G_END_DECLS diff --git a/src/libsysprof-gtk/tests/test-charts.c b/src/libsysprof-gtk/tests/test-charts.c index b3990d6a..02866f4d 100644 --- a/src/libsysprof-gtk/tests/test-charts.c +++ b/src/libsysprof-gtk/tests/test-charts.c @@ -97,6 +97,7 @@ main (int argc, const SysprofTimeSpan *time_span; GtkWidget *chart; GtkWidget *layer; + GtkWidget *split; GtkWindow *window; GtkWidget *box; guint n_samples; @@ -180,12 +181,19 @@ main (int argc, "session", session, "height-request", 128, NULL); - layer = g_object_new (SYSPROF_TYPE_LINE_LAYER, - "series", samples_series, - "title", "Stack Depth as Line", + split = g_object_new (SYSPROF_TYPE_SPLIT_LAYER, + "top", g_object_new (SYSPROF_TYPE_LINE_LAYER, + "series", samples_series, + "title", "Stack Depth as Line", + NULL), + "bottom", g_object_new (SYSPROF_TYPE_LINE_LAYER, + "series", samples_series, + "title", "Stack Depth as Line", + "flip-y", TRUE, + NULL), NULL); sysprof_chart_add_layer (SYSPROF_CHART (chart), - SYSPROF_CHART_LAYER (layer)); + SYSPROF_CHART_LAYER (split)); gtk_box_append (GTK_BOX (box), chart); gtk_window_present (window);