From 19cd4dca010d6cbd029a354e48ff589bb9be11df Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sat, 5 Aug 2023 23:42:11 -0700 Subject: [PATCH] sysprof: iterate on updated greeter design --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-entry-popover.c | 427 ++++++++++++ src/sysprof/sysprof-entry-popover.h | 88 +++ src/sysprof/sysprof-entry-popover.ui | 59 ++ src/sysprof/sysprof-greeter.c | 244 ++++++- src/sysprof/sysprof-greeter.ui | 937 ++++++++++++++------------- src/sysprof/sysprof.gresource.xml | 2 + 7 files changed, 1298 insertions(+), 460 deletions(-) create mode 100644 src/sysprof/sysprof-entry-popover.c create mode 100644 src/sysprof/sysprof-entry-popover.h create mode 100644 src/sysprof/sysprof-entry-popover.ui diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index c09a3b2e..653a8466 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -18,6 +18,7 @@ sysprof_sources = [ 'sysprof-dbus-utility.c', 'sysprof-duplex-layer.c', 'sysprof-energy-section.c', + 'sysprof-entry-popover.c', 'sysprof-files-section.c', 'sysprof-frame-utility.c', 'sysprof-graphics-section.c', diff --git a/src/sysprof/sysprof-entry-popover.c b/src/sysprof/sysprof-entry-popover.c new file mode 100644 index 00000000..1fdf3cbc --- /dev/null +++ b/src/sysprof/sysprof-entry-popover.c @@ -0,0 +1,427 @@ +/* sysprof-entry-popover.c + * + * Copyright (C) 2015 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 . + */ + +#include "config.h" + +#include "sysprof-entry-popover.h" + +typedef struct +{ + GtkPopover parent_instance; + + GtkLabel *title; + GtkLabel *message; + GtkEntry *entry; + GtkButton *button; +} SysprofEntryPopoverPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SysprofEntryPopover, sysprof_entry_popover, GTK_TYPE_POPOVER) + +enum { + PROP_0, + PROP_BUTTON_TEXT, + PROP_MESSAGE, + PROP_READY, + PROP_TEXT, + PROP_TITLE, + LAST_PROP +}; + +enum { + ACTIVATE, + CHANGED, + INSERT_TEXT, + LAST_SIGNAL +}; + +static GParamSpec *properties [LAST_PROP]; +static guint signals [LAST_SIGNAL]; + +const gchar * +sysprof_entry_popover_get_button_text (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_ENTRY_POPOVER (self), NULL); + + return gtk_button_get_label (priv->button); +} + +void +sysprof_entry_popover_set_button_text (SysprofEntryPopover *self, + const gchar *button_text) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_button_set_label (priv->button, button_text); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUTTON_TEXT]); +} + +const gchar * +sysprof_entry_popover_get_message (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_ENTRY_POPOVER (self), NULL); + + return gtk_label_get_text (priv->message); +} + +void +sysprof_entry_popover_set_message (SysprofEntryPopover *self, + const gchar *message) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_label_set_label (priv->message, message); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_MESSAGE]); +} + +gboolean +sysprof_entry_popover_get_ready (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_ENTRY_POPOVER (self), FALSE); + + return gtk_widget_get_sensitive (GTK_WIDGET (priv->button)); +} + +void +sysprof_entry_popover_set_ready (SysprofEntryPopover *self, + gboolean ready) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_widget_set_sensitive (GTK_WIDGET (priv->button), ready); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_READY]); +} + +const gchar * +sysprof_entry_popover_get_text (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_ENTRY_POPOVER (self), NULL); + + return gtk_editable_get_text (GTK_EDITABLE (priv->entry)); +} + +void +sysprof_entry_popover_set_text (SysprofEntryPopover *self, + const gchar *text) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_editable_set_text (GTK_EDITABLE (priv->entry), text); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TEXT]); +} + +const gchar * +sysprof_entry_popover_get_title (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_ENTRY_POPOVER (self), NULL); + + return gtk_label_get_label (priv->title); +} + +void +sysprof_entry_popover_set_title (SysprofEntryPopover *self, + const gchar *title) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_label_set_label (priv->title, title); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); +} + +static void +sysprof_entry_popover_button_clicked (SysprofEntryPopover *self, + GtkButton *button) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + const gchar *text; + + g_assert (SYSPROF_IS_ENTRY_POPOVER (self)); + g_assert (GTK_IS_BUTTON (button)); + + text = gtk_editable_get_text (GTK_EDITABLE (priv->entry)); + g_signal_emit (self, signals [ACTIVATE], 0, text); + gtk_popover_popdown (GTK_POPOVER (self)); +} + +static void +sysprof_entry_popover_entry_activate (SysprofEntryPopover *self, + GtkEntry *entry) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_assert (SYSPROF_IS_ENTRY_POPOVER (self)); + g_assert (GTK_IS_ENTRY (entry)); + + if (sysprof_entry_popover_get_ready (self)) + gtk_widget_activate (GTK_WIDGET (priv->button)); +} + +static void +sysprof_entry_popover_entry_changed (SysprofEntryPopover *self, + GtkEntry *entry) +{ + g_assert (SYSPROF_IS_ENTRY_POPOVER (self)); + g_assert (GTK_IS_ENTRY (entry)); + + g_signal_emit (self, signals [CHANGED], 0); +} + +static void +sysprof_entry_popover_entry_insert_text (SysprofEntryPopover *self, + gchar *new_text, + gint new_text_length, + gint *position, + GtkEntry *entry) +{ + gboolean ret = GDK_EVENT_PROPAGATE; + guint pos; + guint n_chars; + + g_assert (SYSPROF_IS_ENTRY_POPOVER (self)); + g_assert (new_text != NULL); + g_assert (position != NULL); + + pos = *position; + n_chars = (new_text_length >= 0) ? new_text_length : g_utf8_strlen (new_text, -1); + + g_signal_emit (self, signals [INSERT_TEXT], 0, pos, new_text, n_chars, &ret); + + if (ret == GDK_EVENT_STOP) + g_signal_stop_emission_by_name (entry, "insert-text"); +} + +static void +sysprof_entry_popover_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofEntryPopover *self = SYSPROF_ENTRY_POPOVER (object); + + switch (prop_id) + { + case PROP_BUTTON_TEXT: + g_value_set_string (value, sysprof_entry_popover_get_button_text (self)); + break; + + case PROP_MESSAGE: + g_value_set_string (value, sysprof_entry_popover_get_message (self)); + break; + + case PROP_READY: + g_value_set_boolean (value, sysprof_entry_popover_get_ready (self)); + break; + + case PROP_TEXT: + g_value_set_string (value, sysprof_entry_popover_get_text (self)); + break; + + case PROP_TITLE: + g_value_set_string (value, sysprof_entry_popover_get_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_entry_popover_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofEntryPopover *self = SYSPROF_ENTRY_POPOVER (object); + + switch (prop_id) + { + case PROP_BUTTON_TEXT: + sysprof_entry_popover_set_button_text (self, g_value_get_string (value)); + break; + + case PROP_MESSAGE: + sysprof_entry_popover_set_message (self, g_value_get_string (value)); + break; + + case PROP_READY: + sysprof_entry_popover_set_ready (self, g_value_get_boolean (value)); + break; + + case PROP_TEXT: + sysprof_entry_popover_set_text (self, g_value_get_string (value)); + break; + + case PROP_TITLE: + sysprof_entry_popover_set_title (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_entry_popover_class_init (SysprofEntryPopoverClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->get_property = sysprof_entry_popover_get_property; + object_class->set_property = sysprof_entry_popover_set_property; + + properties [PROP_BUTTON_TEXT] = + g_param_spec_string ("button-text", + "Button Text", + "Button Text", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_MESSAGE] = + g_param_spec_string ("message", + "Message", + "Message", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_READY] = + g_param_spec_boolean ("ready", + "Ready", + "Ready", + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_TEXT] = + g_param_spec_string ("text", + "Text", + "Text", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties [PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "Title", + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, LAST_PROP, properties); + + signals [ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SysprofEntryPopoverClass, activate), + NULL, NULL, NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + signals [CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SysprofEntryPopoverClass, changed), + NULL, NULL, NULL, + G_TYPE_NONE, + 0); + + signals [INSERT_TEXT] = + g_signal_new ("insert-text", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SysprofEntryPopoverClass, insert_text), + NULL, NULL, NULL, + G_TYPE_BOOLEAN, + 3, + G_TYPE_UINT, + G_TYPE_STRING, + G_TYPE_UINT); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-entry-popover.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SysprofEntryPopover, title); + gtk_widget_class_bind_template_child_private (widget_class, SysprofEntryPopover, message); + gtk_widget_class_bind_template_child_private (widget_class, SysprofEntryPopover, entry); + gtk_widget_class_bind_template_child_private (widget_class, SysprofEntryPopover, button); +} + +static void +sysprof_entry_popover_init (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + gtk_widget_init_template (GTK_WIDGET (self)); + + g_signal_connect_object (priv->button, + "clicked", + G_CALLBACK (sysprof_entry_popover_button_clicked), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->entry, + "changed", + G_CALLBACK (sysprof_entry_popover_entry_changed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->entry, + "activate", + G_CALLBACK (sysprof_entry_popover_entry_activate), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (gtk_editable_get_delegate (GTK_EDITABLE (priv->entry)), + "insert-text", + G_CALLBACK (sysprof_entry_popover_entry_insert_text), + self, + G_CONNECT_SWAPPED); +} + +GtkWidget * +sysprof_entry_popover_new (void) +{ + return g_object_new (SYSPROF_TYPE_ENTRY_POPOVER, NULL); +} + +void +sysprof_entry_popover_select_all (SysprofEntryPopover *self) +{ + SysprofEntryPopoverPrivate *priv = sysprof_entry_popover_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_ENTRY_POPOVER (self)); + + gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, -1); +} diff --git a/src/sysprof/sysprof-entry-popover.h b/src/sysprof/sysprof-entry-popover.h new file mode 100644 index 00000000..20f189df --- /dev/null +++ b/src/sysprof/sysprof-entry-popover.h @@ -0,0 +1,88 @@ +/* sysprof-entry-popover.h + * + * Copyright (C) 2015-2022 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 . + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_ENTRY_POPOVER (sysprof_entry_popover_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SysprofEntryPopover, sysprof_entry_popover, SYSPROF, ENTRY_POPOVER, GtkPopover) + +struct _SysprofEntryPopoverClass +{ + GtkPopoverClass parent; + + /** + * SysprofEntryPopover::activate: + * @self: The #SysprofEntryPopover instance. + * @text: The text at the time of activation. + * + * This signal is emitted when the popover's forward button is activated. + * Connect to this signal to perform your forward progress. + */ + void (*activate) (SysprofEntryPopover *self, + const gchar *text); + + /** + * SysprofEntryPopover::insert-text: + * @self: A #SysprofEntryPopover. + * @position: the position in UTF-8 characters. + * @chars: the NULL terminated UTF-8 text to insert. + * @n_chars: the number of UTF-8 characters in chars. + * + * Use this signal to determine if text should be allowed to be inserted + * into the text buffer. Return GDK_EVENT_STOP to prevent the text from + * being inserted. + */ + gboolean (*insert_text) (SysprofEntryPopover *self, + guint position, + const gchar *chars, + guint n_chars); + + + /** + * SysprofEntryPopover::changed: + * @self: A #SysprofEntryPopover. + * + * This signal is emitted when the entry text changes. + */ + void (*changed) (SysprofEntryPopover *self); +}; + +GtkWidget *sysprof_entry_popover_new (void); +const gchar *sysprof_entry_popover_get_text (SysprofEntryPopover *self); +void sysprof_entry_popover_set_text (SysprofEntryPopover *self, + const gchar *text); +const gchar *sysprof_entry_popover_get_message (SysprofEntryPopover *self); +void sysprof_entry_popover_set_message (SysprofEntryPopover *self, + const gchar *message); +const gchar *sysprof_entry_popover_get_title (SysprofEntryPopover *self); +void sysprof_entry_popover_set_title (SysprofEntryPopover *self, + const gchar *title); +const gchar *sysprof_entry_popover_get_button_text (SysprofEntryPopover *self); +void sysprof_entry_popover_set_button_text (SysprofEntryPopover *self, + const gchar *button_text); +gboolean sysprof_entry_popover_get_ready (SysprofEntryPopover *self); +void sysprof_entry_popover_set_ready (SysprofEntryPopover *self, + gboolean ready); +void sysprof_entry_popover_select_all (SysprofEntryPopover *self); + +G_END_DECLS diff --git a/src/sysprof/sysprof-entry-popover.ui b/src/sysprof/sysprof-entry-popover.ui new file mode 100644 index 00000000..db7d1820 --- /dev/null +++ b/src/sysprof/sysprof-entry-popover.ui @@ -0,0 +1,59 @@ + + + + + diff --git a/src/sysprof/sysprof-greeter.c b/src/sysprof/sysprof-greeter.c index fbe55724..615b3466 100644 --- a/src/sysprof/sysprof-greeter.c +++ b/src/sysprof/sysprof-greeter.c @@ -26,6 +26,7 @@ #include #include +#include "sysprof-entry-popover.h" #include "sysprof-greeter.h" #include "sysprof-recording-pad.h" @@ -36,12 +37,12 @@ struct _SysprofGreeter AdwWindow parent_instance; GFile *file; + GtkStringList *envvars; AdwViewStack *view_stack; - GtkWidget *open_buttons; - GtkWidget *record_buttons; + GtkListBox *sidebar_list_box; AdwPreferencesPage *record_page; - GtkWidget *open_page; + GtkListBox *app_environment; GtkSwitch *sample_native_stacks; GtkSwitch *sample_javascript_stacks; GtkSwitch *record_disk_usage; @@ -63,22 +64,85 @@ G_DEFINE_FINAL_TYPE (SysprofGreeter, sysprof_greeter, ADW_TYPE_WINDOW) static GParamSpec *properties [N_PROPS]; -static void -sysprof_greeter_view_stack_notify_visible_child (SysprofGreeter *self, - GParamSpec *pspec, - AdwViewStack *stack) +#define STRV_INIT(...) (const char * const[]){__VA_ARGS__,NULL} + +static inline gboolean +str_empty0 (const char *str) { - GtkWidget *visible_child; + return str == NULL || str[0] == 0; +} + +static void +on_env_items_changed_cb (SysprofGreeter *self, + guint position, + guint removed, + guint added, + GListModel *model) +{ + g_assert (SYSPROF_IS_GREETER (self)); + g_assert (G_IS_LIST_MODEL (model)); + + gtk_widget_set_visible (GTK_WIDGET (self->app_environment), + g_list_model_get_n_items (model) > 0); +} + +static void +on_env_entry_changed_cb (SysprofGreeter *self, + SysprofEntryPopover *popover) +{ + const char *errstr = NULL; + gboolean valid = FALSE; + const char *text; + const char *eq; g_assert (SYSPROF_IS_GREETER (self)); - g_assert (ADW_IS_VIEW_STACK (stack)); + g_assert (SYSPROF_IS_ENTRY_POPOVER (popover)); - visible_child = adw_view_stack_get_visible_child (stack); + text = sysprof_entry_popover_get_text (popover); + eq = strchr (text, '='); - gtk_widget_set_visible (GTK_WIDGET (self->record_buttons), - GTK_WIDGET (self->record_page) == visible_child); - gtk_widget_set_visible (GTK_WIDGET (self->open_buttons), - GTK_WIDGET (self->open_page) == visible_child); + if (!str_empty0 (text) && eq == NULL) + errstr = _("Use KEY=VALUE to set an environment variable"); + + if (eq != NULL && eq != text) + { + if (g_unichar_isdigit (g_utf8_get_char (text))) + { + errstr = _("Keys may not start with a number"); + goto failure; + + } + for (const char *iter = text; iter < eq; iter = g_utf8_next_char (iter)) + { + gunichar ch = g_utf8_get_char (iter); + + if (!g_unichar_isalnum (ch) && ch != '_') + { + errstr = _("Keys may only contain alpha-numerics or underline."); + goto failure; + } + } + + if (g_ascii_isalpha (*text)) + valid = TRUE; + } + +failure: + sysprof_entry_popover_set_ready (popover, valid); + sysprof_entry_popover_set_message (popover, errstr); +} + +static void +on_env_entry_activate_cb (SysprofGreeter *self, + const char *text, + SysprofEntryPopover *popover) +{ + g_assert (SYSPROF_IS_GREETER (self)); + g_assert (SYSPROF_IS_ENTRY_POPOVER (popover)); + g_assert (GTK_IS_STRING_LIST (self->envvars)); + + gtk_string_list_append (self->envvars, text); + sysprof_entry_popover_set_text (popover, ""); } static SysprofProfiler * @@ -341,6 +405,120 @@ get_file_path (gpointer unused, return NULL; } +static void +sidebar_row_activated_cb (SysprofGreeter *self, + GtkListBoxRow *row, + GtkListBox *list_box) +{ + AdwViewStackPage *page = g_object_get_data (G_OBJECT (row), "GREETER_PAGE"); + + adw_view_stack_set_visible_child (self->view_stack, + adw_view_stack_page_get_child (page)); + //adw_window_title_set_title (self->view_title, + //adw_view_stack_page_get_title (page)); +} + +static GtkWidget * +sysprof_greeter_create_sidebar_row (gpointer item, + gpointer user_data) +{ + AdwViewStackPage *page = item; + GtkLabel *label; + GtkBox *box; + GtkImage *image; + GtkWidget *row; + + g_assert (ADW_IS_VIEW_STACK_PAGE (page)); + + box = g_object_new (GTK_TYPE_BOX, + "spacing", 6, + NULL); + image = g_object_new (GTK_TYPE_IMAGE, + "icon-name", adw_view_stack_page_get_icon_name (page), + NULL); + label = g_object_new (GTK_TYPE_LABEL, + "label", adw_view_stack_page_get_title (page), + "use-underline", TRUE, + "xalign", .0f, + NULL); + gtk_box_append (box, GTK_WIDGET (image)); + gtk_box_append (box, GTK_WIDGET (label)); + row = g_object_new (GTK_TYPE_LIST_BOX_ROW, + "child", box, + NULL); + g_object_set_data_full (G_OBJECT (row), + "GREETER_PAGE", + g_object_ref (page), + g_object_unref); + return row; +} + +static void +delete_envvar_cb (SysprofGreeter *self, + GtkButton *button) +{ + const char *envvar; + guint n_items; + + g_assert (SYSPROF_IS_GREETER (self)); + g_assert (GTK_IS_BUTTON (button)); + + envvar = g_object_get_data (G_OBJECT (button), "ENVVAR"); + n_items = g_list_model_get_n_items (G_LIST_MODEL (self->envvars)); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(GtkStringObject) str = g_list_model_get_item (G_LIST_MODEL (self->envvars), i); + + if (g_strcmp0 (envvar, gtk_string_object_get_string (str)) == 0) + { + gtk_string_list_remove (self->envvars, i); + break; + } + } +} + +static GtkWidget * +create_envvar_row_cb (gpointer item, + gpointer user_data) +{ + SysprofGreeter *self = user_data; + GtkStringObject *obj = item; + const char *str; + g_autofree char *markup = NULL; + g_autofree char *escaped = NULL; + AdwActionRow *row; + GtkButton *button; + + g_assert (SYSPROF_IS_GREETER (self)); + g_assert (GTK_IS_STRING_OBJECT (obj)); + + str = gtk_string_object_get_string (obj); + escaped = g_markup_escape_text (str, -1); + markup = g_strdup_printf ("%s", escaped); + row = g_object_new (ADW_TYPE_ACTION_ROW, + "title", markup, + "title-selectable", TRUE, + NULL); + button = g_object_new (GTK_TYPE_BUTTON, + "icon-name", "list-remove-symbolic", + "css-classes", STRV_INIT ("flat", "circular"), + "valign", GTK_ALIGN_CENTER, + NULL); + g_object_set_data_full (G_OBJECT (button), + "ENVVAR", + g_strdup (str), + g_free); + g_signal_connect_object (button, + "clicked", + G_CALLBACK (delete_envvar_cb), + self, + G_CONNECT_SWAPPED); + adw_action_row_add_suffix (row, GTK_WIDGET (button)); + + return GTK_WIDGET (row); +} + static void sysprof_greeter_dispose (GObject *object) { @@ -348,6 +526,8 @@ sysprof_greeter_dispose (GObject *object) gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_GREETER); + g_clear_object (&self->envvars); + G_OBJECT_CLASS (sysprof_greeter_parent_class)->dispose (object); } @@ -408,9 +588,8 @@ sysprof_greeter_class_init (SysprofGreeterClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-greeter.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, app_environment); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, bundle_symbols); - gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, open_buttons); - gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, open_page); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_compositor); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_disk_usage); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_network_usage); @@ -420,21 +599,48 @@ sysprof_greeter_class_init (SysprofGreeterClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_system_logs); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_native_stacks); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_javascript_stacks); - gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, record_buttons); + gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sidebar_list_box); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, view_stack); - gtk_widget_class_bind_template_callback (widget_class, sysprof_greeter_view_stack_notify_visible_child); + gtk_widget_class_bind_template_callback (widget_class, sidebar_row_activated_cb); gtk_widget_class_bind_template_callback (widget_class, get_file_path); + gtk_widget_class_bind_template_callback (widget_class, on_env_entry_activate_cb); + gtk_widget_class_bind_template_callback (widget_class, on_env_entry_changed_cb); gtk_widget_class_install_action (widget_class, "win.record-to-memory", NULL, sysprof_greeter_record_to_memory_action); gtk_widget_class_install_action (widget_class, "win.record-to-file", NULL, sysprof_greeter_record_to_file_action); gtk_widget_class_install_action (widget_class, "win.select-file", NULL, sysprof_greeter_select_file_action); + + g_type_ensure (SYSPROF_TYPE_ENTRY_POPOVER); } static void sysprof_greeter_init (SysprofGreeter *self) { + GtkListBoxRow *row; + + self->envvars = gtk_string_list_new (NULL); + g_signal_connect_object (self->envvars, + "items-changed", + G_CALLBACK (on_env_items_changed_cb), + self, + G_CONNECT_SWAPPED); + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_list_box_bind_model (self->sidebar_list_box, + G_LIST_MODEL (adw_view_stack_get_pages (self->view_stack)), + sysprof_greeter_create_sidebar_row, + NULL, NULL); + + gtk_list_box_bind_model (self->app_environment, + G_LIST_MODEL (self->envvars), + create_envvar_row_cb, + self, NULL); + + row = gtk_list_box_get_row_at_index (self->sidebar_list_box, 0); + gtk_list_box_select_row (self->sidebar_list_box, row); + sidebar_row_activated_cb (self, row, self->sidebar_list_box); } GtkWidget * @@ -452,7 +658,7 @@ sysprof_greeter_set_page (SysprofGreeter *self, switch (page) { case SYSPROF_GREETER_PAGE_OPEN: - adw_view_stack_set_visible_child (self->view_stack, GTK_WIDGET (self->open_page)); + //adw_view_stack_set_visible_child (self->view_stack, GTK_WIDGET (self->open_page)); break; default: diff --git a/src/sysprof/sysprof-greeter.ui b/src/sysprof/sysprof-greeter.ui index e42ff916..d2e61e4f 100644 --- a/src/sysprof/sysprof-greeter.ui +++ b/src/sysprof/sysprof-greeter.ui @@ -4,467 +4,522 @@ + +
+ + Help + F1 + app.help + + + About Sysprof + app.about + +
+
diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index 9bb79f02..0ee2de95 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -15,6 +15,7 @@ icons/scalable/actions/metadata-symbolic.svg icons/scalable/actions/process-mounts-symbolic.svg icons/scalable/actions/storage-symbolic.svg + icons/scalable/actions/symbol-symbolic.svg icons/scalable/actions/system-log-symbolic.svg icons/scalable/actions/threads-symbolic.svg sysprof-callgraph-view.ui @@ -25,6 +26,7 @@ sysprof-dbus-utility.ui sysprof-energy-section.ui sysprof-energy-section-counter.ui + sysprof-entry-popover.ui sysprof-files-section.ui sysprof-frame-utility.ui sysprof-graphics-section.ui