diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:39:48 +0000 |
commit | 3ade071f273aaa973e44bf95d6b1d4913a18f03b (patch) | |
tree | e2f99d267ae18427645404f215b984afbe73098d /src/nautilus-floating-bar.c | |
parent | Initial commit. (diff) | |
download | nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.tar.xz nautilus-3ade071f273aaa973e44bf95d6b1d4913a18f03b.zip |
Adding upstream version 43.2.upstream/43.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/nautilus-floating-bar.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/src/nautilus-floating-bar.c b/src/nautilus-floating-bar.c new file mode 100644 index 0000000..547651f --- /dev/null +++ b/src/nautilus-floating-bar.c @@ -0,0 +1,549 @@ +/* Nautilus - Floating status bar. + * + * Copyright (C) 2011 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + * Authors: Cosimo Cecchi <cosimoc@redhat.com> + * + */ + +#include <config.h> + +#include <string.h> + +#include "nautilus-floating-bar.h" + +#define HOVER_HIDE_TIMEOUT_INTERVAL 100 + +struct _NautilusFloatingBar +{ + GtkBox parent; + + gchar *primary_label; + gchar *details_label; + + GtkWidget *primary_label_widget; + GtkWidget *details_label_widget; + GtkWidget *spinner; + gboolean show_spinner; + GtkWidget *stop_button; + gboolean show_stop; + gboolean is_interactive; + guint hover_timeout_id; + + GtkEventController *motion_controller; + double pointer_x_in_parent_coordinates; + double pointer_y_in_parent_coordinates; +}; + +enum +{ + PROP_PRIMARY_LABEL = 1, + PROP_DETAILS_LABEL, + PROP_SHOW_SPINNER, + PROP_SHOW_STOP, + NUM_PROPERTIES +}; + +enum +{ + STOP, + NUM_SIGNALS +}; + +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; +static guint signals[NUM_SIGNALS] = { 0, }; + +G_DEFINE_TYPE (NautilusFloatingBar, nautilus_floating_bar, + GTK_TYPE_BOX); + +static void +stop_button_clicked_cb (GtkButton *button, + NautilusFloatingBar *self) +{ + g_signal_emit (self, signals[STOP], 0); +} + +static void +nautilus_floating_bar_finalize (GObject *obj) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj); + + nautilus_floating_bar_remove_hover_timeout (self); + g_free (self->primary_label); + g_free (self->details_label); + g_clear_object (&self->motion_controller); + + G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->finalize (obj); +} + +static void +nautilus_floating_bar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (object); + + switch (property_id) + { + case PROP_PRIMARY_LABEL: + { + g_value_set_string (value, self->primary_label); + } + break; + + case PROP_DETAILS_LABEL: + { + g_value_set_string (value, self->details_label); + } + break; + + case PROP_SHOW_SPINNER: + { + g_value_set_boolean (value, self->show_spinner); + } + break; + + case PROP_SHOW_STOP: + { + g_value_set_boolean (value, self->show_stop); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + break; + } +} + +static void +nautilus_floating_bar_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (object); + + switch (property_id) + { + case PROP_PRIMARY_LABEL: + { + nautilus_floating_bar_set_primary_label (self, g_value_get_string (value)); + } + break; + + case PROP_DETAILS_LABEL: + { + nautilus_floating_bar_set_details_label (self, g_value_get_string (value)); + } + break; + + case PROP_SHOW_SPINNER: + { + nautilus_floating_bar_set_show_spinner (self, g_value_get_boolean (value)); + } + break; + + case PROP_SHOW_STOP: + { + nautilus_floating_bar_set_show_stop (self, g_value_get_boolean (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } + break; + } +} + +static void +update_labels (NautilusFloatingBar *self) +{ + gboolean primary_visible, details_visible; + + primary_visible = (self->primary_label != NULL) && + (strlen (self->primary_label) > 0); + details_visible = (self->details_label != NULL) && + (strlen (self->details_label) > 0); + + gtk_label_set_text (GTK_LABEL (self->primary_label_widget), + self->primary_label); + gtk_widget_set_visible (self->primary_label_widget, primary_visible); + + gtk_label_set_text (GTK_LABEL (self->details_label_widget), + self->details_label); + gtk_widget_set_visible (self->details_label_widget, details_visible); +} + +void +nautilus_floating_bar_remove_hover_timeout (NautilusFloatingBar *self) +{ + if (self->hover_timeout_id != 0) + { + g_source_remove (self->hover_timeout_id); + self->hover_timeout_id = 0; + } +} + +typedef struct +{ + NautilusFloatingBar *floating_bar; + gint x_down_limit; + gint x_upper_limit; + gint y_down_limit; + gint y_upper_limit; +} CheckPointerData; + +static void +check_pointer_data_free (gpointer data) +{ + g_slice_free (CheckPointerData, data); +} + +static gboolean +check_pointer_timeout (gpointer user_data) +{ + CheckPointerData *data = user_data; + NautilusFloatingBar *self = data->floating_bar; + double pointer_x = self->pointer_x_in_parent_coordinates; + double pointer_y = self->pointer_y_in_parent_coordinates; + + if (pointer_x == -1 || + pointer_y == -1 || + pointer_x < data->x_down_limit || + pointer_x > data->x_upper_limit || + pointer_y < data->y_down_limit || + pointer_y > data->y_upper_limit) + { + gtk_widget_show (GTK_WIDGET (self)); + self->hover_timeout_id = 0; + + return G_SOURCE_REMOVE; + } + else + { + gtk_widget_hide (GTK_WIDGET (self)); + } + + return G_SOURCE_CONTINUE; +} + +static void +on_event_controller_motion_motion (GtkEventControllerMotion *controller, + double x, + double y, + gpointer user_data) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (user_data); + GtkWidget *parent; + CheckPointerData *data; + gdouble x_pos; + gdouble y_pos; + + self->pointer_x_in_parent_coordinates = x; + self->pointer_y_in_parent_coordinates = y; + + if (self->is_interactive || !gtk_widget_is_visible (GTK_WIDGET (self))) + { + return; + } + + parent = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller)); + gtk_widget_translate_coordinates (GTK_WIDGET (self), parent, 0, 0, &x_pos, &y_pos); + + if (x < x_pos || y < y_pos) + { + return; + } + + if (self->hover_timeout_id != 0) + { + return; + } + + data = g_slice_new (CheckPointerData); + data->floating_bar = self; + data->x_down_limit = x_pos; + data->x_upper_limit = x_pos + gtk_widget_get_allocated_width (GTK_WIDGET (self)); + data->y_down_limit = y_pos; + data->y_upper_limit = y_pos + gtk_widget_get_allocated_height (GTK_WIDGET (self)); + + self->hover_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, HOVER_HIDE_TIMEOUT_INTERVAL, + check_pointer_timeout, data, + check_pointer_data_free); + + g_source_set_name_by_id (self->hover_timeout_id, "[nautilus-floating-bar] on_event_controller_motion_motion"); +} + +static void +on_event_controller_motion_leave (GtkEventControllerMotion *controller, + gpointer user_data) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (user_data); + + self->pointer_x_in_parent_coordinates = -1; + self->pointer_y_in_parent_coordinates = -1; +} + +static void +on_parent_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (object); + GtkWidget *parent; + + parent = gtk_widget_get_parent (GTK_WIDGET (object)); + + if (self->motion_controller != NULL) + { + GtkWidget *old_parent; + + old_parent = gtk_event_controller_get_widget (self->motion_controller); + g_warn_if_fail (old_parent != NULL); + if (old_parent != NULL) + { + gtk_widget_remove_controller (old_parent, self->motion_controller); + } + + g_object_unref (self->motion_controller); + self->motion_controller = NULL; + } + + if (parent != NULL) + { + self->motion_controller = g_object_ref (gtk_event_controller_motion_new ()); + gtk_widget_add_controller (parent, self->motion_controller); + + gtk_event_controller_set_propagation_phase (self->motion_controller, + GTK_PHASE_CAPTURE); + g_signal_connect (self->motion_controller, "leave", + G_CALLBACK (on_event_controller_motion_leave), self); + g_signal_connect (self->motion_controller, "motion", + G_CALLBACK (on_event_controller_motion_motion), self); + } +} + +static void +nautilus_floating_bar_constructed (GObject *obj) +{ + NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj); + GtkWidget *w, *box, *labels_box; + GtkStyleContext *context; + + G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->constructed (obj); + + box = GTK_WIDGET (obj); + + w = gtk_spinner_new (); + gtk_box_append (GTK_BOX (box), w); + gtk_widget_set_visible (w, self->show_spinner); + /* As a workaround for https://gitlab.gnome.org/GNOME/gtk/-/issues/1025, + * ensure the spinner animates if and only if it's visible, to reduce CPU + * usage. */ + g_object_bind_property (obj, "show-spinner", + w, "spinning", + G_BINDING_SYNC_CREATE); + self->spinner = w; + + gtk_widget_set_size_request (w, 16, 16); + gtk_widget_set_margin_start (w, 8); + + labels_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_append (GTK_BOX (box), labels_box); + g_object_set (labels_box, + "hexpand", TRUE, + "margin-top", 2, + "margin-bottom", 2, + "margin-start", 12, + "margin-end", 12, + NULL); + gtk_widget_show (labels_box); + + w = gtk_label_new (NULL); + gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_MIDDLE); + gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE); + gtk_box_append (GTK_BOX (labels_box), w); + self->primary_label_widget = w; + gtk_widget_show (w); + + w = gtk_label_new (NULL); + gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE); + gtk_box_append (GTK_BOX (labels_box), w); + self->details_label_widget = w; + gtk_widget_show (w); + + w = gtk_button_new_from_icon_name ("process-stop-symbolic"); + context = gtk_widget_get_style_context (w); + gtk_style_context_add_class (context, "circular"); + gtk_style_context_add_class (context, "flat"); + gtk_widget_set_valign (w, GTK_ALIGN_CENTER); + gtk_box_append (GTK_BOX (self), w); + self->stop_button = w; + gtk_widget_set_visible (w, FALSE); + + g_signal_connect (self->stop_button, "clicked", + G_CALLBACK (stop_button_clicked_cb), self); +} + +static void +nautilus_floating_bar_init (NautilusFloatingBar *self) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_add_class (context, "floating-bar"); + + self->motion_controller = NULL; + self->pointer_x_in_parent_coordinates = -1; + self->pointer_y_in_parent_coordinates = -1; + + g_signal_connect (self, + "notify::parent", + G_CALLBACK (on_parent_changed), + NULL); +} + +static void +nautilus_floating_bar_class_init (NautilusFloatingBarClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->constructed = nautilus_floating_bar_constructed; + oclass->set_property = nautilus_floating_bar_set_property; + oclass->get_property = nautilus_floating_bar_get_property; + oclass->finalize = nautilus_floating_bar_finalize; + + properties[PROP_PRIMARY_LABEL] = + g_param_spec_string ("primary-label", + "Bar's primary label", + "Primary label displayed by the bar", + NULL, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + properties[PROP_DETAILS_LABEL] = + g_param_spec_string ("details-label", + "Bar's details label", + "Details label displayed by the bar", + NULL, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + properties[PROP_SHOW_SPINNER] = + g_param_spec_boolean ("show-spinner", + "Show spinner", + "Whether a spinner should be shown in the floating bar", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_SHOW_STOP] = + g_param_spec_boolean ("show-stop", + "Show stop button", + "Whether a stop button should be shown in the floating bar", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + signals[STOP] = g_signal_new ("stop", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); +} + +void +nautilus_floating_bar_set_primary_label (NautilusFloatingBar *self, + const gchar *label) +{ + if (g_strcmp0 (self->primary_label, label) != 0) + { + g_free (self->primary_label); + self->primary_label = g_strdup (label); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIMARY_LABEL]); + + update_labels (self); + } +} + +void +nautilus_floating_bar_set_details_label (NautilusFloatingBar *self, + const gchar *label) +{ + if (g_strcmp0 (self->details_label, label) != 0) + { + g_free (self->details_label); + self->details_label = g_strdup (label); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DETAILS_LABEL]); + + update_labels (self); + } +} + +void +nautilus_floating_bar_set_labels (NautilusFloatingBar *self, + const gchar *primary_label, + const gchar *details_label) +{ + nautilus_floating_bar_set_primary_label (self, primary_label); + nautilus_floating_bar_set_details_label (self, details_label); +} + +void +nautilus_floating_bar_set_show_spinner (NautilusFloatingBar *self, + gboolean show_spinner) +{ + if (self->show_spinner != show_spinner) + { + self->show_spinner = show_spinner; + gtk_widget_set_visible (self->spinner, + show_spinner); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SPINNER]); + } +} + +void +nautilus_floating_bar_set_show_stop (NautilusFloatingBar *self, + gboolean show_stop) +{ + if (self->show_stop != show_stop) + { + self->show_stop = show_stop; + gtk_widget_set_visible (self->stop_button, + show_stop); + self->is_interactive = show_stop; + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_STOP]); + } +} + +GtkWidget * +nautilus_floating_bar_new (const gchar *primary_label, + const gchar *details_label, + gboolean show_spinner) +{ + return g_object_new (NAUTILUS_TYPE_FLOATING_BAR, + "primary-label", primary_label, + "details-label", details_label, + "show-spinner", show_spinner, + "orientation", GTK_ORIENTATION_HORIZONTAL, + "spacing", 8, + NULL); +} |