diff options
-rw-r--r-- | endless/eosapplication.c | 18 | ||||
-rw-r--r-- | endless/eostopbar-private.h | 5 | ||||
-rw-r--r-- | endless/eostopbar.c | 175 | ||||
-rw-r--r-- | endless/eoswindow.c | 41 |
4 files changed, 234 insertions, 5 deletions
diff --git a/endless/eosapplication.c b/endless/eosapplication.c index 91dbd73..510ff3b 100644 --- a/endless/eosapplication.c +++ b/endless/eosapplication.c @@ -612,6 +612,16 @@ eos_application_class_init (EosApplicationClass *klass) } static void +set_image_credits_action_enabled (EosApplication *self, + gboolean enabled) +{ + GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self), + "image-credits"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); + /* action map owns action */ +} + +static void eos_application_init (EosApplication *self) { EosApplicationPrivate *priv = eos_application_get_instance_private (self); @@ -623,6 +633,7 @@ eos_application_init (EosApplication *self) }; g_action_map_add_action_entries (G_ACTION_MAP (self), actions, G_N_ELEMENTS (actions), self); + set_image_credits_action_enabled (self, FALSE); g_signal_connect (self, "notify::application-id", G_CALLBACK (on_app_id_set), self); @@ -734,11 +745,8 @@ eos_application_set_image_attribution_file (EosApplication *self, if (priv->image_attribution_file == NULL || file == NULL) { - GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self), - "image-credits"); - gboolean enabled = (file == NULL); - g_simple_action_set_enabled (G_SIMPLE_ACTION (self), enabled); - /* action map owns action */ + gboolean enabled = (file != NULL); + set_image_credits_action_enabled (self, enabled); } g_clear_object (&priv->image_attribution_file); diff --git a/endless/eostopbar-private.h b/endless/eostopbar-private.h index 3d0f4a7..aa6e44b 100644 --- a/endless/eostopbar-private.h +++ b/endless/eostopbar-private.h @@ -57,6 +57,11 @@ void eos_top_bar_set_center_widget (EosTopBar *self, void eos_top_bar_update_window_maximized (EosTopBar *self, gboolean is_maximized); +gboolean eos_top_bar_get_show_credits_button (EosTopBar *self); + +void eos_top_bar_set_show_credits_button (EosTopBar *self, + gboolean show_credits_button); + G_END_DECLS #endif /* EOS_TOP_BAR_H */ diff --git a/endless/eostopbar.c b/endless/eostopbar.c index 9d7f276..3c1596d 100644 --- a/endless/eostopbar.c +++ b/endless/eostopbar.c @@ -29,6 +29,7 @@ #define _EOS_TOP_BAR_MAXIMIZE_ICON_NAME "window-maximize-symbolic" #define _EOS_TOP_BAR_UNMAXIMIZE_ICON_NAME "window-unmaximize-symbolic" #define _EOS_TOP_BAR_CLOSE_ICON_NAME "window-close-symbolic" +#define _EOS_TOP_BAR_CREDITS_ICON_NAME "user-info-symbolic" typedef struct { GtkWidget *actions_grid; @@ -44,6 +45,13 @@ typedef struct { GtkWidget *maximize_icon; GtkWidget *close_button; GtkWidget *close_icon; + GtkWidget *credits_button; + GtkWidget *credits_icon; + GtkWidget *credits_stack; + + gboolean show_credits_button; + guint credits_enter_handler; + guint credits_leave_handler; } EosTopBarPrivate; G_DEFINE_TYPE_WITH_PRIVATE (EosTopBar, eos_top_bar, GTK_TYPE_EVENT_BOX) @@ -52,11 +60,58 @@ enum { CLOSE_CLICKED, MINIMIZE_CLICKED, MAXIMIZE_CLICKED, + CREDITS_CLICKED, LAST_SIGNAL }; static guint top_bar_signals[LAST_SIGNAL] = { 0 }; +enum { + PROP_0, + PROP_SHOW_CREDITS_BUTTON, + NPROPS +}; + +static GParamSpec *eos_top_bar_props[NPROPS] = { NULL, }; + +static void +eos_top_bar_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EosTopBar *self = EOS_TOP_BAR (object); + + switch (property_id) + { + case PROP_SHOW_CREDITS_BUTTON: + g_value_set_boolean (value, eos_top_bar_get_show_credits_button (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +eos_top_bar_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EosTopBar *self = EOS_TOP_BAR (object); + + switch (property_id) + { + case PROP_SHOW_CREDITS_BUTTON: + eos_top_bar_set_show_credits_button (self, g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + static void eos_top_bar_get_preferred_height (GtkWidget *widget, int *minimum, @@ -98,6 +153,8 @@ eos_top_bar_class_init (EosTopBarClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + object_class->get_property = eos_top_bar_get_property; + object_class->set_property = eos_top_bar_set_property; widget_class->get_preferred_height = eos_top_bar_get_preferred_height; widget_class->draw = eos_top_bar_draw; @@ -133,6 +190,19 @@ eos_top_bar_class_init (EosTopBarClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + top_bar_signals[CREDITS_CLICKED] = + g_signal_new ("credits-clicked", G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + eos_top_bar_props[PROP_SHOW_CREDITS_BUTTON] = + g_param_spec_boolean ("show-credits-button", "Show credits button", + "Whether the credits button is discoverable", + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, NPROPS, eos_top_bar_props); } static void @@ -159,6 +229,23 @@ on_close_clicked_cb (GtkButton *button, g_signal_emit (self, top_bar_signals[CLOSE_CLICKED], 0); } +static gboolean +on_stack_hover (GtkStack *stack, + GdkEvent *event, + gpointer data) +{ + gboolean show = GPOINTER_TO_INT (data); + gtk_stack_set_visible_child_name (stack, show ? "button" : "blank"); + return GDK_EVENT_PROPAGATE; +} + +static void +on_credits_clicked (GtkButton *button, + EosTopBar *self) +{ + g_signal_emit (self, top_bar_signals[CREDITS_CLICKED], 0); +} + static void eos_top_bar_init (EosTopBar *self) { @@ -236,10 +323,36 @@ eos_top_bar_init (EosTopBar *self) gtk_container_add (GTK_CONTAINER (priv->close_button), priv->close_icon); + /* This works like a revealer but it's really a GtkStack so that it takes up + space and presents a target even when it's not shown. */ + priv->credits_stack = gtk_stack_new (); + gtk_stack_set_transition_type (GTK_STACK (priv->credits_stack), + GTK_STACK_TRANSITION_TYPE_CROSSFADE); + gtk_widget_add_events (priv->credits_stack, + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK); + gtk_stack_add_named (GTK_STACK (priv->credits_stack), + gtk_event_box_new (), "blank"); + priv->credits_button = g_object_new (GTK_TYPE_BUTTON, + "can-focus", FALSE, + "halign", GTK_ALIGN_END, + "valign", GTK_ALIGN_CENTER, + NULL); + priv->credits_icon = + gtk_image_new_from_icon_name (_EOS_TOP_BAR_CREDITS_ICON_NAME, + GTK_ICON_SIZE_SMALL_TOOLBAR); + g_object_set (priv->credits_icon, + "pixel-size", _EOS_TOP_BAR_ICON_SIZE_PX, + "margin", _EOS_TOP_BAR_BUTTON_PADDING_PX, + NULL); + gtk_container_add (GTK_CONTAINER (priv->credits_button), priv->credits_icon); + gtk_stack_add_named (GTK_STACK (priv->credits_stack), + priv->credits_button, "button"); + gtk_container_add (GTK_CONTAINER (priv->actions_grid), priv->left_top_bar_attach); gtk_container_add (GTK_CONTAINER (priv->actions_grid), priv->center_top_bar_attach); + gtk_container_add (GTK_CONTAINER (priv->actions_grid), priv->credits_stack); gtk_container_add (GTK_CONTAINER (priv->actions_grid), priv->minimize_button); gtk_container_add (GTK_CONTAINER (priv->actions_grid), @@ -258,6 +371,16 @@ eos_top_bar_init (EosTopBar *self) G_CALLBACK (on_maximize_clicked_cb), self); g_signal_connect (priv->close_button, "clicked", G_CALLBACK (on_close_clicked_cb), self); + priv->credits_enter_handler = + g_signal_connect (priv->credits_stack, "enter-notify-event", + G_CALLBACK (on_stack_hover), GINT_TO_POINTER (TRUE)); + priv->credits_leave_handler = + g_signal_connect (priv->credits_stack, "leave-notify-event", + G_CALLBACK (on_stack_hover), GINT_TO_POINTER (FALSE)); + g_signal_handler_block (priv->credits_stack, priv->credits_enter_handler); + g_signal_handler_block (priv->credits_stack, priv->credits_leave_handler); + g_signal_connect (priv->credits_button, "clicked", + G_CALLBACK (on_credits_clicked), self); } GtkWidget * @@ -355,3 +478,55 @@ eos_top_bar_update_window_maximized (EosTopBar *self, else gtk_style_context_remove_class (context, _EOS_STYLE_CLASS_UNMAXIMIZED); } + +/* + * eos_top_bar_get_show_credits_button: + * @self: the top bar + * + * See eos_top_bar_set_show_credits_button(). + * + * Returns: %TRUE if credits button should be discoverable, %FALSE if not. + */ +gboolean +eos_top_bar_get_show_credits_button (EosTopBar *self) +{ + EosTopBarPrivate *priv = eos_top_bar_get_instance_private (self); + return priv->show_credits_button; +} + +/* + * eos_top_bar_set_show_credits_button: + * @self: the top bar + * @show_credits_button: whether the credits button should be discoverable. + * + * Gets whether the credits button should be discoverable. + * Note that the credits button is not visible as such, but when the mouse + * hovers over it, it becomes visible if this is set to %TRUE. + * If this is %FALSE, the button never becomes visible. + */ +void +eos_top_bar_set_show_credits_button (EosTopBar *self, + gboolean show_credits_button) +{ + EosTopBarPrivate *priv = eos_top_bar_get_instance_private (self); + if (priv->show_credits_button == show_credits_button) + return; + + priv->show_credits_button = show_credits_button; + if (show_credits_button) + { + g_signal_handler_unblock (priv->credits_stack, + priv->credits_enter_handler); + g_signal_handler_unblock (priv->credits_stack, + priv->credits_leave_handler); + } + else + { + gtk_stack_set_visible_child_name (GTK_STACK (priv->credits_stack), + "blank"); + g_signal_handler_block (priv->credits_stack, priv->credits_enter_handler); + g_signal_handler_block (priv->credits_stack, priv->credits_leave_handler); + } + g_object_notify_by_pspec (G_OBJECT (self), + eos_top_bar_props[PROP_SHOW_CREDITS_BUTTON]); +} diff --git a/endless/eoswindow.c b/endless/eoswindow.c index 0b39ffb..d128a5e 100644 --- a/endless/eoswindow.c +++ b/endless/eoswindow.c @@ -342,6 +342,34 @@ update_page (EosWindow *self) } static void +on_image_credits_enabled_changed (GActionGroup *group, + const gchar *action_name, + gboolean enabled, + EosWindow *self) +{ + EosWindowPrivate *priv = eos_window_get_instance_private (self); + eos_top_bar_set_show_credits_button (EOS_TOP_BAR (priv->top_bar), enabled); +} + +static void +eos_window_constructed (GObject *object) +{ + EosWindow *self = EOS_WINDOW (object); + EosWindowPrivate *priv = eos_window_get_instance_private (self); + + G_OBJECT_CLASS (eos_window_parent_class)->constructed (object); + + GtkApplication *application = + gtk_window_get_application (GTK_WINDOW (object)); + GFile *credits_file = + eos_application_get_image_attribution_file (EOS_APPLICATION (application)); + eos_top_bar_set_show_credits_button (EOS_TOP_BAR (priv->top_bar), + (credits_file != NULL)); + g_signal_connect (application, "action-enabled-changed::image-credits", + G_CALLBACK (on_image_credits_enabled_changed), self); +} + +static void eos_window_get_property (GObject *object, guint property_id, GValue *value, @@ -510,6 +538,7 @@ eos_window_class_init (EosWindowClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + object_class->constructed = eos_window_constructed; object_class->get_property = eos_window_get_property; object_class->set_property = eos_window_set_property; object_class->finalize = eos_window_finalize; @@ -631,6 +660,16 @@ on_close_clicked_cb (GtkWidget *top_bar, gtk_window_close (GTK_WINDOW (self)); } +static void +on_credits_clicked (GtkWidget *top_bar, + EosWindow *self) +{ + GtkApplication *application = gtk_window_get_application (GTK_WINDOW (self)); + /* application cannot be NULL */ + g_action_group_activate_action (G_ACTION_GROUP (application), "image-credits", + NULL); +} + static gboolean on_window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event) @@ -748,6 +787,8 @@ eos_window_init (EosWindow *self) G_CALLBACK (on_maximize_clicked_cb), self); g_signal_connect (priv->top_bar, "close-clicked", G_CALLBACK (on_close_clicked_cb), self); + g_signal_connect (priv->top_bar, "credits-clicked", + G_CALLBACK (on_credits_clicked), self); g_signal_connect (self, "window-state-event", G_CALLBACK (on_window_state_event_cb), NULL); |