My collection of plugins for the Qtile window manager.
git clone https://mcol.xyz/code/qtools
Log | Files | Refs | README

commit dbda507e9faba5661419f2faebb37c7a182e10a6
parent 94e9f6b814c9e96ed81993dca0f85e3e52e5e56f
Author: mcol <mcol@posteo.net>
Date:   Sat, 25 Jan 2020 00:08:10 +0000

make notification subclass from Popup

Diffstat:
Mqtools/notification/notification.py | 165++++++++++++++++++++++++++-----------------------------------------------------
1 file changed, 54 insertions(+), 111 deletions(-)

diff --git a/qtools/notification/notification.py b/qtools/notification/notification.py @@ -14,32 +14,13 @@ Example usage: """ -from xcffib.xproto import StackMode - from libqtile import configurable, pangocffi, window from libqtile.lazy import lazy from libqtile.notify import notifier from libqtile.drawer import Drawer +from libqtile.log_utils import logger - -ALIGNMENTS = { - 'left': pangocffi.pango.PANGO_ALIGN_LEFT, - 'center': pangocffi.pango.PANGO_ALIGN_CENTER, - 'right': pangocffi.pango.PANGO_ALIGN_RIGHT, -} - - -class Popup: - """ - These represent a single pop-up window. These are (re)cycled, so if we have a - maximum of two windows visible at once, we keep two of these and re-draw and present - them. - """ - def __init__(self, win, drawer, layout): - self.win = win - self.drawer = drawer - self.layout = layout - self.id = None +from qtools import Popup class Server(configurable.Configurable): @@ -53,15 +34,17 @@ class Server(configurable.Configurable): - overflow - spacing between lines - replace_id - - notifications moving up to first position if possible """ - defaults = [ - ('x', 96, 'X position of notifications.'), - ('y', 96, 'Y position of notifications.'), - ('width', 256, 'Width of notifications.'), - ('height', 64, 'Height of notifications.'), + ('format', '{summary}\n{body}', 'Text format.'), ('opacity', 1.0, 'Opacity of notifications.'), + ('border_width', 4, 'Line width of drawn borders.'), + ('corner_radius', None, 'Corner radius for round corners, or None.'), + ('font', 'sans', 'Font used in notifications.'), + ('fontsize', 14, 'Size of font.'), + ('fontshadow', None, 'Color for text shadows, or None for no shadows.'), + ('padding', None, 'Padding at sides of text.'), + ('text_alignment', 'left', 'Text alignment: left, center or right.'), ( 'foreground', ('#ffffff', '#ffffff', '#ffffff'), @@ -77,14 +60,6 @@ class Server(configurable.Configurable): ('#111111', '#111111', '#111111'), 'Border colours in ascending order of urgency. Or None for none.', ), - ('border_width', 4, 'Line width of drawn borders.'), - ('corner_radius', None, 'Corner radius for round corners, or None.'), - ('font', 'sans', 'Font used in notifications.'), - ('fontsize', 14, 'Size of font.'), - ('fontshadow', None, 'Color for text shadows, or None for no shadows.'), - ('padding', None, 'Padding at sides of text.'), - ('format', '{summary}\n{body}', 'Text format.'), - ('text_alignment', 'left', 'Text alignment: left, center or right.'), ( 'timeout', (5000, 5000, 0), @@ -107,6 +82,7 @@ class Server(configurable.Configurable): self._hidden = [] self._shown = [] self._queue = [] + self._positions = [] self._make_attr_list('foreground') self._make_attr_list('background') @@ -133,7 +109,7 @@ class Server(configurable.Configurable): def configure(self, qtile): """ This method needs to be called to set up the Server with the Qtile manager and - reorganise some configuration options. + create the required popup windows. """ self.qtile = qtile @@ -141,64 +117,26 @@ class Server(configurable.Configurable): self.padding = self.fontsize / 2 if self.border_width: self.border = [self.qtile.color_pixel(c) for c in self.border] + + self._popup_config = {} + for opt in Popup.defaults: + key = opt[0] + if hasattr(self, key): + value = getattr(self, key) + if isinstance(value, (tuple, list)): + self._popup_config[key] = value[1] + else: + self._popup_config[key] = value + for win in range(self.max_windows): - popup = self._create_window(win) + popup = Popup(self.qtile, **self._popup_config) self._popups.append(popup) self._hidden.append(popup) - - notifier.register(self._notify) - - def _create_window(self, win): - """ - Get a Popup instance to maintain a window, drawer and textlayout with a specific - configuration. - """ - win = window.Internal.create( - self.qtile, - self.x, - self.y + (self.height + self.gap) * win, - self.width, self.height, - self.opacity, - ) - drawer = Drawer( - self.qtile, win.window.wid, self.width, self.height, - ) - layout = drawer.textlayout( - text='', - colour=self.foreground[1], - font_family=self.font, - font_size=self.fontsize, - font_shadow=self.fontshadow, - wrap=True if self.overflow == 'extend_y' else False, - markup=True, - ) - layout.layout.set_alignment(ALIGNMENTS[self.text_alignment]) - - if self.border_width: - win.window.configure(borderwidth=self.border_width) - if self.corner_radius: - win.window.round_corners( - self.width, self.height, self.corner_radius, self.border_width, + self._positions.append( + (self.x, self.y + win * (self.height + self.border_width * 3 + self.gap)) ) - popup = Popup(win, drawer, layout) - win.handle_Expose = self._handle_Expose - win.handle_KeyPress = self._handle_KeyPress - win.handle_ButtonPress = self._get_popup_ButtonPress(popup) - self.qtile.windows_map[win.window.wid] = win - return popup - - def _handle_Expose(self, e): - pass - - def _handle_KeyPress(self, event): - pass - - def _get_popup_ButtonPress(self, popup): - def _inner(event): - if event.detail == 1: - self._close(popup) - return _inner + notifier.register(self._notify) def _notify(self, notif): """ @@ -206,11 +144,7 @@ class Server(configurable.Configurable): received via dbus. They will either be drawn now or queued to be drawn soon. """ if self._hidden: - for popup in self._popups: - if popup in self._hidden: - break - self._hidden.remove(popup) - self._send(notif, popup) + self._send(notif, self._hidden.pop()) else: self._queue.append(notif) @@ -224,20 +158,19 @@ class Server(configurable.Configurable): summary = pangocffi.markup_escape_text(notif.summary) if notif.body: body = pangocffi.markup_escape_text(notif.body) - text = self.format.format(summary=summary, body=body) urgency = notif.hints.get('urgency', 1) - popup.drawer.clear(self.background[urgency]) - popup.layout.colour = self.foreground[urgency] - popup.layout.text = text - popup.layout.draw( - self.padding, (popup.win.height - popup.layout.height) / 2, - ) + popup.x, popup.y = self._positions[len(self._shown)] + popup.background = self.background[urgency] + popup.foreground = self.foreground[urgency] + popup.text = self.format.format(summary=summary, body=body) + popup.clear() + popup.draw_text() if self.border_width: - popup.win.window.set_attribute(borderpixel=self.border[urgency]) - popup.win.unhide() - popup.drawer.draw() - popup.win.window.configure(stackmode=StackMode.Above) + popup.set_border(self.border[urgency]) + popup.place() + popup.unhide() + popup.draw_window() popup.id = notif.id if notif.timeout is None or notif.timeout < 0: @@ -252,25 +185,35 @@ class Server(configurable.Configurable): """ Close the specified Popup instance. """ - if nid is not None and popup.id != nid: - return - if popup in self._shown: self._shown.remove(popup) + if nid is not None and popup.id != nid: + return + popup.hide() if self._queue: self._send(self._queue.pop(0), popup) else: - popup.win.hide() self._hidden.append(popup) + for index, shown in enumerate(self._shown): + shown.x, shown.y = self._positions[index] + shown.place() + def close(self, qtile=None): """ - This method can be bound to keys to close the oldest of any visible notification - windows. + Close the oldest of all visible popup windows. """ if self._shown: self._close(self._shown[0]) + def close_all(self, qtile=None): + """ + Close all popup windows. + """ + self._queue.clear() + for popup in self._shown: + self._close(popup) + #def prev(self, qtile=None): # self._notify(notifier.notifications[self._current_id])