From db4cc122cb4ee3e06bca570f9879bcd069ad92ce Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 13 Nov 2018 16:59:39 +0100 Subject: [PATCH 1/5] PICARD-1060: Fix collections menu not looking and behaving like a normal menu --- picard/ui/collectionmenu.py | 61 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index 14fa7e380..dab734caf 100644 --- a/picard/ui/collectionmenu.py +++ b/picard/ui/collectionmenu.py @@ -21,6 +21,7 @@ import locale from PyQt5 import ( QtCore, + QtGui, QtWidgets, ) @@ -39,14 +40,17 @@ class CollectionMenu(QtWidgets.QMenu): def update_collections(self): self.clear() + self.actions = [] for id_, collection in sorted(user_collections.items(), key=lambda k_v: (locale.strxfrm(str(k_v[1])), k_v[0])): action = QtWidgets.QWidgetAction(self) - action.setDefaultWidget(CollectionCheckBox(self, collection)) + action.setDefaultWidget(CollectionMenuItem(self, collection)) self.addAction(action) + self.actions.append(action) self.addSeparator() self.refresh_action = self.addAction(_("Refresh List")) + self.hovered.connect(self.update_highlight) def refresh_list(self): self.refresh_action.setEnabled(False) @@ -57,13 +61,64 @@ class CollectionMenu(QtWidgets.QMenu): if self.actionAt(event.pos()) == self.refresh_action and self.refresh_action.isEnabled(): self.refresh_list() + def update_highlight(self, action): + for a in self.actions: + a.defaultWidget().set_active(a == action) + + def update_active_action_for_widget(self, widget): + for action in self.actions: + action_widget = action.defaultWidget() + is_active = action_widget == widget + if is_active: + self.setActiveAction(action) + action_widget.set_active(is_active) + + +class CollectionMenuItem(QtWidgets.QWidget): + + def __init__(self, menu, collection): + super().__init__() + self.menu = menu + self._setup_layout(menu, collection) + self._setup_colors() + + def _setup_layout(self, menu, collection): + layout = QtWidgets.QVBoxLayout(self) + style = self.style() + lmargin = style.pixelMetric(QtWidgets.QStyle.PM_LayoutLeftMargin) + rmargin = style.pixelMetric(QtWidgets.QStyle.PM_LayoutRightMargin) + layout.setContentsMargins(lmargin, 0, rmargin, 0) + self.checkbox = CollectionCheckBox(self, menu, collection) + layout.addWidget(self.checkbox) + + def _setup_colors(self): + palette = QtGui.QPalette() + bgcolor = self.palette().highlight().color() + self.textColor = palette.text().color() + self.highlightColor = palette.highlightedText().color() + palette.setColor(QtGui.QPalette.Background, bgcolor) + self.setPalette(palette) + + def set_active(self, active): + self.setAutoFillBackground(active) + palette = self.palette() + textcolor = self.highlightColor if active else self.textColor + palette.setColor(QtGui.QPalette.WindowText, textcolor) + self.checkbox.setPalette(palette) + + def enterEvent(self, e): + self.menu.update_active_action_for_widget(self) + + def leaveEvent(self, e): + self.set_active(False) + class CollectionCheckBox(QtWidgets.QCheckBox): - def __init__(self, menu, collection): + def __init__(self, parent, menu, collection): self.menu = menu self.collection = collection - super().__init__(self.label()) + super().__init__(self.label(), parent) releases = collection.releases & menu.ids if len(releases) == len(menu.ids): From cea3dae9c5f89f189bc86c17667d8d5f12b4b3fc Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 13 Nov 2018 21:15:46 +0100 Subject: [PATCH 2/5] PICARD-1060: CollectionMenu: Disable event handling while updating collection list --- picard/ui/collectionmenu.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index dab734caf..a14e5cc68 100644 --- a/picard/ui/collectionmenu.py +++ b/picard/ui/collectionmenu.py @@ -2,6 +2,7 @@ # # Picard, the next-generation MusicBrainz tagger # Copyright (C) 2013 Michael Wiencek +# Copyright (C) 2018 Philipp Wolfer # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -36,9 +37,11 @@ class CollectionMenu(QtWidgets.QMenu): def __init__(self, albums, *args): super().__init__(*args) self.ids = set(a.id for a in albums) + self._ignore_update = False self.update_collections() def update_collections(self): + self._ignore_update = True self.clear() self.actions = [] for id_, collection in sorted(user_collections.items(), @@ -48,6 +51,7 @@ class CollectionMenu(QtWidgets.QMenu): action.setDefaultWidget(CollectionMenuItem(self, collection)) self.addAction(action) self.actions.append(action) + self._ignore_update = False self.addSeparator() self.refresh_action = self.addAction(_("Refresh List")) self.hovered.connect(self.update_highlight) @@ -62,15 +66,21 @@ class CollectionMenu(QtWidgets.QMenu): self.refresh_list() def update_highlight(self, action): + if self._ignore_update: + return for a in self.actions: a.defaultWidget().set_active(a == action) def update_active_action_for_widget(self, widget): + if self._ignore_update: + return for action in self.actions: action_widget = action.defaultWidget() is_active = action_widget == widget if is_active: + self._ignore_hover = True self.setActiveAction(action) + self._ignore_hover = False action_widget.set_active(is_active) From cc1864edcf979e45cf53a917adc6edc96badba10 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 13 Nov 2018 21:50:56 +0100 Subject: [PATCH 3/5] PICARD-1060: Fixed vertical spacing in CollectionMenu --- picard/ui/collectionmenu.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index a14e5cc68..4d445bb73 100644 --- a/picard/ui/collectionmenu.py +++ b/picard/ui/collectionmenu.py @@ -95,9 +95,12 @@ class CollectionMenuItem(QtWidgets.QWidget): def _setup_layout(self, menu, collection): layout = QtWidgets.QVBoxLayout(self) style = self.style() - lmargin = style.pixelMetric(QtWidgets.QStyle.PM_LayoutLeftMargin) - rmargin = style.pixelMetric(QtWidgets.QStyle.PM_LayoutRightMargin) - layout.setContentsMargins(lmargin, 0, rmargin, 0) + layout.setContentsMargins( + style.pixelMetric(QtWidgets.QStyle.PM_LayoutLeftMargin), + style.pixelMetric(QtWidgets.QStyle.PM_FocusFrameVMargin), + style.pixelMetric(QtWidgets.QStyle.PM_LayoutRightMargin), + style.pixelMetric(QtWidgets.QStyle.PM_FocusFrameVMargin), + ) self.checkbox = CollectionCheckBox(self, menu, collection) layout.addWidget(self.checkbox) From 6e1ad4d4511794008e69887fc3c5376f3fdfadd7 Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 13 Nov 2018 23:08:58 +0100 Subject: [PATCH 4/5] PICARD-1060: Use QStyle for painting CollectionMenu This results in a native look when using Windows or macOS styles --- picard/ui/collectionmenu.py | 38 +++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index 4d445bb73..b796a1fd7 100644 --- a/picard/ui/collectionmenu.py +++ b/picard/ui/collectionmenu.py @@ -89,8 +89,8 @@ class CollectionMenuItem(QtWidgets.QWidget): def __init__(self, menu, collection): super().__init__() self.menu = menu + self.active = False self._setup_layout(menu, collection) - self._setup_colors() def _setup_layout(self, menu, collection): layout = QtWidgets.QVBoxLayout(self) @@ -99,25 +99,12 @@ class CollectionMenuItem(QtWidgets.QWidget): style.pixelMetric(QtWidgets.QStyle.PM_LayoutLeftMargin), style.pixelMetric(QtWidgets.QStyle.PM_FocusFrameVMargin), style.pixelMetric(QtWidgets.QStyle.PM_LayoutRightMargin), - style.pixelMetric(QtWidgets.QStyle.PM_FocusFrameVMargin), - ) + style.pixelMetric(QtWidgets.QStyle.PM_FocusFrameVMargin)) self.checkbox = CollectionCheckBox(self, menu, collection) layout.addWidget(self.checkbox) - def _setup_colors(self): - palette = QtGui.QPalette() - bgcolor = self.palette().highlight().color() - self.textColor = palette.text().color() - self.highlightColor = palette.highlightedText().color() - palette.setColor(QtGui.QPalette.Background, bgcolor) - self.setPalette(palette) - def set_active(self, active): - self.setAutoFillBackground(active) - palette = self.palette() - textcolor = self.highlightColor if active else self.textColor - palette.setColor(QtGui.QPalette.WindowText, textcolor) - self.checkbox.setPalette(palette) + self.active = active def enterEvent(self, e): self.menu.update_active_action_for_widget(self) @@ -125,6 +112,18 @@ class CollectionMenuItem(QtWidgets.QWidget): def leaveEvent(self, e): self.set_active(False) + def paintEvent(self, e): + painter = QtWidgets.QStylePainter(self) + option = QtWidgets.QStyleOptionMenuItem() + option.initFrom(self) + option.state = QtWidgets.QStyle.State_None + if self.isEnabled(): + option.state |= QtWidgets.QStyle.State_Enabled + if self.active: + option.state |= QtWidgets.QStyle.State_Selected + option.text = self.checkbox.label() + painter.drawControl(QtWidgets.QStyle.CE_MenuItem, option) + class CollectionCheckBox(QtWidgets.QCheckBox): @@ -159,3 +158,10 @@ class CollectionCheckBox(QtWidgets.QCheckBox): def label(self): c = self.collection return ngettext("%s (%i release)", "%s (%i releases)", c.size) % (c.name, c.size) + + def paintEvent(self, e): + painter = QtWidgets.QStylePainter(self) + option = QtWidgets.QStyleOptionButton() + self.initStyleOption(option) + option.text = None + painter.drawControl(QtWidgets.QStyle.CE_CheckBox, option) From a0aa65cc8a4e26e23fd4863fdf6194a51ea5bd4e Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 13 Nov 2018 23:23:40 +0100 Subject: [PATCH 5/5] PICARD-1060: Use checkbox text rendering again for CollectionsMenu Fixes misplaced menu items on macOS --- picard/ui/collectionmenu.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index b796a1fd7..cb07f3d04 100644 --- a/picard/ui/collectionmenu.py +++ b/picard/ui/collectionmenu.py @@ -91,6 +91,7 @@ class CollectionMenuItem(QtWidgets.QWidget): self.menu = menu self.active = False self._setup_layout(menu, collection) + self._setup_colors() def _setup_layout(self, menu, collection): layout = QtWidgets.QVBoxLayout(self) @@ -103,8 +104,17 @@ class CollectionMenuItem(QtWidgets.QWidget): self.checkbox = CollectionCheckBox(self, menu, collection) layout.addWidget(self.checkbox) + def _setup_colors(self): + palette = self.palette() + self.text_color = palette.text().color() + self.highlight_color = palette.highlightedText().color() + def set_active(self, active): self.active = active + palette = self.palette() + textcolor = self.highlight_color if active else self.text_color + palette.setColor(QtGui.QPalette.WindowText, textcolor) + self.checkbox.setPalette(palette) def enterEvent(self, e): self.menu.update_active_action_for_widget(self) @@ -121,7 +131,6 @@ class CollectionMenuItem(QtWidgets.QWidget): option.state |= QtWidgets.QStyle.State_Enabled if self.active: option.state |= QtWidgets.QStyle.State_Selected - option.text = self.checkbox.label() painter.drawControl(QtWidgets.QStyle.CE_MenuItem, option) @@ -158,10 +167,3 @@ class CollectionCheckBox(QtWidgets.QCheckBox): def label(self): c = self.collection return ngettext("%s (%i release)", "%s (%i releases)", c.size) % (c.name, c.size) - - def paintEvent(self, e): - painter = QtWidgets.QStylePainter(self) - option = QtWidgets.QStyleOptionButton() - self.initStyleOption(option) - option.text = None - painter.drawControl(QtWidgets.QStyle.CE_CheckBox, option)