mirror of
https://github.com/elementary/gala.git
synced 2024-08-16 18:40:27 +03:00
Optionally support native notification windows (#552)
This commit is contained in:
parent
cdd3bdcfb0
commit
a127d09a22
@ -57,6 +57,11 @@
|
||||
<summary>Enable edge tiling when dropping windows on screen edges</summary>
|
||||
<description>If enabled, dropping windows on vertical screen edges maximizes them vertically and resizes them horizontally to cover half of the available area. Dropping windows on the top screen edge maximizes them completely.</description>
|
||||
</key>
|
||||
<key type="b" name="use-new-notifications">
|
||||
<default>false</default>
|
||||
<summary>If new notifications should be used</summary>
|
||||
<description>If new notifications UI should be used, requires io.elemenetary.notifications to be present and running</description>
|
||||
</key>
|
||||
<key type="s" name="panel-main-menu-action">
|
||||
<default>''</default>
|
||||
<summary>Panel main menu action</summary>
|
||||
|
@ -22,13 +22,18 @@ namespace Gala.Plugins.Notify
|
||||
{
|
||||
public class Main : Gala.Plugin
|
||||
{
|
||||
private GLib.Settings behavior_settings;
|
||||
Gala.WindowManager? wm = null;
|
||||
|
||||
NotifyServer server;
|
||||
NotificationStack stack;
|
||||
|
||||
uint owner_id = 0U;
|
||||
|
||||
public override void initialize (Gala.WindowManager wm)
|
||||
{
|
||||
behavior_settings = new GLib.Settings ("org.pantheon.desktop.gala.behavior");
|
||||
|
||||
this.wm = wm;
|
||||
#if HAS_MUTTER330
|
||||
unowned Meta.Display display = wm.get_display ();
|
||||
@ -41,26 +46,40 @@ namespace Gala.Plugins.Notify
|
||||
#else
|
||||
stack = new NotificationStack (screen);
|
||||
#endif
|
||||
wm.ui_group.add_child (stack);
|
||||
track_actor (stack);
|
||||
|
||||
stack.animations_changed.connect ((running) => {
|
||||
freeze_track = running;
|
||||
});
|
||||
|
||||
server = new NotifyServer (stack);
|
||||
|
||||
update_position ();
|
||||
#if HAS_MUTTER330
|
||||
unowned Meta.MonitorManager monitor_manager = Meta.MonitorManager.@get ();
|
||||
monitor_manager.monitors_changed.connect (update_position);
|
||||
display.workareas_changed.connect (update_position);
|
||||
#else
|
||||
screen.monitors_changed.connect (update_position);
|
||||
screen.workareas_changed.connect (update_position);
|
||||
#endif
|
||||
|
||||
Bus.own_name (BusType.SESSION, "org.freedesktop.Notifications", BusNameOwnerFlags.NONE,
|
||||
server = new NotifyServer (stack);
|
||||
|
||||
if (!behavior_settings.get_boolean ("use-new-notifications")) {
|
||||
enable ();
|
||||
}
|
||||
|
||||
behavior_settings.changed["use-new-notifications"].connect (() => {
|
||||
if (!behavior_settings.get_boolean ("use-new-notifications")) {
|
||||
enable ();
|
||||
} else {
|
||||
disable ();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void enable ()
|
||||
{
|
||||
if (owner_id != 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
wm.ui_group.add_child (stack);
|
||||
track_actor (stack);
|
||||
|
||||
update_position ();
|
||||
|
||||
owner_id = Bus.own_name (BusType.SESSION, "org.freedesktop.Notifications", BusNameOwnerFlags.REPLACE,
|
||||
(connection) => {
|
||||
try {
|
||||
connection.register_object ("/org/freedesktop/Notifications", server);
|
||||
@ -76,6 +95,20 @@ namespace Gala.Plugins.Notify
|
||||
});
|
||||
}
|
||||
|
||||
void disable ()
|
||||
{
|
||||
if (owner_id == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bus.unown_name (owner_id);
|
||||
|
||||
untrack_actor (stack);
|
||||
wm.ui_group.remove_child (stack);
|
||||
|
||||
owner_id = 0U;
|
||||
}
|
||||
|
||||
void update_position ()
|
||||
{
|
||||
#if HAS_MUTTER330
|
||||
|
157
src/NotificationStack.vala
Normal file
157
src/NotificationStack.vala
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2020 elementary, Inc (https://elementary.io)
|
||||
* 2014 Tom Beckmann
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
public class Gala.NotificationStack : Object {
|
||||
// we need to keep a small offset to the top, because we clip the container to
|
||||
// its allocations and the close button would be off for the first notification
|
||||
private const int TOP_OFFSET = 2;
|
||||
private const int ADDITIONAL_MARGIN = 12;
|
||||
private const int MARGIN = 12;
|
||||
|
||||
private const int WIDTH = 300;
|
||||
|
||||
private int stack_x;
|
||||
private int stack_y;
|
||||
private int stack_width;
|
||||
|
||||
public Meta.Screen screen { get; construct; }
|
||||
|
||||
private Gee.ArrayList<unowned Meta.WindowActor> notifications;
|
||||
|
||||
public NotificationStack (Meta.Screen screen) {
|
||||
Object (screen: screen);
|
||||
}
|
||||
|
||||
construct {
|
||||
notifications = new Gee.ArrayList<Meta.WindowActor> ();
|
||||
|
||||
screen.monitors_changed.connect (update_stack_allocation);
|
||||
screen.workareas_changed.connect (update_stack_allocation);
|
||||
update_stack_allocation ();
|
||||
}
|
||||
|
||||
public void show_notification (Meta.WindowActor notification) {
|
||||
notification.set_pivot_point (0.5f, 0.5f);
|
||||
|
||||
unowned Meta.Window window = notification.get_meta_window ();
|
||||
window.stick ();
|
||||
|
||||
var scale = Utils.get_ui_scaling_factor ();
|
||||
|
||||
var entry = new Clutter.TransitionGroup ();
|
||||
entry.remove_on_complete = true;
|
||||
entry.duration = 400;
|
||||
|
||||
var opacity_transition = new Clutter.PropertyTransition ("opacity");
|
||||
opacity_transition.set_from_value (0);
|
||||
opacity_transition.set_to_value (255);
|
||||
|
||||
var flip_transition = new Clutter.KeyframeTransition ("rotation-angle-x");
|
||||
flip_transition.set_from_value (90.0);
|
||||
flip_transition.set_to_value (0.0);
|
||||
flip_transition.set_key_frames ({ 0.6 });
|
||||
flip_transition.set_values ({ -10.0 });
|
||||
|
||||
entry.add_transition (opacity_transition);
|
||||
entry.add_transition (flip_transition);
|
||||
notification.add_transition ("entry", entry);
|
||||
|
||||
/**
|
||||
* We will make space for the incomming notification
|
||||
* by shifting all current notifications by height
|
||||
* and then add it to the notifications list.
|
||||
*/
|
||||
update_positions (notification.height);
|
||||
|
||||
move_window (notification, stack_x, stack_y + TOP_OFFSET + ADDITIONAL_MARGIN * scale);
|
||||
notifications.insert (0, notification);
|
||||
}
|
||||
|
||||
private void update_stack_allocation () {
|
||||
var primary = screen.get_primary_monitor ();
|
||||
var area = screen.get_active_workspace ().get_work_area_for_monitor (primary);
|
||||
|
||||
var scale = Utils.get_ui_scaling_factor ();
|
||||
stack_width = (WIDTH + MARGIN) * scale;
|
||||
|
||||
stack_x = area.x + area.width - stack_width;
|
||||
stack_y = area.y;
|
||||
}
|
||||
|
||||
private void update_positions (float add_y = 0.0f) {
|
||||
var scale = Utils.get_ui_scaling_factor ();
|
||||
var y = stack_y + TOP_OFFSET + add_y + ADDITIONAL_MARGIN * scale;
|
||||
var i = notifications.size;
|
||||
var delay_step = i > 0 ? 150 / i : 0;
|
||||
foreach (var actor in notifications) {
|
||||
actor.save_easing_state ();
|
||||
actor.set_easing_mode (Clutter.AnimationMode.EASE_OUT_BACK);
|
||||
actor.set_easing_duration (200);
|
||||
actor.set_easing_delay ((i--) * delay_step);
|
||||
|
||||
move_window (actor, -1, (int)y);
|
||||
actor.restore_easing_state ();
|
||||
|
||||
y += actor.height;
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy_notification (Meta.WindowActor notification) {
|
||||
notification.save_easing_state ();
|
||||
notification.set_easing_duration (100);
|
||||
notification.set_easing_mode (Clutter.AnimationMode.EASE_IN_QUAD);
|
||||
notification.opacity = 0;
|
||||
|
||||
notification.x += stack_width;
|
||||
notification.restore_easing_state ();
|
||||
|
||||
notifications.remove (notification);
|
||||
update_positions ();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes care of properly updating both the actor
|
||||
* position and the actual window position.
|
||||
*
|
||||
* To enable animations for a window we first need to move it's frame
|
||||
* in the compositor and then calculate & apply the coordinates for the window
|
||||
* actor.
|
||||
*/
|
||||
private static void move_window (Meta.WindowActor actor, int x, int y) {
|
||||
if (actor.is_destroyed ()) {
|
||||
return;
|
||||
}
|
||||
|
||||
unowned Meta.Window window = actor.get_meta_window ();
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var rect = window.get_frame_rect ();
|
||||
|
||||
window.move_frame (false, x != -1 ? x : rect.x, y != -1 ? y : rect.y);
|
||||
|
||||
/**
|
||||
* move_frame does not guarantee that the frame rectangle
|
||||
* will be updated instantly, get the buffer rectangle.
|
||||
*/
|
||||
rect = window.get_buffer_rect ();
|
||||
actor.x = rect.x - ((actor.width - rect.width) / 2);
|
||||
actor.y = rect.y - ((actor.height - rect.height) / 2);
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ namespace Gala {
|
||||
public class BehaviorSettings : Granite.Services.Settings {
|
||||
public bool dynamic_workspaces { get; set; }
|
||||
public bool edge_tiling { get; set; }
|
||||
public bool use_new_notifications { get; set; }
|
||||
public string panel_main_menu_action { get; set; }
|
||||
public string toggle_recording_action { get; set; }
|
||||
public string overlay_action { get; set; }
|
||||
|
@ -70,6 +70,8 @@ namespace Gala {
|
||||
private Meta.Window? moving; //place for the window that is being moved over
|
||||
|
||||
Daemon? daemon_proxy = null;
|
||||
|
||||
NotificationStack notification_stack;
|
||||
|
||||
Gee.LinkedList<ModalProxy> modal_stack = new Gee.LinkedList<ModalProxy> ();
|
||||
|
||||
@ -159,6 +161,8 @@ namespace Gala {
|
||||
#endif
|
||||
KeyboardManager.init (display);
|
||||
|
||||
notification_stack = new NotificationStack (screen);
|
||||
|
||||
// Due to a bug which enables access to the stage when using multiple monitors
|
||||
// in the screensaver, we have to listen for changes and make sure the input area
|
||||
// is set to NONE when we are in locked mode
|
||||
@ -986,7 +990,7 @@ namespace Gala {
|
||||
public override void show_window_menu (Meta.Window window, Meta.WindowMenuType menu, int x, int y) {
|
||||
switch (menu) {
|
||||
case Meta.WindowMenuType.WM:
|
||||
if (daemon_proxy == null) {
|
||||
if (daemon_proxy == null || window.get_window_type () == Meta.WindowType.NOTIFICATION) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1527,6 +1531,13 @@ namespace Gala {
|
||||
window.is_attached_dialog ())
|
||||
dim_window (window.find_root_ancestor (), true);
|
||||
|
||||
break;
|
||||
case Meta.WindowType.NOTIFICATION:
|
||||
if (BehaviorSettings.get_default ().use_new_notifications) {
|
||||
notification_stack.show_notification (actor);
|
||||
map_completed (actor);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
map_completed (actor);
|
||||
@ -1618,6 +1629,17 @@ namespace Gala {
|
||||
actor.opacity = 0U;
|
||||
actor.restore_easing_state ();
|
||||
|
||||
ulong destroy_handler_id = 0UL;
|
||||
destroy_handler_id = actor.transitions_completed.connect (() => {
|
||||
actor.disconnect (destroy_handler_id);
|
||||
destroying.remove (actor);
|
||||
destroy_completed (actor);
|
||||
});
|
||||
break;
|
||||
case Meta.WindowType.NOTIFICATION:
|
||||
destroying.add (actor);
|
||||
notification_stack.destroy_notification (actor);
|
||||
|
||||
ulong destroy_handler_id = 0UL;
|
||||
destroy_handler_id = actor.transitions_completed.connect (() => {
|
||||
actor.disconnect (destroy_handler_id);
|
||||
@ -1930,11 +1952,13 @@ namespace Gala {
|
||||
parents.prepend (actor.get_parent ());
|
||||
clutter_actor_reparent (actor, static_windows);
|
||||
|
||||
actor.set_translation (-clone_offset_x, -clone_offset_y, 0);
|
||||
actor.save_easing_state ();
|
||||
actor.set_easing_duration (300);
|
||||
actor.opacity = 0;
|
||||
actor.restore_easing_state ();
|
||||
if (window.window_type != Meta.WindowType.NOTIFICATION) {
|
||||
actor.set_translation (-clone_offset_x, -clone_offset_y, 0);
|
||||
actor.save_easing_state ();
|
||||
actor.set_easing_duration (300);
|
||||
actor.opacity = 0;
|
||||
actor.restore_easing_state ();
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
|
@ -5,6 +5,7 @@ gala_bin_sources = files(
|
||||
'DragDropAction.vala',
|
||||
'InternalUtils.vala',
|
||||
'KeyboardManager.vala',
|
||||
'NotificationStack.vala',
|
||||
'Main.vala',
|
||||
'MediaFeedback.vala',
|
||||
'PluginManager.vala',
|
||||
|
Loading…
Reference in New Issue
Block a user