diff --git a/src/layersurface.c b/src/layersurface.c index 7ac55b3d42e7c37b8c40ee82a115cc36a6064c2e..5c06d0b942d7060a89e1174a15eeb53b6c85fcc0 100644 --- a/src/layersurface.c +++ b/src/layersurface.c @@ -11,6 +11,8 @@ #include "config.h" #include "layersurface.h" +#include "phosh-wayland.h" + #include /** @@ -755,3 +757,57 @@ phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self) if (priv->wl_surface) wl_surface_commit (priv->wl_surface); } + + +static struct wl_region * +wl_region_from_cairo_region (cairo_region_t *region) +{ + PhoshWayland *wayland; + struct wl_compositor *compositor; + struct wl_region *wl_region; + int i, n_rects; + + wayland = phosh_wayland_get_default (); + compositor = phosh_wayland_get_wl_compositor (wayland); + wl_region = wl_compositor_create_region (compositor); + + if (wl_region == NULL) + return NULL; + + n_rects = cairo_region_num_rectangles (region); + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (region, i, &rect); + wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height); + } + + return wl_region; +} + + +/** + * phosh_layer_surface_wl_surface_set_input_region: + * @self: The #PhoshLayerSurface + * @region: The region to set + * + * Sets the region of the surface that can receive pointer and touch events. + */ +void +phosh_layer_surface_wl_surface_set_input_region (PhoshLayerSurface *self, + cairo_region_t *region) +{ + PhoshLayerSurfacePrivate *priv; + struct wl_region *wl_region; + + g_return_if_fail (PHOSH_IS_LAYER_SURFACE (self)); + priv = phosh_layer_surface_get_instance_private (self); + + if (priv->wl_surface) + wl_surface_commit (priv->wl_surface); + + wl_region = wl_region_from_cairo_region (region); + wl_surface_set_input_region (priv->wl_surface, wl_region); + + if (wl_region != NULL) + wl_region_destroy (wl_region); +} diff --git a/src/layersurface.h b/src/layersurface.h index 7656e170536183e88092d00e2f26c9ef0ef4c0c7..ed6aad55b819ecadb05d0833418225f8561b447f 100644 --- a/src/layersurface.h +++ b/src/layersurface.h @@ -47,5 +47,7 @@ void phosh_layer_surface_set_exclusive_zone(PhoshLa void phosh_layer_surface_set_kbd_interactivity(PhoshLayerSurface *self, gboolean interactivity); void phosh_layer_surface_wl_surface_commit (PhoshLayerSurface *self); +void phosh_layer_surface_wl_surface_set_input_region (PhoshLayerSurface *self, + cairo_region_t *region); G_END_DECLS diff --git a/src/notifications/notification-banner.c b/src/notifications/notification-banner.c index 8ff4419a368ff16054cca873bd6722dfce1109fe..decc7600c4541a388413ae92f0a6ea2a03f97bd5 100644 --- a/src/notifications/notification-banner.c +++ b/src/notifications/notification-banner.c @@ -12,6 +12,7 @@ #include "notification-banner.h" #include "notification-frame.h" #include "shell.h" +#include "swipe-away-bin.h" #include "util.h" #include @@ -33,14 +34,10 @@ static GParamSpec *props[LAST_PROP]; struct _PhoshNotificationBanner { PhoshLayerSurface parent; + PhoshSwipeAwayBin *hide_bin; PhoshNotification *notification; gulong handler_expired; gulong handler_closed; - - struct { - double progress; - gint64 last_frame; - } animation; }; typedef struct _PhoshNotificationBanner PhoshNotificationBanner; @@ -56,6 +53,16 @@ clear_handler (PhoshNotificationBanner *self) } +static void +removed (PhoshNotificationBanner *self) +{ + clear_handler (self); + + /* Close the banner */ + gtk_widget_destroy (GTK_WIDGET (self)); +} + + static void expired (PhoshNotification *notification, PhoshNotificationBanner *self) @@ -63,10 +70,7 @@ expired (PhoshNotification *notification, g_return_if_fail (PHOSH_IS_NOTIFICATION_BANNER (self)); g_return_if_fail (PHOSH_IS_NOTIFICATION (notification)); - clear_handler (self); - - /* Close the banner */ - gtk_widget_destroy (GTK_WIDGET (self)); + phosh_swipe_away_bin_remove (self->hide_bin); } @@ -78,10 +82,7 @@ closed (PhoshNotification *notification, g_return_if_fail (PHOSH_IS_NOTIFICATION_BANNER (self)); g_return_if_fail (PHOSH_IS_NOTIFICATION (notification)); - clear_handler (self); - - /* Close the banner */ - gtk_widget_destroy (GTK_WIDGET (self)); + removed (self); } @@ -90,13 +91,19 @@ phosh_notification_banner_set_notification (PhoshNotificationBanner *self, PhoshNotification *notification) { GtkWidget *content; + int height; g_set_object (&self->notification, notification); content = phosh_notification_frame_new (TRUE); + phosh_notification_frame_set_reserve_size (PHOSH_NOTIFICATION_FRAME (content), TRUE); phosh_notification_frame_bind_notification (PHOSH_NOTIFICATION_FRAME (content), self->notification); - gtk_container_add (GTK_CONTAINER (self), content); + + g_object_get (self, "height", &height, NULL); + gtk_widget_set_margin_top (content, height); + + gtk_container_add (GTK_CONTAINER (self->hide_bin), content); self->handler_expired = g_signal_connect (self->notification, "expired", G_CALLBACK (expired), self); @@ -160,64 +167,35 @@ phosh_notification_banner_finalize (GObject *object) static void -phosh_notification_banner_slide (PhoshNotificationBanner *self) -{ - int margin; - int height; - double progress = hdy_ease_out_cubic (self->animation.progress); - - progress = 1.0 - progress; - - gtk_window_get_size (GTK_WINDOW (self), NULL, &height); - margin = (height - 300) * progress; - - phosh_layer_surface_set_margins (PHOSH_LAYER_SURFACE (self), margin, 0, 0, 0); - - phosh_layer_surface_wl_surface_commit (PHOSH_LAYER_SURFACE (self)); -} - - -static gboolean -animate_down_cb (GtkWidget *widget, - GdkFrameClock *frame_clock, - gpointer user_data) +phosh_notification_banner_show (GtkWidget *widget) { - gint64 time; - gboolean finished = FALSE; PhoshNotificationBanner *self = PHOSH_NOTIFICATION_BANNER (widget); - time = gdk_frame_clock_get_frame_time (frame_clock) - self->animation.last_frame; - if (self->animation.last_frame < 0) { - time = 0; - } - - self->animation.progress += 0.06666 * time / 16666.00; - self->animation.last_frame = gdk_frame_clock_get_frame_time (frame_clock); - - if (self->animation.progress >= 1.0) { - finished = TRUE; - self->animation.progress = 1.0; - } + GTK_WIDGET_CLASS (phosh_notification_banner_parent_class)->show (widget); - phosh_notification_banner_slide (self); + phosh_swipe_away_bin_reveal (self->hide_bin); - return finished ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE; } static void -phosh_notification_banner_show (GtkWidget *widget) +phosh_notification_banner_size_allocate (GtkWidget *widget, + GtkAllocation *alloc) { PhoshNotificationBanner *self = PHOSH_NOTIFICATION_BANNER (widget); - gboolean enable_animations; - enable_animations = hdy_get_enable_animations (GTK_WIDGET (self)); + GTK_WIDGET_CLASS (phosh_notification_banner_parent_class)->size_allocate (widget, alloc); - self->animation.last_frame = -1; - self->animation.progress = enable_animations ? 0.0 : 1.0; - gtk_widget_add_tick_callback (GTK_WIDGET (self), animate_down_cb, NULL, NULL); + if (gtk_widget_get_realized (widget)) { + cairo_rectangle_int_t rect = { 0, 0, 10, 10 }; + cairo_region_t *region = cairo_region_create_rectangle (&rect); - GTK_WIDGET_CLASS (phosh_notification_banner_parent_class)->show (widget); + g_print ("does this ever happen\n"); + + phosh_layer_surface_wl_surface_set_input_region (PHOSH_LAYER_SURFACE (self), region); + + cairo_region_destroy (region); + } } @@ -232,6 +210,7 @@ phosh_notification_banner_class_init (PhoshNotificationBannerClass *klass) object_class->get_property = phosh_notification_banner_get_property; widget_class->show = phosh_notification_banner_show; + widget_class->size_allocate = phosh_notification_banner_size_allocate; /** * PhoshNotificationBanner:notification: @@ -249,6 +228,12 @@ phosh_notification_banner_class_init (PhoshNotificationBannerClass *klass) g_object_class_install_properties (object_class, LAST_PROP, props); + gtk_widget_class_set_template_from_resource (widget_class, + "/sm/puri/phosh/ui/notification-banner.ui"); + + gtk_widget_class_bind_template_child (widget_class, PhoshNotificationBanner, hide_bin); + gtk_widget_class_bind_template_callback (widget_class, removed); + gtk_widget_class_set_css_name (widget_class, "phosh-notification-banner"); } @@ -256,7 +241,11 @@ phosh_notification_banner_class_init (PhoshNotificationBannerClass *klass) static void phosh_notification_banner_init (PhoshNotificationBanner *self) { - self->animation.progress = 0.0; + g_type_ensure (PHOSH_TYPE_SWIPE_AWAY_BIN); + + gtk_widget_init_template (GTK_WIDGET (self)); + + phosh_swipe_away_bin_hide (self->hide_bin); } @@ -273,16 +262,11 @@ phosh_notification_banner_new (PhoshNotification *notification) return g_object_new (PHOSH_TYPE_NOTIFICATION_BANNER, "notification", notification, /* layer surface */ - "margin-top", -300, "layer-shell", phosh_wayland_get_zwlr_layer_shell_v1 (wl), "wl-output", monitor ? monitor->wl_output : NULL, "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, - "height", 50, - "width", MIN (width, 450), + "width", MIN (width, 450) * 3, "layer", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, - "kbd-interactivity", FALSE, - "exclusive-zone", 0, - "namespace", "phosh notification", NULL); } diff --git a/src/notifications/notification-frame.c b/src/notifications/notification-frame.c index 801becae3301f90b8a73589872178d9c3729d7c5..0bda9211a7d780dd33578bae098e27807b485adf 100644 --- a/src/notifications/notification-frame.c +++ b/src/notifications/notification-frame.c @@ -12,9 +12,12 @@ #include "notification-content.h" #include "notification-frame.h" #include "notification-source.h" +#include "swipe-away-bin.h" #include "util.h" #include "timestamp-label.h" +#include + /** * SECTION:notification-frame * @short_description: A frame containing one or more notifications @@ -25,13 +28,14 @@ enum { PROP_0, PROP_SHOW_BODY, + PROP_RESERVE_SIZE, LAST_PROP }; static GParamSpec *props[LAST_PROP]; struct _PhoshNotificationFrame { - GtkBox parent; + GtkEventBox parent; GListModel *model; gulong model_watch; @@ -40,17 +44,31 @@ struct _PhoshNotificationFrame { GBinding *bind_icon; GBinding *bind_timestamp; + GtkWidget *box; GtkWidget *lbl_app_name; GtkWidget *img_icon; GtkWidget *list_notifs; GtkWidget *updated; gboolean show_body; + + /* needed so that the gestures aren't immediately destroyed */ + GtkGesture *header_click_gesture; + GtkGesture *list_click_gesture; + + int start_x; + int start_y; + GtkListBoxRow *active_row; + + gboolean reserve_size; }; typedef struct _PhoshNotificationFrame PhoshNotificationFrame; -G_DEFINE_TYPE (PhoshNotificationFrame, phosh_notification_frame, GTK_TYPE_BOX) +G_DEFINE_TYPE (PhoshNotificationFrame, phosh_notification_frame, GTK_TYPE_EVENT_BOX) + + +#define DRAG_THRESHOLD_DISTANCE 16 enum { @@ -72,6 +90,9 @@ phosh_notification_frame_set_property (GObject *object, case PROP_SHOW_BODY: self->show_body = g_value_get_boolean (value); break; + case PROP_RESERVE_SIZE: + phosh_notification_frame_set_reserve_size (self, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -91,6 +112,9 @@ phosh_notification_frame_get_property (GObject *object, case PROP_SHOW_BODY: g_value_set_boolean (value, self->show_body); break; + case PROP_RESERVE_SIZE: + g_value_set_boolean (value, phosh_notification_frame_get_reserve_size (self)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -113,22 +137,118 @@ phosh_notification_frame_finalize (GObject *object) } -/* When the title row is clicked we proxy it to the first item */ static gboolean -header_activated (PhoshNotificationFrame *self, GdkEventButton *event) +motion_notify (PhoshNotificationFrame *self, + GdkEventMotion *event) { - g_autoptr (PhoshNotification) notification = NULL; + if (self->start_x >= 0 && self->start_y >= 0) { + int current_x, current_y; + double dx, dy; + + gtk_widget_translate_coordinates (GTK_WIDGET (self->box), + gtk_widget_get_toplevel (GTK_WIDGET (self)), + event->x, event->y, + ¤t_x, ¤t_y); + + dx = current_x - self->start_x; + dy = current_y - self->start_y; + + if (sqrt (dx * dx + dy * dy) > DRAG_THRESHOLD_DISTANCE) { + gtk_gesture_set_state (self->header_click_gesture, GTK_EVENT_SEQUENCE_DENIED); + gtk_gesture_set_state (self->list_click_gesture, GTK_EVENT_SEQUENCE_DENIED); + } + } - g_return_val_if_fail (PHOSH_IS_NOTIFICATION_FRAME (self), FALSE); + return GDK_EVENT_PROPAGATE; +} - notification = g_list_model_get_item (self->model, 0); - g_return_val_if_fail (PHOSH_IS_NOTIFICATION (notification), FALSE); +static void +pressed (PhoshNotificationFrame *self, + int n_press, + double x, + double y, + GtkGesture *gesture, + GtkGesture *other_gesture) +{ + GdkEventSequence *sequence = + gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); + + if (n_press != 1) { + gtk_gesture_set_sequence_state (gesture, sequence, GTK_EVENT_SEQUENCE_DENIED); + + return; + } + + gtk_widget_translate_coordinates (self->box, + gtk_widget_get_toplevel (GTK_WIDGET (self)), + x, y, + &self->start_x, &self->start_y); + + /* When the title row is clicked we proxy it to the first item */ + self->active_row = + gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->list_notifs), + gesture == self->header_click_gesture ? 0 : y); + + gtk_gesture_set_sequence_state (other_gesture, sequence, GTK_EVENT_SEQUENCE_DENIED); +} + +static void +header_pressed (PhoshNotificationFrame *self, + int n_press, + double x, + double y) +{ + pressed (self, n_press, x, y, + self->header_click_gesture, + self->list_click_gesture); +} + + +static void +list_pressed (PhoshNotificationFrame *self, + int n_press, + double x, + double y) +{ + pressed (self, n_press, x, y, + self->list_click_gesture, + self->header_click_gesture); +} + + +static void +released (PhoshNotificationFrame *self, + int n_press, + double x, + double y, + GtkGesture *gesture) +{ + GtkListBoxRow *pressed_row = self->active_row; + GtkListBoxRow *active_row; + PhoshNotificationContent *content; + PhoshNotification *notification; + + /* When the title row is clicked we proxy it to the first item */ + active_row = + gtk_list_box_get_row_at_y (GTK_LIST_BOX (self->list_notifs), + gesture == self->header_click_gesture ? 0 : y); + + self->active_row = NULL; + self->start_x = -1; + self->start_y = -1; + + if (pressed_row != active_row) { + gtk_gesture_set_state (gesture, GTK_EVENT_SEQUENCE_DENIED); + + return; + } + + content = PHOSH_NOTIFICATION_CONTENT (active_row); + notification = phosh_notification_content_get_notification (content); phosh_notification_activate (notification, PHOSH_NOTIFICATION_DEFAULT_ACTION); - - return FALSE; } @@ -150,6 +270,22 @@ notification_activated (PhoshNotificationFrame *self, } +static void +removed (PhoshNotificationFrame *self) +{ + guint i, n; + + n = g_list_model_get_n_items (self->model); + for (i = 0; i < n; i++) { + g_autoptr (PhoshNotification) notification = NULL; + + notification = g_list_model_get_item (self->model, 0); + + phosh_notification_close (notification, PHOSH_NOTIFICATION_REASON_CLOSED); + } +} + + static void phosh_notification_frame_class_init (PhoshNotificationFrameClass *klass) { @@ -173,6 +309,13 @@ phosh_notification_frame_class_init (PhoshNotificationFrameClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + props[PROP_RESERVE_SIZE] = + g_param_spec_boolean ("reserve-size", + "Reserve Size", + "Allocate larger size than the child so that the child is never clipped when animating", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + g_object_class_install_properties (object_class, LAST_PROP, props); signals[SIGNAL_EMPTY] = g_signal_new ("empty", @@ -188,13 +331,20 @@ phosh_notification_frame_class_init (PhoshNotificationFrameClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/sm/puri/phosh/ui/notification-frame.ui"); + gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, box); gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, lbl_app_name); gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, img_icon); gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, list_notifs); gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, updated); + gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, header_click_gesture); + gtk_widget_class_bind_template_child (widget_class, PhoshNotificationFrame, list_click_gesture); - gtk_widget_class_bind_template_callback (widget_class, header_activated); + gtk_widget_class_bind_template_callback (widget_class, motion_notify); + gtk_widget_class_bind_template_callback (widget_class, header_pressed); + gtk_widget_class_bind_template_callback (widget_class, list_pressed); + gtk_widget_class_bind_template_callback (widget_class, released); gtk_widget_class_bind_template_callback (widget_class, notification_activated); + gtk_widget_class_bind_template_callback (widget_class, removed); gtk_widget_class_set_css_name (widget_class, "phosh-notification-frame"); } @@ -204,6 +354,11 @@ static void phosh_notification_frame_init (PhoshNotificationFrame *self) { self->show_body = TRUE; + self->start_x = -1; + self->start_y = -1; + + g_type_ensure (PHOSH_TYPE_SWIPE_AWAY_BIN); + gtk_widget_init_template (GTK_WIDGET (self)); } @@ -319,3 +474,29 @@ phosh_notification_frame_bind_notification (PhoshNotificationFrame *self, phosh_notification_frame_bind_model (self, G_LIST_MODEL (store)); } + + +gboolean +phosh_notification_frame_get_reserve_size (PhoshNotificationFrame *self) +{ + g_return_val_if_fail (PHOSH_IS_NOTIFICATION_FRAME (self), FALSE); + + return self->reserve_size; +} + + +void +phosh_notification_frame_set_reserve_size (PhoshNotificationFrame *self, + gboolean reserve_size) +{ + g_return_if_fail (PHOSH_IS_NOTIFICATION_FRAME (self)); + + reserve_size = !!reserve_size; + + if (reserve_size == self->reserve_size) + return; + + self->reserve_size = reserve_size; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_RESERVE_SIZE]); +} diff --git a/src/notifications/notification-frame.h b/src/notifications/notification-frame.h index 843fa0ab3d4532d4fabdc9ccef67784dcbe0c09c..8ca2258075b191f752d0a1cbdecc46707bef3683 100644 --- a/src/notifications/notification-frame.h +++ b/src/notifications/notification-frame.h @@ -18,7 +18,7 @@ G_BEGIN_DECLS #define PHOSH_TYPE_NOTIFICATION_FRAME (phosh_notification_frame_get_type ()) -G_DECLARE_FINAL_TYPE (PhoshNotificationFrame, phosh_notification_frame, PHOSH, NOTIFICATION_FRAME, GtkBox) +G_DECLARE_FINAL_TYPE (PhoshNotificationFrame, phosh_notification_frame, PHOSH, NOTIFICATION_FRAME, GtkEventBox) GtkWidget *phosh_notification_frame_new (gboolean show_body); @@ -27,4 +27,8 @@ void phosh_notification_frame_bind_notification (PhoshNotification void phosh_notification_frame_bind_model (PhoshNotificationFrame *self, GListModel *model); +gboolean phosh_notification_frame_get_reserve_size (PhoshNotificationFrame *self); +void phosh_notification_frame_set_reserve_size (PhoshNotificationFrame *self, + gboolean reserve_size); + G_END_DECLS diff --git a/src/phosh-wayland.c b/src/phosh-wayland.c index 506639124ac7a731564dbe9d163008335e4e96fb..ee8760e8d83fddcac0f27a64f024b6905fdbae31 100644 --- a/src/phosh-wayland.c +++ b/src/phosh-wayland.c @@ -40,6 +40,7 @@ struct _PhoshWayland { struct phosh_private *phosh_private; uint32_t phosh_private_version; struct zwp_virtual_keyboard_manager_v1 *zwp_virtual_keyboard_manager_v1; + struct wl_compositor *compositor; struct wl_display *display; struct wl_registry *registry; struct wl_seat *wl_seat; @@ -158,6 +159,12 @@ registry_handle_global (void *data, name, &zwp_virtual_keyboard_manager_v1_interface, 1); + } else if (!strcmp (interface, wl_compositor_interface.name)) { + self->compositor = wl_registry_bind ( + registry, + name, + &wl_compositor_interface, + MIN (version, 3)); } } @@ -266,18 +273,20 @@ phosh_wayland_constructed (GObject *object) num_outputs = g_hash_table_size(self->wl_outputs); if (!num_outputs || !self->layer_shell || !self->idle_manager || !self->input_inhibit_manager || !self->xdg_wm_base || - !self->zxdg_output_manager_v1) { + !self->zxdg_output_manager_v1 || !self->compositor) { g_error ("Could not find needed globals\n" "outputs: %d, layer_shell: %p, idle_manager: %p, " "inhibit: %p, xdg_wm: %p, " "xdg_output: %p, wlr_output_manager: %p, " - "wlr_foreign_toplevel_manager: %p" + "wlr_foreign_toplevel_manager: %p, " + "compositor: %p" "\n", num_outputs, self->layer_shell, self->idle_manager, self->input_inhibit_manager, self->xdg_wm_base, self->zxdg_output_manager_v1, self->zwlr_output_manager_v1, - self->zwlr_foreign_toplevel_manager_v1); + self->zwlr_foreign_toplevel_manager_v1, + self->compositor); } if (!self->phosh_private) { g_info ("Could not find phosh private interface, disabling some features"); @@ -365,6 +374,15 @@ phosh_wayland_get_gamma_control_manager (PhoshWayland *self) } +struct wl_compositor* +phosh_wayland_get_wl_compositor (PhoshWayland *self) +{ + g_return_val_if_fail (PHOSH_IS_WAYLAND (self), NULL); + + return self->compositor; +} + + struct wl_seat* phosh_wayland_get_wl_seat (PhoshWayland *self) { diff --git a/src/phosh-wayland.h b/src/phosh-wayland.h index c6c6401a759f7bb3e8512e8ab5ed2eeb6ebe12d2..4e643aa51f3cf7b8be2a535e388dbdfa4d226d03 100644 --- a/src/phosh-wayland.h +++ b/src/phosh-wayland.h @@ -60,6 +60,7 @@ struct gamma_control_manager *phosh_wayland_get_gamma_control_manager (P struct org_kde_kwin_idle *phosh_wayland_get_org_kde_kwin_idle (PhoshWayland *self); struct phosh_private *phosh_wayland_get_phosh_private (PhoshWayland *self); uint32_t phosh_wayland_get_phosh_private_version (PhoshWayland *self); +struct wl_compositor *phosh_wayland_get_wl_compositor (PhoshWayland *self); struct wl_seat *phosh_wayland_get_wl_seat (PhoshWayland *self); struct wl_shm *phosh_wayland_get_wl_shm (PhoshWayland *self); struct xdg_wm_base *phosh_wayland_get_xdg_wm_base (PhoshWayland *self); diff --git a/src/phosh.gresources.xml b/src/phosh.gresources.xml index 428fe760593d4cf49bbfe704585502f2920ff8a5..5ca4e19e5c61488b97f278d51a4096d4c8641819 100644 --- a/src/phosh.gresources.xml +++ b/src/phosh.gresources.xml @@ -11,6 +11,7 @@ ui/home.ui ui/lockscreen.ui ui/media-player.ui + ui/notification-banner.ui ui/notification-content.ui ui/notification-frame.ui ui/polkit-auth-prompt.ui diff --git a/src/stylesheet/common.css b/src/stylesheet/common.css index 576a7acc9ca95227865dad03e21a72cba361b2e1..eb6dab0685c89232a50fc12198c2647bb8a0fef7 100644 --- a/src/stylesheet/common.css +++ b/src/stylesheet/common.css @@ -258,7 +258,7 @@ phosh-lockscreen .phosh-notification-tray row { margin-right: 12px; } -phosh-lockscreen .phosh-notification-tray phosh-notification-frame { +phosh-lockscreen .phosh-notification-tray phosh-notification-frame .notification-container { background-color: @phosh_notification_bg_color; margin: 3px 0px 0px 0px; } @@ -393,13 +393,13 @@ phosh-notification-content:last-child .actions-area button:last-child { border-bottom: none; } -phosh-notification-frame { +phosh-notification-frame .notification-container { border-radius: 6px; background-color: @phosh_notification_bg_color; margin: 12px; } -phosh-notification-banner > phosh-notification-frame { +phosh-notification-banner .notification-container { box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.33), 0px 5px 6px 0px rgba(0, 0, 0, 0.07), 0px 0px 0px 1px rgba(0, 0, 0, 0.2); diff --git a/src/swipe-away-bin.c b/src/swipe-away-bin.c index dca4723c41c928f9b301c876a8733a382bc8a409..46ccf565fcc2812ae13e52a4514f1eea2423a52c 100644 --- a/src/swipe-away-bin.c +++ b/src/swipe-away-bin.c @@ -18,11 +18,28 @@ enum { }; static guint signals[N_SIGNALS] = { 0 }; + +enum { + PROP_0, + PROP_ALLOW_NEGATIVE, + PROP_RESERVE_SIZE, + PROP_ORIENTATION, + + LAST_PROP = PROP_ORIENTATION +}; +static GParamSpec *props[LAST_PROP]; + + struct _PhoshSwipeAwayBin { GtkEventBox parent_instance; + GtkOrientation orientation; + gboolean allow_negative; + gboolean reserve_size; + double progress; + int distance; HdySwipeTracker *tracker; PhoshAnimation *animation; }; @@ -30,6 +47,7 @@ struct _PhoshSwipeAwayBin static void phosh_swipe_away_bin_swipeable_init (HdySwipeableInterface *iface); G_DEFINE_TYPE_WITH_CODE (PhoshSwipeAwayBin, phosh_swipe_away_bin, GTK_TYPE_EVENT_BOX, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) G_IMPLEMENT_INTERFACE (HDY_TYPE_SWIPEABLE, phosh_swipe_away_bin_swipeable_init)) @@ -118,6 +136,19 @@ end_swipe_cb (PhoshSwipeAwayBin *self, } +static void +update_orientation (PhoshSwipeAwayBin *self) +{ + gboolean reversed = + self->orientation == GTK_ORIENTATION_HORIZONTAL && + gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL; + + hdy_swipe_tracker_set_reversed (self->tracker, reversed); + + gtk_widget_queue_allocate (GTK_WIDGET (self)); +} + + static void phosh_swipe_away_bin_finalize (GObject *object) { @@ -130,6 +161,67 @@ phosh_swipe_away_bin_finalize (GObject *object) } +static void +phosh_swipe_away_bin_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (object); + + switch (prop_id) { + case PROP_ALLOW_NEGATIVE: + g_value_set_boolean (value, phosh_swipe_away_bin_get_allow_negative (self)); + break; + + case PROP_RESERVE_SIZE: + g_value_set_boolean (value, phosh_swipe_away_bin_get_reserve_size (self)); + break; + + case PROP_ORIENTATION: + g_value_set_enum (value, self->orientation); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + +static void +phosh_swipe_away_bin_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (object); + + switch (prop_id) { + case PROP_ALLOW_NEGATIVE: + phosh_swipe_away_bin_set_allow_negative (self, g_value_get_boolean (value)); + break; + + case PROP_RESERVE_SIZE: + phosh_swipe_away_bin_set_reserve_size (self, g_value_get_boolean (value)); + break; + + case PROP_ORIENTATION: + { + GtkOrientation orientation = g_value_get_enum (value); + if (orientation != self->orientation) { + self->orientation = orientation; + update_orientation (self); + g_object_notify (G_OBJECT (self), "orientation"); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + static void phosh_swipe_away_bin_size_allocate (GtkWidget *widget, GtkAllocation *alloc) @@ -143,15 +235,94 @@ phosh_swipe_away_bin_size_allocate (GtkWidget *widget, if (!child || !gtk_widget_get_visible (child)) return; + child_alloc.y = alloc->y; child_alloc.x = alloc->x; - child_alloc.y = alloc->y - (int) (self->progress * alloc->height); child_alloc.width = alloc->width; child_alloc.height = alloc->height; + if (self->orientation == GTK_ORIENTATION_HORIZONTAL) { + if (self->reserve_size) { + self->distance = alloc->width / 3; + + child_alloc.width = self->distance; + child_alloc.x += self->distance; + } else { + self->distance = alloc->width; + } + + if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL) + child_alloc.x += (int) (self->progress * self->distance); + else + child_alloc.x -= (int) (self->progress * self->distance); + + } else { + if (self->reserve_size) { + self->distance = alloc->height / 3; + + child_alloc.height = self->distance; + child_alloc.y += self->distance; + } else { + self->distance = alloc->height; + } + + child_alloc.y -= (int) (self->progress * self->distance); + } + + gtk_widget_size_allocate (child, &child_alloc); } +static void +phosh_swipe_away_bin_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (widget); + + GTK_WIDGET_CLASS (phosh_swipe_away_bin_parent_class)->get_preferred_width (widget, minimum, natural); + + if (self->reserve_size && + self->orientation == GTK_ORIENTATION_HORIZONTAL) { + if (minimum) + *minimum *= 3; + + if (natural) + *natural *= 3; + } +} + + +static void +phosh_swipe_away_bin_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (widget); + + GTK_WIDGET_CLASS (phosh_swipe_away_bin_parent_class)->get_preferred_height (widget, minimum, natural); + + if (self->reserve_size && + self->orientation == GTK_ORIENTATION_VERTICAL) { + if (minimum) + *minimum *= 3; + + if (natural) + *natural *= 3; + } +} + + +static void +phosh_swipe_away_bin_direction_changed (GtkWidget *widget, + GtkTextDirection previous_direction) +{ + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (widget); + + update_orientation (self); +} + + static void phosh_swipe_away_bin_class_init (PhoshSwipeAwayBinClass *klass) { @@ -159,7 +330,32 @@ phosh_swipe_away_bin_class_init (PhoshSwipeAwayBinClass *klass) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->finalize = phosh_swipe_away_bin_finalize; + object_class->get_property = phosh_swipe_away_bin_get_property; + object_class->set_property = phosh_swipe_away_bin_set_property; widget_class->size_allocate = phosh_swipe_away_bin_size_allocate; + widget_class->get_preferred_width = phosh_swipe_away_bin_get_preferred_width; + widget_class->get_preferred_height = phosh_swipe_away_bin_get_preferred_height; + widget_class->direction_changed = phosh_swipe_away_bin_direction_changed; + + props[PROP_ALLOW_NEGATIVE] = + g_param_spec_boolean ("allow-negative", + "Allow Negative", + "Use [-1:1] progress range instead of [0:1]", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + props[PROP_RESERVE_SIZE] = + g_param_spec_boolean ("reserve-size", + "Reserve Size", + "Allocate larger size than the child so that the child is never clipped when animating", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY); + + g_object_class_install_properties (object_class, LAST_PROP, props); + + g_object_class_override_property (object_class, + PROP_ORIENTATION, + "orientation"); signals[REMOVED] = g_signal_new ("removed", @@ -179,9 +375,11 @@ phosh_swipe_away_bin_init (PhoshSwipeAwayBin *self) { self->tracker = hdy_swipe_tracker_new (HDY_SWIPEABLE (self)); - gtk_orientable_set_orientation (GTK_ORIENTABLE (self->tracker), - GTK_ORIENTATION_VERTICAL); + g_object_bind_property (self, "orientation", + self->tracker, "orientation", + G_BINDING_SYNC_CREATE); hdy_swipe_tracker_set_allow_mouse_drag (self->tracker, TRUE); + update_orientation (self); g_signal_connect_object (self->tracker, "begin-swipe", G_CALLBACK (begin_swipe_cb), self, @@ -207,7 +405,9 @@ phosh_swipe_away_bin_get_swipe_tracker (HdySwipeable *swipeable) static double phosh_swipe_away_bin_get_distance (HdySwipeable *swipeable) { - return (double) gtk_widget_get_allocated_height (GTK_WIDGET (swipeable)); + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (swipeable); + + return self->distance; } @@ -231,12 +431,21 @@ static double * phosh_swipe_away_bin_get_snap_points (HdySwipeable *swipeable, int *n_snap_points) { - double *points = g_new0 (double, 2); + PhoshSwipeAwayBin *self = PHOSH_SWIPE_AWAY_BIN (swipeable); + int n = self->allow_negative ? 3 : 2; + double *points; - points[1] = 1; + points = g_new0 (double, n); + + if (self->allow_negative) { + points[0] = -1; + points[2] = 1; + } else { + points[1] = 1; + } if (n_snap_points) - *n_snap_points = 2; + *n_snap_points = n; return points; } @@ -262,6 +471,84 @@ phosh_swipe_away_bin_swipeable_init (HdySwipeableInterface *iface) } +gboolean +phosh_swipe_away_bin_get_allow_negative (PhoshSwipeAwayBin *self) +{ + g_return_val_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self), FALSE); + + return self->allow_negative; +} + + +void +phosh_swipe_away_bin_set_allow_negative (PhoshSwipeAwayBin *self, + gboolean allow_negative) +{ + g_return_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self)); + + allow_negative = !!allow_negative; + + if (allow_negative == self->allow_negative) + return; + + self->allow_negative = allow_negative; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ALLOW_NEGATIVE]); +} + + +gboolean +phosh_swipe_away_bin_get_reserve_size (PhoshSwipeAwayBin *self) +{ + g_return_val_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self), FALSE); + + return self->reserve_size; +} + + +void +phosh_swipe_away_bin_set_reserve_size (PhoshSwipeAwayBin *self, + gboolean reserve_size) +{ + g_return_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self)); + + reserve_size = !!reserve_size; + + if (reserve_size == self->reserve_size) + return; + + self->reserve_size = reserve_size; + + gtk_widget_queue_resize (GTK_WIDGET (self)); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_RESERVE_SIZE]); +} + + +void +phosh_swipe_away_bin_hide (PhoshSwipeAwayBin *self) +{ + g_return_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self)); + + if (self->animation) + phosh_animation_stop (self->animation); + + set_progress (self, 1); +} + + +void +phosh_swipe_away_bin_reveal (PhoshSwipeAwayBin *self) +{ + g_return_if_fail (PHOSH_IS_SWIPE_AWAY_BIN (self)); + + if (self->animation) + phosh_animation_stop (self->animation); + + animate (self, 200, 0, PHOSH_ANIMATION_TYPE_EASE_OUT_CUBIC); +} + + void phosh_swipe_away_bin_remove (PhoshSwipeAwayBin *self) { diff --git a/src/swipe-away-bin.h b/src/swipe-away-bin.h index 67930046e3b796ef1ce34ac92a2f6bd4633bd17b..73ab3e96e4aff30b4b430e40d8313b77cc2a65ec 100644 --- a/src/swipe-away-bin.h +++ b/src/swipe-away-bin.h @@ -13,5 +13,15 @@ G_DECLARE_FINAL_TYPE (PhoshSwipeAwayBin, phosh_swipe_away_bin, PHOSH, SWIPE_AWAY_BIN, GtkEventBox) +gboolean phosh_swipe_away_bin_get_allow_negative (PhoshSwipeAwayBin *self); +void phosh_swipe_away_bin_set_allow_negative (PhoshSwipeAwayBin *self, + gboolean allow_negative); + +gboolean phosh_swipe_away_bin_get_reserve_size (PhoshSwipeAwayBin *self); +void phosh_swipe_away_bin_set_reserve_size (PhoshSwipeAwayBin *self, + gboolean reserve_size); + +void phosh_swipe_away_bin_hide (PhoshSwipeAwayBin *self); +void phosh_swipe_away_bin_reveal (PhoshSwipeAwayBin *self); void phosh_swipe_away_bin_remove (PhoshSwipeAwayBin *self); void phosh_swipe_away_bin_undo (PhoshSwipeAwayBin *self); diff --git a/src/top-panel.c b/src/top-panel.c index 739aff96ccf3fd234b20397015812cd5839c312c..92c915b24f26e9423decf195a045bf5d8a10ac8c 100644 --- a/src/top-panel.c +++ b/src/top-panel.c @@ -63,6 +63,8 @@ typedef struct _PhoshTopPanel { GdkSeat *seat; GSimpleActionGroup *actions; + + GtkGesture *click_gesture; /* needed so that the gesture isn't destroyed immediately */ } PhoshTopPanel; G_DEFINE_TYPE (PhoshTopPanel, phosh_top_panel, PHOSH_TYPE_LAYER_SURFACE) @@ -244,10 +246,10 @@ on_key_press_event (PhoshTopPanel *self, GdkEventKey *event, gpointer data) } -static gboolean -on_button_press_event (PhoshTopPanel *self, GdkEventButton *event, gpointer data) +static void +released_cb (PhoshTopPanel *self) { - phosh_trigger_feedback ("button-pressed"); + phosh_trigger_feedback ("button-released"); /* * The popover has to be popdown manually as it doesn't happen @@ -258,7 +260,6 @@ on_button_press_event (PhoshTopPanel *self, GdkEventButton *event, gpointer data else phosh_top_panel_fold (self); - return GDK_EVENT_PROPAGATE; } @@ -321,15 +322,11 @@ phosh_top_panel_constructed (GObject *object) } /* Settings menu and it's top-bar / menu */ - gtk_widget_add_events (GTK_WIDGET (self), GDK_KEY_PRESS_MASK); + gtk_widget_add_events (GTK_WIDGET (self), GDK_ALL_EVENTS_MASK); g_signal_connect (G_OBJECT (self), "key-press-event", G_CALLBACK (on_key_press_event), NULL); - g_signal_connect (G_OBJECT (self), - "button-press-event", - G_CALLBACK (on_button_press_event), - NULL); g_signal_connect_swapped (self->settings, "setting-done", G_CALLBACK (phosh_top_panel_fold), @@ -398,6 +395,8 @@ phosh_top_panel_class_init (PhoshTopPanelClass *klass) gtk_widget_class_bind_template_child (widget_class, PhoshTopPanel, box); gtk_widget_class_bind_template_child (widget_class, PhoshTopPanel, stack); gtk_widget_class_bind_template_child (widget_class, PhoshTopPanel, settings); + gtk_widget_class_bind_template_child (widget_class, PhoshTopPanel, click_gesture); + gtk_widget_class_bind_template_callback (widget_class, released_cb); } diff --git a/src/ui/activity.ui b/src/ui/activity.ui index 30d22abd4909f36992d2c9684615c1973f213858..98675d47496c043ad0657495a24f19a29f678208 100644 --- a/src/ui/activity.ui +++ b/src/ui/activity.ui @@ -9,6 +9,7 @@ True + vertical diff --git a/src/ui/notification-banner.ui b/src/ui/notification-banner.ui new file mode 100644 index 0000000000000000000000000000000000000000..9f2445d35ba21824813fa65f9bcf4e08c8dcb426 --- /dev/null +++ b/src/ui/notification-banner.ui @@ -0,0 +1,18 @@ + + + + + diff --git a/src/ui/notification-frame.ui b/src/ui/notification-frame.ui index 1ac860a8a1b143fe296ff8e8396f3fff760e809b..c196ed608ac74f757e20a3231784d109f3c053d1 100644 --- a/src/ui/notification-frame.ui +++ b/src/ui/notification-frame.ui @@ -1,83 +1,110 @@ - + + PhoshTopPanel + + diff --git a/tests/test-notification-frame.c b/tests/test-notification-frame.c index 8e3267e7cdb2178852897195d0ed794bb1d76516..d7afe135b28858c7b6938e5e7569a72bca6280df 100644 --- a/tests/test-notification-frame.c +++ b/tests/test-notification-frame.c @@ -50,40 +50,6 @@ test_phosh_notification_frame_new (void) } -static void -test_phosh_notification_frame_header_activated (void) -{ - g_autoptr (PhoshNotification) noti = NULL; - GtkWidget *frame = NULL; - g_autoptr (GDateTime) now = g_date_time_new_now_local (); - - noti = phosh_notification_new (0, - NULL, - NULL, - "Hey", - "Testing", - NULL, - NULL, - PHOSH_NOTIFICATION_URGENCY_NORMAL, - NULL, - FALSE, - FALSE, - NULL, - now); - - frame = phosh_notification_frame_new (TRUE); - phosh_notification_frame_bind_notification (PHOSH_NOTIFICATION_FRAME (frame), - noti); - g_signal_connect (noti, "actioned", G_CALLBACK (actioned), NULL); - - actioned_called = FALSE; - - header_activated (PHOSH_NOTIFICATION_FRAME (frame), - (GdkEventButton *) gdk_event_new (GDK_BUTTON_PRESS)); - g_assert_true (actioned_called); -} - - static void test_phosh_notification_frame_notification_activated (void) { @@ -130,7 +96,6 @@ main (int argc, char **argv) gtk_test_init (&argc, &argv, NULL); g_test_add_func ("/phosh/notification-frame/new", test_phosh_notification_frame_new); - g_test_add_func ("/phosh/notification-frame/header-activated", test_phosh_notification_frame_header_activated); g_test_add_func ("/phosh/notification-frame/notification-activated", test_phosh_notification_frame_notification_activated); return g_test_run ();