From 39c669084f00d366564f67a12eb097820c366b2b Mon Sep 17 00:00:00 2001 From: Sander Sweers Date: Fri, 8 Jul 2022 23:32:53 +0200 Subject: [PATCH] Replace custom MessageArea with standard widget + animation A Gtk.InfoBar already has a Gtk.Revealer which animates perfectly fine for our use. --- blueman/gui/Makefile.am | 2 +- blueman/gui/MessageArea.py | 105 ------------------- blueman/gui/manager/ManagerDeviceMenu.py | 7 +- blueman/main/Manager.py | 46 ++++++-- blueman/plugins/manager/PulseAudioProfile.py | 3 +- data/ui/manager-main.ui | 81 +++++++++++++- po/POTFILES.in | 1 - 7 files changed, 123 insertions(+), 122 deletions(-) delete mode 100644 blueman/gui/MessageArea.py diff --git a/blueman/gui/Makefile.am b/blueman/gui/Makefile.am index 6df80b341..b15ba3c6f 100644 --- a/blueman/gui/Makefile.am +++ b/blueman/gui/Makefile.am @@ -3,7 +3,7 @@ SUBDIRS = \ manager bluemandir = $(pythondir)/blueman/gui -blueman_PYTHON = MessageArea.py Animation.py GsmSettings.py CommonUi.py DeviceList.py DeviceSelectorDialog.py DeviceSelectorList.py DeviceSelectorWidget.py GenericList.py GtkAnimation.py __init__.py Notification.py +blueman_PYTHON = Animation.py GsmSettings.py CommonUi.py DeviceList.py DeviceSelectorDialog.py DeviceSelectorList.py DeviceSelectorWidget.py GenericList.py GtkAnimation.py __init__.py Notification.py CLEANFILES = \ $(BUILT_SOURCES) diff --git a/blueman/gui/MessageArea.py b/blueman/gui/MessageArea.py deleted file mode 100644 index 564f1bf51..000000000 --- a/blueman/gui/MessageArea.py +++ /dev/null @@ -1,105 +0,0 @@ -from gettext import gettext as _ -from typing import Optional - -from blueman.gui.GtkAnimation import WidgetFade - -import gi -gi.require_version("Gtk", "3.0") -gi.require_version("Gdk", "3.0") -from gi.repository import Gtk -from gi.repository import Gdk -from gi.repository import Pango - - -class MessageArea(Gtk.InfoBar): - _inst_: "MessageArea" - - def __new__(cls) -> "MessageArea": - if not hasattr(MessageArea, "_inst_"): - MessageArea._inst_ = super().__new__(cls) - - return MessageArea._inst_ - - def __init__(self) -> None: - super().__init__(show_close_button=True) - - self.set_name("MessageArea") - - self.text = "" - - self.anim = WidgetFade(self, self.get_style_context().get_background_color(Gtk.StateFlags.NORMAL)) - self.hl_anim = WidgetFade(self, Gdk.RGBA(1, 0, 0, 1)) - - self.icon = Gtk.Image(pixel_size=16, visible=True) - self.label = Gtk.Label(xalign=0, ellipsize=Pango.EllipsizeMode.END, single_line_mode=True, - selectable=True, visible=True) - - im = Gtk.Image(icon_name="dialog-information", pixel_size=16, visible=True) - self.b_more = self.add_button(_("More"), 0) - self.b_more.set_image(im) - self.b_more.props.relief = Gtk.ReliefStyle.NONE - - self.content_area = self.get_content_area() - self.content_area.add(self.icon) - self.content_area.add(self.label) - - self.connect("response", self.on_response) - - def on_response(self, info_bar: Gtk.InfoBar, response_id: int) -> None: - if response_id == 0: - assert self.bt is not None - parent = self.get_toplevel() - assert isinstance(parent, Gtk.Container) - d = Gtk.MessageDialog(parent=parent, type=Gtk.MessageType.INFO, - buttons=Gtk.ButtonsType.CLOSE, text='\n'.join((self.text, self.bt))) - d.run() - d.destroy() - elif response_id == Gtk.ResponseType.CLOSE: - info_bar.props.visible = False - - @staticmethod - def close() -> None: - MessageArea._inst_.response(1) - - @staticmethod - def show_message(text: str, bt: Optional[str] = None, icon: str = "dialog-warning") -> None: - MessageArea._inst_._show_message(text, bt, icon) - - def _show_message(self, text: str, bt: Optional[str] = None, icon: str = "dialog-warning") -> None: - def on_finished(anim: WidgetFade) -> None: - anim.disconnect(sig) - anim.freeze() - - self.text = text - self.bt = bt - - self.label.props.tooltip_text = text - self.icon.props.icon_name = icon - - if icon == "dialog-warning": - self.set_message_type(Gtk.MessageType.ERROR) - self.hl_anim.color = Gdk.RGBA(1, 0, 0, 1) - else: - self.set_message_type(Gtk.MessageType.INFO) - self.hl_anim.color = Gdk.RGBA(0, 0, 1, 1) - - if not self.props.visible: - sig = self.anim.connect("animation-finished", on_finished) - self.anim.thaw() - self.show() - self.anim.animate(start=0.0, end=1.0, duration=500) - else: - sig = self.hl_anim.connect("animation-finished", on_finished) - self.hl_anim.thaw() - self.hl_anim.animate(start=0.7, end=1.0, duration=1000) - - if self.bt: - self.label.props.label = self.text + "…" - self.b_more.props.visible = True - else: - self.label.props.label = self.text - self.b_more.props.visible = False - - # Queue a resize to workaround negative height problem, see issue #369 - # TODO revisit this in later Gtk versions to see if the problem persists - self.queue_resize() diff --git a/blueman/gui/manager/ManagerDeviceMenu.py b/blueman/gui/manager/ManagerDeviceMenu.py index afa50dcef..47077899e 100644 --- a/blueman/gui/manager/ManagerDeviceMenu.py +++ b/blueman/gui/manager/ManagerDeviceMenu.py @@ -11,7 +11,6 @@ from blueman.gui.manager.ManagerProgressbar import ManagerProgressbar from blueman.main.Builder import Builder from blueman.main.DBusProxies import AppletService, DBusProxyFailed -from blueman.gui.MessageArea import MessageArea from blueman.Sdp import ( ServiceUUID, AUDIO_SOURCE_SVCLASS_ID, @@ -130,8 +129,6 @@ def success(_obj: AppletService, _result: None, _user_data: None) -> None: logging.info("success") prog.message(_("Success!")) - MessageArea.close() - self.unset_op(device) def fail(_obj: Optional[AppletService], result: GLib.Error, _user_data: None) -> None: @@ -177,7 +174,7 @@ def ok(_obj: AppletService, _result: None, _user_date: None) -> None: def err(_obj: Optional[AppletService], result: GLib.Error, _user_date: None) -> None: logging.warning(f"disconnect failed {result}") msg, tb = e_(result.message) - MessageArea.show_message(_("Disconnection Failed: ") + msg, tb) + self.Blueman.infobar_update(_("Disconnection Failed: ") + msg, bt=tb) self.generate() if self._appl is None: @@ -215,7 +212,7 @@ def _handle_error_message(self, error: GLib.Error) -> None: msg = error.message.split(":", 3)[-1].strip() if msg != "Cancelled": - MessageArea.show_message(_("Connection Failed: ") + msg) + self.Blueman.infobar_update(_("Connection Failed: ") + msg) @staticmethod def _get_errno(error: GLib.Error) -> Optional[int]: diff --git a/blueman/main/Manager.py b/blueman/main/Manager.py index aea083f75..a18db7ca4 100644 --- a/blueman/main/Manager.py +++ b/blueman/main/Manager.py @@ -17,7 +17,6 @@ from blueman.main.Config import Config from blueman.main.DBusProxies import AppletService, DBusProxyFailed from blueman.gui.CommonUi import ErrorDialog -from blueman.gui.MessageArea import MessageArea from blueman.gui.Notification import Notification from blueman.main.PluginManager import PluginManager import blueman.plugins.manager @@ -72,17 +71,16 @@ def do_activate(self) -> None: # Connect to configure event to store new window position and size self.window.connect("configure-event", self._on_configure) - grid = self.builder.get_widget("grid", Gtk.Grid) - toolbar = self.builder.get_widget("toolbar", Gtk.Toolbar) statusbar = self.builder.get_widget("statusbar", Gtk.Box) + self._infobar = self.builder.get_widget("message_area", Gtk.InfoBar) + self._infobar.connect("response", self._infobar_response) + self._infobar_bt: str = "empty" + self.Plugins = PluginManager(ManagerPlugin, blueman.plugins.manager, self) self.Plugins.load_plugin() - area = MessageArea() - grid.attach(area, 0, 3, 1, 1) - self._applethandlerid: Optional[int] = None # Add margin for resize grip or it will overlap @@ -208,13 +206,47 @@ def on_progress(_lst: ManagerDeviceList, frac: float) -> None: def on_error(e: Exception) -> None: prog.finalize() - MessageArea.show_message(*e_(e)) + self.infobar_update(*e_(e)) self.List.discover_devices(error_handler=on_error) s1 = self.List.connect("discovery-progress", on_progress) s2 = self.List.connect("adapter-property-changed", prop_changed) + def infobar_update(self, message: str, bt: Optional[str] = None, icon_name: str = "dialog-warning") -> None: + if icon_name == "dialog-warning": + self._infobar.set_message_type(Gtk.MessageType.WARNING) + else: + self._infobar.set_message_type(Gtk.MessageType.INFO) + + more_button = self.builder.get_widget("ib_more_button", Gtk.Button) + image = self.builder.get_widget("ib_icon", Gtk.Image) + msg_lbl = self.builder.get_widget("ib_message", Gtk.Label) + image.set_from_icon_name(icon_name, 16) + + if bt is not None: + msg_lbl.set_text(f"{message}…") + self._infobar_bt = f"{message}\n{bt}" + more_button.show() + else: + more_button.hide() + msg_lbl.set_text(f"{message}") + + self._infobar.set_visible(True) + self._infobar.set_revealed(True) + + def _infobar_response(self, info_bar: Gtk.InfoBar, response_id: int) -> None: + logging.debug(f"Response: {response_id}") + if response_id == Gtk.ResponseType.CLOSE: + info_bar.set_revealed(False) + GLib.timeout_add(250, lambda: info_bar.set_visible(False)) # transition is 250. + elif response_id == 0: + dialog = Gtk.MessageDialog(parent=self.window, type=Gtk.MessageType.INFO, modal=True, + buttons=Gtk.ButtonsType.CLOSE, text=self._infobar_bt) + dialog.connect("response", lambda d, _i: d.destroy()) + dialog.connect("close", lambda d: d.destroy()) + dialog.show() + @staticmethod def bond(device: Device) -> None: def error_handler(e: Exception) -> None: diff --git a/blueman/plugins/manager/PulseAudioProfile.py b/blueman/plugins/manager/PulseAudioProfile.py index 033e675db..6c8f6beea 100644 --- a/blueman/plugins/manager/PulseAudioProfile.py +++ b/blueman/plugins/manager/PulseAudioProfile.py @@ -6,7 +6,6 @@ from blueman.plugins.ManagerPlugin import ManagerPlugin from blueman.main.PulseAudioUtils import PulseAudioUtils, EventType from blueman.gui.manager.ManagerDeviceMenu import ManagerDeviceMenu, MenuItemsProvider, DeviceMenuItem -from blueman.gui.MessageArea import MessageArea from blueman.Functions import create_menuitem from blueman.Sdp import AUDIO_SOURCE_SVCLASS_ID, AUDIO_SINK_SVCLASS_ID, ServiceUUID @@ -84,7 +83,7 @@ def on_selection_changed(self, item: Gtk.CheckMenuItem, device: Device, profile: def on_result(res: int) -> None: if not res: - MessageArea.show_message(_("Failed to change profile to %s" % profile)) + self.parent.infobar_update(_("Failed to change profile to %s" % profile)) pa.set_card_profile(c["index"], profile, on_result) diff --git a/data/ui/manager-main.ui b/data/ui/manager-main.ui index c0d089bf0..44e16302b 100644 --- a/data/ui/manager-main.ui +++ b/data/ui/manager-main.ui @@ -2,6 +2,11 @@ + + True + False + dialog-information + False blueman @@ -217,7 +222,81 @@ - + + MessageArea + False + True + False + + + False + 8 + + + More + True + True + ib_more_icon + none + + + False + True + 0 + + + + + False + False + 0 + + + + + False + 16 + + + True + False + 16 + dialog-warning + + + False + True + 0 + + + + + True + False + True + True + + + False + True + 1 + + + + + False + False + 0 + + + + ib_more_button + + + + 0 + 3 + diff --git a/po/POTFILES.in b/po/POTFILES.in index 37f696ae0..d128585d9 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -35,7 +35,6 @@ blueman/gui/manager/ManagerToolbar.py blueman/gui/manager/__init__.py blueman/gui/GtkAnimation.py blueman/gui/DeviceSelectorDialog.py -blueman/gui/MessageArea.py blueman/gui/Animation.py blueman/gui/DeviceSelectorList.py blueman/gui/CommonUi.py