diff --git a/picard/ui/collectionmenu.py b/picard/ui/collectionmenu.py index 14fa7e380..cb07f3d04 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 @@ -21,6 +22,7 @@ import locale from PyQt5 import ( QtCore, + QtGui, QtWidgets, ) @@ -35,18 +37,24 @@ 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(), 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._ignore_update = False 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 +65,81 @@ 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): + 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) + + +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) + style = self.style() + 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) + + 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) + + 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 + painter.drawControl(QtWidgets.QStyle.CE_MenuItem, option) + 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):