mirror of
https://github.com/ErikReider/SwayNotificationCenter.git
synced 2024-10-05 21:08:40 +03:00
Add inline replies to notifications (#221)
This commit is contained in:
parent
4aefd3aabd
commit
ba4a2665fa
@ -56,6 +56,7 @@ Post your setup here: [Config flex 💪](https://github.com/ErikReider/SwayNotif
|
|||||||
|
|
||||||
- Keyboard shortcuts
|
- Keyboard shortcuts
|
||||||
- Notification body markup with image support
|
- Notification body markup with image support
|
||||||
|
- Inline replies
|
||||||
- A panel to view previous notifications
|
- A panel to view previous notifications
|
||||||
- Show album art for notifications like Spotify
|
- Show album art for notifications like Spotify
|
||||||
- Do not disturb
|
- Do not disturb
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"control-center-margin-right": 0,
|
"control-center-margin-right": 0,
|
||||||
"control-center-margin-left": 0,
|
"control-center-margin-left": 0,
|
||||||
"notification-2fa-action": true,
|
"notification-2fa-action": true,
|
||||||
|
"notification-inline-replies": false,
|
||||||
"notification-icon-size": 64,
|
"notification-icon-size": 64,
|
||||||
"notification-body-image-height": 100,
|
"notification-body-image-height": 100,
|
||||||
"notification-body-image-width": 200,
|
"notification-body-image-width": 200,
|
||||||
|
@ -555,6 +555,12 @@ namespace SwayNotificationCenter {
|
|||||||
*/
|
*/
|
||||||
public bool notification_2fa_action { get; set; default = true; }
|
public bool notification_2fa_action { get; set; default = true; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If notifications should display a text field to reply if the
|
||||||
|
* sender requests it.
|
||||||
|
*/
|
||||||
|
public bool notification_inline_replies { get; set; default = false; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification icon size, in pixels.
|
* Notification icon size, in pixels.
|
||||||
*/
|
*/
|
||||||
|
@ -80,6 +80,11 @@
|
|||||||
"description": "If each notification should display a 'COPY \"1234\"' action",
|
"description": "If each notification should display a 'COPY \"1234\"' action",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"notification-inline-replies": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If notifications should display a text field to reply if the sender requests it. NOTE: Replying in popup notifications is only available if the compositor supports GTK Layer-Shell ON_DEMAND keyboard interactivity.",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"notification-icon-size": {
|
"notification-icon-size": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "The notification icon size (in pixels)",
|
"description": "The notification icon size (in pixels)",
|
||||||
|
@ -133,6 +133,14 @@ namespace SwayNotificationCenter {
|
|||||||
// sometimes being passed through to unfucused application
|
// sometimes being passed through to unfucused application
|
||||||
// Ex: Firefox in a fullscreen YouTube video
|
// Ex: Firefox in a fullscreen YouTube video
|
||||||
this.key_release_event.connect ((w, event_key) => {
|
this.key_release_event.connect ((w, event_key) => {
|
||||||
|
if (this.get_focus () is Gtk.Entry) {
|
||||||
|
switch (Gdk.keyval_name (event_key.keyval)) {
|
||||||
|
case "Escape":
|
||||||
|
this.set_focus (null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (event_key.type == Gdk.EventType.KEY_RELEASE) {
|
if (event_key.type == Gdk.EventType.KEY_RELEASE) {
|
||||||
switch (Gdk.keyval_name (event_key.keyval)) {
|
switch (Gdk.keyval_name (event_key.keyval)) {
|
||||||
case "Escape":
|
case "Escape":
|
||||||
@ -145,6 +153,7 @@ namespace SwayNotificationCenter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.key_press_event.connect ((w, event_key) => {
|
this.key_press_event.connect ((w, event_key) => {
|
||||||
|
if (this.get_focus () is Gtk.Entry) return false;
|
||||||
if (event_key.type == Gdk.EventType.KEY_PRESS) {
|
if (event_key.type == Gdk.EventType.KEY_PRESS) {
|
||||||
var children = list_box.get_children ();
|
var children = list_box.get_children ();
|
||||||
Notification noti = (Notification)
|
Notification noti = (Notification)
|
||||||
@ -205,7 +214,7 @@ namespace SwayNotificationCenter {
|
|||||||
}
|
}
|
||||||
navigate_list (list_position);
|
navigate_list (list_position);
|
||||||
}
|
}
|
||||||
return true;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Switches the stack page depending on the
|
// Switches the stack page depending on the
|
||||||
@ -464,7 +473,9 @@ namespace SwayNotificationCenter {
|
|||||||
|
|
||||||
public void add_notification (NotifyParams param,
|
public void add_notification (NotifyParams param,
|
||||||
NotiDaemon noti_daemon) {
|
NotiDaemon noti_daemon) {
|
||||||
var noti = new Notification.regular (param, noti_daemon);
|
var noti = new Notification.regular (param,
|
||||||
|
noti_daemon,
|
||||||
|
NotificationType.CONTROL_CENTER);
|
||||||
noti.grab_focus.connect ((w) => {
|
noti.grab_focus.connect ((w) => {
|
||||||
uint i = list_box.get_children ().index (w);
|
uint i = list_box.get_children ().index (w);
|
||||||
if (list_position != uint.MAX && list_position != i) {
|
if (list_position != uint.MAX && list_position != i) {
|
||||||
|
@ -3,6 +3,8 @@ namespace SwayNotificationCenter {
|
|||||||
static string ? style_path;
|
static string ? style_path;
|
||||||
static string ? config_path;
|
static string ? config_path;
|
||||||
|
|
||||||
|
static uint layer_shell_protocol_version = 3;
|
||||||
|
|
||||||
static Settings self_settings;
|
static Settings self_settings;
|
||||||
|
|
||||||
public void main (string[] args) {
|
public void main (string[] args) {
|
||||||
@ -40,6 +42,10 @@ namespace SwayNotificationCenter {
|
|||||||
ConfigModel.init (config_path);
|
ConfigModel.init (config_path);
|
||||||
Functions.load_css (style_path);
|
Functions.load_css (style_path);
|
||||||
|
|
||||||
|
if (ConfigModel.instance.layer_shell) {
|
||||||
|
layer_shell_protocol_version = GtkLayerShell.get_protocol_version ();
|
||||||
|
}
|
||||||
|
|
||||||
swaync_daemon = new SwayncDaemon ();
|
swaync_daemon = new SwayncDaemon ();
|
||||||
Bus.own_name (BusType.SESSION, "org.erikreider.swaync.cc",
|
Bus.own_name (BusType.SESSION, "org.erikreider.swaync.cc",
|
||||||
BusNameOwnerFlags.NONE,
|
BusNameOwnerFlags.NONE,
|
||||||
|
@ -113,6 +113,7 @@ namespace SwayNotificationCenter {
|
|||||||
"synchronous",
|
"synchronous",
|
||||||
"private-synchronous",
|
"private-synchronous",
|
||||||
"x-canonical-private-synchronous",
|
"x-canonical-private-synchronous",
|
||||||
|
"inline-reply",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,5 +340,8 @@ namespace SwayNotificationCenter {
|
|||||||
* support the concept of being able to "invoke" a notification.
|
* support the concept of being able to "invoke" a notification.
|
||||||
*/
|
*/
|
||||||
public signal void ActionInvoked (uint32 id, string action_key);
|
public signal void ActionInvoked (uint32 id, string action_key);
|
||||||
|
|
||||||
|
/** To be used by the non-standard "inline-reply" capability */
|
||||||
|
public signal void NotificationReplied (uint32 id, string text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +102,10 @@ namespace SwayNotificationCenter {
|
|||||||
private int priv_value { private get; private set; }
|
private int priv_value { private get; private set; }
|
||||||
public bool has_synch { public get; private set; }
|
public bool has_synch { public get; private set; }
|
||||||
|
|
||||||
|
/** Inline-replies */
|
||||||
|
public Action ? inline_reply { get; set; }
|
||||||
|
public string ? inline_reply_placeholder { get; set; }
|
||||||
|
|
||||||
// Custom hints
|
// Custom hints
|
||||||
/** Disables scripting for notification */
|
/** Disables scripting for notification */
|
||||||
public bool swaync_no_script { get; set; }
|
public bool swaync_no_script { get; set; }
|
||||||
@ -133,30 +137,12 @@ namespace SwayNotificationCenter {
|
|||||||
this.replaces = false;
|
this.replaces = false;
|
||||||
this.has_synch = false;
|
this.has_synch = false;
|
||||||
|
|
||||||
s_hints ();
|
parse_hints ();
|
||||||
|
|
||||||
Array<Action> ac_array = new Array<Action> ();
|
parse_actions (actions);
|
||||||
if (actions.length > 1 && actions.length % 2 == 0) {
|
|
||||||
for (int i = 0; i < actions.length; i++) {
|
|
||||||
var action = new Action ();
|
|
||||||
action.identifier = actions[i];
|
|
||||||
action.name = actions[i + 1];
|
|
||||||
if (action.name != null && action.identifier != null
|
|
||||||
&& action.name != "" && action.identifier != "") {
|
|
||||||
|
|
||||||
if (action.identifier.down () == "default") {
|
|
||||||
default_action = action;
|
|
||||||
} else {
|
|
||||||
ac_array.append_val (action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.actions = ac_array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void s_hints () {
|
private void parse_hints () {
|
||||||
foreach (var hint in hints.get_keys ()) {
|
foreach (var hint in hints.get_keys ()) {
|
||||||
Variant hint_value = hints[hint];
|
Variant hint_value = hints[hint];
|
||||||
switch (hint) {
|
switch (hint) {
|
||||||
@ -239,12 +225,46 @@ namespace SwayNotificationCenter {
|
|||||||
urgency = UrgencyLevels.from_value (hint_value.get_byte ());
|
urgency = UrgencyLevels.from_value (hint_value.get_byte ());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "x-kde-reply-placeholder-text":
|
||||||
|
if (hint_value.is_of_type (VariantType.STRING)) {
|
||||||
|
inline_reply_placeholder = hint_value.get_string ();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parse_actions (string[] actions) {
|
||||||
|
Array<Action> parsed_actions = new Array<Action> ();
|
||||||
|
if (actions.length > 1 && actions.length % 2 == 0) {
|
||||||
|
for (int i = 0; i < actions.length; i++) {
|
||||||
|
var action = new Action ();
|
||||||
|
action.identifier = actions[i];
|
||||||
|
action.name = actions[i + 1];
|
||||||
|
if (action.name != null && action.identifier != null
|
||||||
|
&& action.name != "" && action.identifier != "") {
|
||||||
|
|
||||||
|
string id = action.identifier.down ();
|
||||||
|
switch (id) {
|
||||||
|
case "default":
|
||||||
|
default_action = action;
|
||||||
|
break;
|
||||||
|
case "inline-reply":
|
||||||
|
inline_reply = action;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parsed_actions.append_val (action);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.actions = parsed_actions;
|
||||||
|
}
|
||||||
|
|
||||||
public string to_string () {
|
public string to_string () {
|
||||||
var params = new HashTable<string, string ? > (str_hash, str_equal);
|
var params = new HashTable<string, string ?> (str_hash, str_equal);
|
||||||
|
|
||||||
params.set ("applied_id", applied_id.to_string ());
|
params.set ("applied_id", applied_id.to_string ());
|
||||||
params.set ("app_name", app_name);
|
params.set ("app_name", app_name);
|
||||||
@ -280,6 +300,8 @@ namespace SwayNotificationCenter {
|
|||||||
_actions += "\n\t" + _action.to_string ();
|
_actions += "\n\t" + _action.to_string ();
|
||||||
}
|
}
|
||||||
params.set ("actions", string.joinv ("", _actions));
|
params.set ("actions", string.joinv ("", _actions));
|
||||||
|
params.set ("inline-reply", inline_reply == null
|
||||||
|
? null : inline_reply.to_string ());
|
||||||
|
|
||||||
string[] result = {};
|
string[] result = {};
|
||||||
foreach (var k in params.get_keys ()) {
|
foreach (var k in params.get_keys ()) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.38.2 -->
|
<!-- Generated with glade 3.40.0 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.24"/>
|
<requires lib="gtk+" version="3.24"/>
|
||||||
<requires lib="libhandy" version="1.2"/>
|
<requires lib="libhandy" version="1.2"/>
|
||||||
@ -45,10 +45,10 @@
|
|||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkButton" id="default_button">
|
<object class="GtkEventBox" id="default_action">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="receives-default">False</property>
|
<property name="events">GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@ -192,6 +192,49 @@
|
|||||||
<property name="position">2</property>
|
<property name="position">2</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="inline_reply_box">
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="inline_reply_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="input-hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_EMOJI | GTK_INPUT_HINT_NONE</property>
|
||||||
|
<style>
|
||||||
|
<class name="inline-reply-entry"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="inline_reply_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">True</property>
|
||||||
|
<style>
|
||||||
|
<class name="inline-reply-button"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="inline-reply"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<style>
|
<style>
|
||||||
<class name="notification-content"/>
|
<class name="notification-content"/>
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
namespace SwayNotificationCenter {
|
namespace SwayNotificationCenter {
|
||||||
|
|
||||||
|
public enum NotificationType { CONTROL_CENTER, POPUP }
|
||||||
|
|
||||||
[GtkTemplate (ui = "/org/erikreider/sway-notification-center/notification/notification.ui")]
|
[GtkTemplate (ui = "/org/erikreider/sway-notification-center/notification/notification.ui")]
|
||||||
public class Notification : Gtk.ListBoxRow {
|
public class Notification : Gtk.ListBoxRow {
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
@ -10,7 +13,11 @@ namespace SwayNotificationCenter {
|
|||||||
unowned Gtk.EventBox event_box;
|
unowned Gtk.EventBox event_box;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
unowned Gtk.Button default_button;
|
unowned Gtk.EventBox default_action;
|
||||||
|
|
||||||
|
|
||||||
|
/** The default_action gesture. Allows clicks while not in swipe gesture. */
|
||||||
|
private Gtk.GestureMultiPress gesture;
|
||||||
|
|
||||||
[GtkChild]
|
[GtkChild]
|
||||||
unowned Gtk.ProgressBar progress_bar;
|
unowned Gtk.ProgressBar progress_bar;
|
||||||
@ -36,6 +43,17 @@ namespace SwayNotificationCenter {
|
|||||||
[GtkChild]
|
[GtkChild]
|
||||||
unowned Gtk.Image body_image;
|
unowned Gtk.Image body_image;
|
||||||
|
|
||||||
|
// Inline Reply
|
||||||
|
[GtkChild]
|
||||||
|
unowned Gtk.Box inline_reply_box;
|
||||||
|
[GtkChild]
|
||||||
|
unowned Gtk.Entry inline_reply_entry;
|
||||||
|
[GtkChild]
|
||||||
|
unowned Gtk.Button inline_reply_button;
|
||||||
|
|
||||||
|
private bool default_action_down = false;
|
||||||
|
private bool default_action_in = false;
|
||||||
|
|
||||||
public static Gtk.IconSize icon_size = Gtk.IconSize.INVALID;
|
public static Gtk.IconSize icon_size = Gtk.IconSize.INVALID;
|
||||||
private int notification_icon_size { get; default = ConfigModel.instance.notification_icon_size; }
|
private int notification_icon_size { get; default = ConfigModel.instance.notification_icon_size; }
|
||||||
|
|
||||||
@ -55,6 +73,12 @@ namespace SwayNotificationCenter {
|
|||||||
public NotifyParams param { get; construct; }
|
public NotifyParams param { get; construct; }
|
||||||
public NotiDaemon noti_daemon { get; construct; }
|
public NotiDaemon noti_daemon { get; construct; }
|
||||||
|
|
||||||
|
public NotificationType notification_type {
|
||||||
|
get;
|
||||||
|
construct;
|
||||||
|
default = NotificationType.POPUP;
|
||||||
|
}
|
||||||
|
|
||||||
public uint timeout_delay { get; construct; }
|
public uint timeout_delay { get; construct; }
|
||||||
public uint timeout_low_delay { get; construct; }
|
public uint timeout_low_delay { get; construct; }
|
||||||
public uint timeout_critical_delay { get; construct; }
|
public uint timeout_critical_delay { get; construct; }
|
||||||
@ -63,6 +87,8 @@ namespace SwayNotificationCenter {
|
|||||||
|
|
||||||
public int number_of_body_lines { get; construct; default = 10; }
|
public int number_of_body_lines { get; construct; default = 10; }
|
||||||
|
|
||||||
|
public bool has_inline_reply { get; private set; default = false; }
|
||||||
|
|
||||||
private int carousel_empty_widget_index = 0;
|
private int carousel_empty_widget_index = 0;
|
||||||
|
|
||||||
private static Regex code_regex;
|
private static Regex code_regex;
|
||||||
@ -83,18 +109,23 @@ namespace SwayNotificationCenter {
|
|||||||
|
|
||||||
/** Show a non-timed notification */
|
/** Show a non-timed notification */
|
||||||
public Notification.regular (NotifyParams param,
|
public Notification.regular (NotifyParams param,
|
||||||
NotiDaemon noti_daemon) {
|
NotiDaemon noti_daemon,
|
||||||
Object (noti_daemon: noti_daemon, param: param);
|
NotificationType notification_type) {
|
||||||
|
Object (noti_daemon: noti_daemon,
|
||||||
|
param: param,
|
||||||
|
notification_type: notification_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Show a timed notification */
|
/** Show a timed notification */
|
||||||
public Notification.timed (NotifyParams param,
|
public Notification.timed (NotifyParams param,
|
||||||
NotiDaemon noti_daemon,
|
NotiDaemon noti_daemon,
|
||||||
|
NotificationType notification_type,
|
||||||
uint timeout,
|
uint timeout,
|
||||||
uint timeout_low,
|
uint timeout_low,
|
||||||
uint timeout_critical) {
|
uint timeout_critical) {
|
||||||
Object (noti_daemon: noti_daemon,
|
Object (noti_daemon: noti_daemon,
|
||||||
param: param,
|
param: param,
|
||||||
|
notification_type: notification_type,
|
||||||
is_timed: true,
|
is_timed: true,
|
||||||
timeout_delay: timeout,
|
timeout_delay: timeout,
|
||||||
timeout_low_delay: timeout_low,
|
timeout_low_delay: timeout_low,
|
||||||
@ -116,6 +147,54 @@ namespace SwayNotificationCenter {
|
|||||||
stderr.printf ("Invalid regex: %s", e.message);
|
stderr.printf ("Invalid regex: %s", e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build the default_action gesture. Makes clickes compatible with
|
||||||
|
// the Hdy Swipe gesture unlike a regular ::button_release_event
|
||||||
|
gesture = new Gtk.GestureMultiPress (default_action);
|
||||||
|
gesture.set_touch_only (false);
|
||||||
|
gesture.set_exclusive (true);
|
||||||
|
gesture.set_button (Gdk.BUTTON_PRIMARY);
|
||||||
|
gesture.set_propagation_phase (Gtk.PropagationPhase.BUBBLE);
|
||||||
|
gesture.pressed.connect ((_gesture, _n_press, _x, _y) => {
|
||||||
|
default_action_in = true;
|
||||||
|
default_action_down = true;
|
||||||
|
default_action_update_state ();
|
||||||
|
});
|
||||||
|
gesture.released.connect ((gesture, _n_press, _x, _y) => {
|
||||||
|
// Emit released
|
||||||
|
if (!default_action_down) return;
|
||||||
|
default_action_down = false;
|
||||||
|
if (default_action_in) {
|
||||||
|
click_default_action ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Gdk.EventSequence ? sequence = gesture.get_current_sequence ();
|
||||||
|
if (sequence == null) {
|
||||||
|
default_action_in = false;
|
||||||
|
default_action_update_state ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
gesture.update.connect ((gesture, sequence) => {
|
||||||
|
Gtk.GestureSingle gesture_single = (Gtk.GestureSingle) gesture;
|
||||||
|
if (sequence != gesture_single.get_current_sequence ()) return;
|
||||||
|
|
||||||
|
Gtk.Allocation allocation;
|
||||||
|
double x, y;
|
||||||
|
|
||||||
|
default_action.get_allocation (out allocation);
|
||||||
|
gesture.get_point (sequence, out x, out y);
|
||||||
|
bool in_button = (x >= 0 && y >= 0 && x < allocation.width && y < allocation.height);
|
||||||
|
if (default_action_in != in_button) {
|
||||||
|
default_action_in = in_button;
|
||||||
|
default_action_update_state ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
gesture.cancel.connect ((_gesture, _sequence) => {
|
||||||
|
if (default_action_down) {
|
||||||
|
default_action_down = false;
|
||||||
|
default_action_update_state ();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.transition_time = ConfigModel.instance.transition_time;
|
this.transition_time = ConfigModel.instance.transition_time;
|
||||||
build_noti ();
|
build_noti ();
|
||||||
|
|
||||||
@ -125,6 +204,18 @@ namespace SwayNotificationCenter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void default_action_update_state () {
|
||||||
|
bool pressed = default_action_in && default_action_down;
|
||||||
|
|
||||||
|
Gtk.StateFlags flags = default_action.get_state_flags () &
|
||||||
|
~(Gtk.StateFlags.PRELIGHT | Gtk.StateFlags.ACTIVE);
|
||||||
|
|
||||||
|
if (default_action_in) flags |= Gtk.StateFlags.PRELIGHT;
|
||||||
|
if (pressed) flags |= Gtk.StateFlags.ACTIVE;
|
||||||
|
|
||||||
|
default_action.set_state_flags (flags, true);
|
||||||
|
}
|
||||||
|
|
||||||
private void on_size_allocation (Gtk.Allocation _ignored) {
|
private void on_size_allocation (Gtk.Allocation _ignored) {
|
||||||
// Force recomputing the allocated size of the wrapped GTK label in the body.
|
// Force recomputing the allocated size of the wrapped GTK label in the body.
|
||||||
// `queue_resize` alone DOES NOT WORK because it does not properly invalidate
|
// `queue_resize` alone DOES NOT WORK because it does not properly invalidate
|
||||||
@ -151,13 +242,31 @@ namespace SwayNotificationCenter {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
default_button.clicked.connect (click_default_action);
|
// Adds CSS :hover selector to EventBox
|
||||||
|
default_action.enter_notify_event.connect ((event) => {
|
||||||
|
if (event.detail != Gdk.NotifyType.INFERIOR
|
||||||
|
&& event.window == default_action.get_window ()) {
|
||||||
|
default_action_in = true;
|
||||||
|
default_action_update_state ();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
default_action.leave_notify_event.connect ((event) => {
|
||||||
|
if (event.detail != Gdk.NotifyType.INFERIOR
|
||||||
|
&& event.window == default_action.get_window ()) {
|
||||||
|
default_action_in = false;
|
||||||
|
default_action_update_state ();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
default_action.unmap.connect (() => default_action_in = false);
|
||||||
|
|
||||||
close_revealer.set_transition_duration (this.transition_time);
|
close_revealer.set_transition_duration (this.transition_time);
|
||||||
|
|
||||||
close_button.clicked.connect (() => close_notification ());
|
close_button.clicked.connect (() => close_notification ());
|
||||||
|
|
||||||
this.event_box.enter_notify_event.connect (() => {
|
this.event_box.enter_notify_event.connect ((event) => {
|
||||||
close_revealer.set_reveal_child (true);
|
close_revealer.set_reveal_child (true);
|
||||||
remove_noti_timeout ();
|
remove_noti_timeout ();
|
||||||
return false;
|
return false;
|
||||||
@ -207,6 +316,7 @@ namespace SwayNotificationCenter {
|
|||||||
|
|
||||||
set_body ();
|
set_body ();
|
||||||
set_icon ();
|
set_icon ();
|
||||||
|
set_inline_reply ();
|
||||||
set_actions ();
|
set_actions ();
|
||||||
set_style_urgency ();
|
set_style_urgency ();
|
||||||
|
|
||||||
@ -311,7 +421,7 @@ namespace SwayNotificationCenter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void click_alt_action (uint index) {
|
public void click_alt_action (uint index) {
|
||||||
List<weak Gtk.Widget>? children = alt_actions_box.get_children ();
|
List<weak Gtk.Widget> ? children = alt_actions_box.get_children ();
|
||||||
uint length = children.length ();
|
uint length = children.length ();
|
||||||
if (length == 0 || index >= length) return;
|
if (length == 0 || index >= length) return;
|
||||||
|
|
||||||
@ -356,6 +466,54 @@ namespace SwayNotificationCenter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void set_inline_reply () {
|
||||||
|
// Only show inline replies in popup notifications if the compositor
|
||||||
|
// supports ON_DEMAND layer shell keyboard interactivity
|
||||||
|
if (!ConfigModel.instance.notification_inline_replies
|
||||||
|
|| (ConfigModel.instance.layer_shell
|
||||||
|
&& layer_shell_protocol_version < 4
|
||||||
|
&& notification_type == NotificationType.POPUP)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (param.inline_reply == null) return;
|
||||||
|
|
||||||
|
has_inline_reply = true;
|
||||||
|
|
||||||
|
inline_reply_box.show ();
|
||||||
|
|
||||||
|
inline_reply_entry.set_placeholder_text (
|
||||||
|
param.inline_reply_placeholder ?? "Enter Text");
|
||||||
|
// Set reply Button sensitivity to disabled if Entry text is empty
|
||||||
|
inline_reply_entry.bind_property (
|
||||||
|
"text",
|
||||||
|
inline_reply_button, "sensitive",
|
||||||
|
BindingFlags.SYNC_CREATE,
|
||||||
|
(binding, srcval, ref targetval) => {
|
||||||
|
targetval.set_boolean (((string) srcval).strip ().length > 0);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
null);
|
||||||
|
|
||||||
|
inline_reply_entry.key_release_event.connect ((w, event_key) => {
|
||||||
|
switch (Gdk.keyval_name (event_key.keyval)) {
|
||||||
|
case "Return":
|
||||||
|
inline_reply_button.clicked ();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
inline_reply_button.set_label (param.inline_reply.name ?? "Reply");
|
||||||
|
inline_reply_button.clicked.connect (() => {
|
||||||
|
string text = inline_reply_entry.get_text ().strip ();
|
||||||
|
if (text.length == 0) return;
|
||||||
|
noti_daemon.NotificationReplied (param.applied_id, text);
|
||||||
|
// Dismiss notification without activating Action
|
||||||
|
action_clicked (null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void set_actions () {
|
private void set_actions () {
|
||||||
// Check for security codes
|
// Check for security codes
|
||||||
string ? code = parse_body_codes ();
|
string ? code = parse_body_codes ();
|
||||||
@ -543,6 +701,7 @@ namespace SwayNotificationCenter {
|
|||||||
/** Forces the EventBox to reload its style_context #27 */
|
/** Forces the EventBox to reload its style_context #27 */
|
||||||
public void reload_style_context () {
|
public void reload_style_context () {
|
||||||
event_box.get_style_context ().changed ();
|
event_box.get_style_context ().changed ();
|
||||||
|
default_action.get_style_context ().changed ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ namespace SwayNotificationCenter {
|
|||||||
|
|
||||||
private double last_upper = 0;
|
private double last_upper = 0;
|
||||||
|
|
||||||
|
Gee.HashSet<uint32> inline_reply_notifications = new Gee.HashSet<uint32> ();
|
||||||
|
|
||||||
private const int MAX_HEIGHT = 600;
|
private const int MAX_HEIGHT = 600;
|
||||||
|
|
||||||
private NotificationWindow () {
|
private NotificationWindow () {
|
||||||
@ -158,6 +160,7 @@ namespace SwayNotificationCenter {
|
|||||||
public delegate bool remove_iter_func (Notification notification);
|
public delegate bool remove_iter_func (Notification notification);
|
||||||
|
|
||||||
public void close_all_notifications (remove_iter_func ? func = null) {
|
public void close_all_notifications (remove_iter_func ? func = null) {
|
||||||
|
inline_reply_notifications.clear ();
|
||||||
if (!this.get_realized ()) return;
|
if (!this.get_realized ()) return;
|
||||||
foreach (var w in box.get_children ()) {
|
foreach (var w in box.get_children ()) {
|
||||||
Notification notification = (Notification) w;
|
Notification notification = (Notification) w;
|
||||||
@ -170,6 +173,17 @@ namespace SwayNotificationCenter {
|
|||||||
private void remove_notification (Notification ? noti, bool replaces) {
|
private void remove_notification (Notification ? noti, bool replaces) {
|
||||||
// Remove notification and its destruction timeout
|
// Remove notification and its destruction timeout
|
||||||
if (noti != null) {
|
if (noti != null) {
|
||||||
|
#if HAVE_LATEST_GTK_LAYER_SHELL
|
||||||
|
if (noti.has_inline_reply) {
|
||||||
|
inline_reply_notifications.remove (noti.param.applied_id);
|
||||||
|
if (inline_reply_notifications.size == 0
|
||||||
|
&& GtkLayerShell.get_keyboard_mode (this)
|
||||||
|
!= GtkLayerShell.KeyboardMode.NONE) {
|
||||||
|
GtkLayerShell.set_keyboard_mode (
|
||||||
|
this, GtkLayerShell.KeyboardMode.NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
noti.remove_noti_timeout ();
|
noti.remove_noti_timeout ();
|
||||||
noti.destroy ();
|
noti.destroy ();
|
||||||
}
|
}
|
||||||
@ -188,9 +202,21 @@ namespace SwayNotificationCenter {
|
|||||||
NotiDaemon noti_daemon) {
|
NotiDaemon noti_daemon) {
|
||||||
var noti = new Notification.timed (param,
|
var noti = new Notification.timed (param,
|
||||||
noti_daemon,
|
noti_daemon,
|
||||||
|
NotificationType.POPUP,
|
||||||
ConfigModel.instance.timeout,
|
ConfigModel.instance.timeout,
|
||||||
ConfigModel.instance.timeout_low,
|
ConfigModel.instance.timeout_low,
|
||||||
ConfigModel.instance.timeout_critical);
|
ConfigModel.instance.timeout_critical);
|
||||||
|
#if HAVE_LATEST_GTK_LAYER_SHELL
|
||||||
|
if (noti.has_inline_reply) {
|
||||||
|
inline_reply_notifications.add (param.applied_id);
|
||||||
|
|
||||||
|
if (GtkLayerShell.get_keyboard_mode (this)
|
||||||
|
!= GtkLayerShell.KeyboardMode.ON_DEMAND) {
|
||||||
|
GtkLayerShell.set_keyboard_mode (
|
||||||
|
this, GtkLayerShell.KeyboardMode.ON_DEMAND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (list_reverse) {
|
if (list_reverse) {
|
||||||
box.pack_start (noti);
|
box.pack_start (noti);
|
||||||
|
@ -6,11 +6,15 @@
|
|||||||
|
|
||||||
@define-color noti-border-color rgba(255, 255, 255, 0.15);
|
@define-color noti-border-color rgba(255, 255, 255, 0.15);
|
||||||
@define-color noti-bg rgb(48, 48, 48);
|
@define-color noti-bg rgb(48, 48, 48);
|
||||||
|
@define-color noti-bg-darker rgb(38, 38, 38);
|
||||||
@define-color noti-bg-hover rgb(56, 56, 56);
|
@define-color noti-bg-hover rgb(56, 56, 56);
|
||||||
@define-color noti-bg-focus rgba(68, 68, 68, 0.6);
|
@define-color noti-bg-focus rgba(68, 68, 68, 0.6);
|
||||||
@define-color noti-close-bg rgba(255, 255, 255, 0.1);
|
@define-color noti-close-bg rgba(255, 255, 255, 0.1);
|
||||||
@define-color noti-close-bg-hover rgba(255, 255, 255, 0.15);
|
@define-color noti-close-bg-hover rgba(255, 255, 255, 0.15);
|
||||||
|
|
||||||
|
@define-color text-color rgb(255, 255, 255);
|
||||||
|
@define-color text-color-disabled rgb(150, 150, 150);
|
||||||
|
|
||||||
@define-color bg-selected rgb(0, 128, 255);
|
@define-color bg-selected rgb(0, 128, 255);
|
||||||
|
|
||||||
.notification-row {
|
.notification-row {
|
||||||
@ -58,7 +62,7 @@
|
|||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
background: @noti-close-bg;
|
background: @noti-close-bg;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
@ -84,7 +88,8 @@
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
background: @noti-bg;
|
background: @noti-bg;
|
||||||
border: 1px solid @noti-border-color;
|
border: 1px solid @noti-border-color;
|
||||||
color: white;
|
color: @text-color;
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-default-action:hover,
|
.notification-default-action:hover,
|
||||||
@ -119,6 +124,32 @@
|
|||||||
border-right: 1px solid @noti-border-color;
|
border-right: 1px solid @noti-border-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-reply {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.inline-reply-entry {
|
||||||
|
background: @noti-bg-darker;
|
||||||
|
color: @text-color;
|
||||||
|
caret-color: @text-color;
|
||||||
|
border: 1px solid @noti-border-color;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.inline-reply-button {
|
||||||
|
margin-left: 4px;
|
||||||
|
background: @noti-bg;
|
||||||
|
border: 1px solid @noti-border-color;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
.inline-reply-button:disabled {
|
||||||
|
background: initial;
|
||||||
|
color: @text-color-disabled;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.inline-reply-button:hover {
|
||||||
|
background: @noti-bg-hover;
|
||||||
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +163,7 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +171,7 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
margin-right: 18px;
|
margin-right: 18px;
|
||||||
}
|
}
|
||||||
@ -149,7 +180,7 @@
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +214,7 @@
|
|||||||
}
|
}
|
||||||
.widget-title > button {
|
.widget-title > button {
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
background: @noti-bg;
|
background: @noti-bg;
|
||||||
border: 1px solid @noti-border-color;
|
border: 1px solid @noti-border-color;
|
||||||
@ -318,7 +349,7 @@
|
|||||||
}
|
}
|
||||||
.widget-inhibitors > button {
|
.widget-inhibitors > button {
|
||||||
font-size: initial;
|
font-size: initial;
|
||||||
color: white;
|
color: @text-color;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
background: @noti-bg;
|
background: @noti-bg;
|
||||||
border: 1px solid @noti-border-color;
|
border: 1px solid @noti-border-color;
|
||||||
|
Loading…
Reference in New Issue
Block a user