Make the layout menu nicer

This commit is contained in:
Kovid Goyal 2017-06-16 16:07:31 +05:30
parent 4cebbfd766
commit b22fdd6d08
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 151 additions and 9 deletions

View File

@ -12,7 +12,7 @@ from PyQt5.Qt import (Qt, QApplication, QStackedWidget, QMenu, QTimer,
QVBoxLayout, QWidget, QSplitter, QToolButton, QIcon, QPainter, QStyleOption)
from calibre.utils.config import prefs
from calibre.utils.icu import sort_key, primary_sort_key
from calibre.utils.icu import sort_key
from calibre.constants import (isosx, __appname__, preferred_encoding,
get_version)
from calibre.gui2 import config, is_widescreen, gprefs, error_dialog, open_url
@ -22,6 +22,7 @@ from calibre.gui2.widgets import Splitter, LayoutButton
from calibre.gui2.tag_browser.ui import TagBrowserWidget
from calibre.gui2.book_details import BookDetails
from calibre.gui2.notify import get_notifier
from calibre.gui2.layout_menu import LayoutMenu
_keep_refs = []
@ -591,7 +592,7 @@ class LayoutMixin(object): # {{{
b.setPopupMode(b.InstantPopup)
b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
b.setText(_('Layout')), b.setIcon(QIcon(I('config.png')))
b.setMenu(QMenu()), b.menu().aboutToShow.connect(self.populate_layout_menu)
b.setMenu(LayoutMenu(self))
b.setToolTip(_(
'Show and hide various parts of the calibre main window'))
self.status_bar.addPermanentWidget(b)
@ -599,13 +600,6 @@ class LayoutMixin(object): # {{{
self.setStatusBar(self.status_bar)
self.status_bar.update_label.linkActivated.connect(self.update_link_clicked)
def populate_layout_menu(self):
m = self.layout_button.menu()
m.clear()
buttons = sorted(self.layout_buttons, key=lambda b:primary_sort_key(b.label))
for b in buttons:
m.addAction(b.icon(), b.text(), b.click)
def finalize_layout(self):
self.status_bar.initialize(self.system_tray_icon)
self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info)

View File

@ -0,0 +1,148 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
from PyQt5.Qt import (
QFontMetrics, QHBoxLayout, QIcon, QMenu, QPainter, QPushButton, QSize,
QSizePolicy, Qt, QWidget, QStyleOption, QStyle)
from calibre.utils.icu import primary_sort_key
ICON_SZ = 64
class LayoutItem(QWidget):
def __init__(self, button, parent=None):
QWidget.__init__(self, parent)
self.mouse_over = False
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.button = button
self.text = button.label
self.setCursor(Qt.PointingHandCursor)
self.fm = QFontMetrics(self.font())
self._bi = self._di = None
@property
def bright_icon(self):
if self._bi is None:
self._bi = self.button.icon().pixmap(ICON_SZ, ICON_SZ)
return self._bi
@property
def dull_icon(self):
if self._di is None:
self._di = self.button.icon().pixmap(ICON_SZ, ICON_SZ, mode=QIcon.Disabled)
return self._di
def event(self, ev):
m = None
et = ev.type()
if et == ev.Enter:
m = True
elif et == ev.Leave:
m = False
if m is not None and m != self.mouse_over:
self.mouse_over = m
self.update()
return QWidget.event(self, ev)
def sizeHint(self):
br = self.fm.boundingRect(self.text)
w = max(br.width(), ICON_SZ) + 10
h = 2 * self.fm.lineSpacing() + ICON_SZ + 8
return QSize(w, h)
def paintEvent(self, ev):
shown = self.button.isChecked()
ls = self.fm.lineSpacing()
painter = QPainter(self)
if self.mouse_over:
tool = QStyleOption()
tool.rect = self.rect()
tool.state = QStyle.State_Raised | QStyle.State_Active | QStyle.State_MouseOver
s = self.style()
s.drawPrimitive(QStyle.PE_PanelButtonTool, tool, painter, self)
painter.drawText(
0, 0,
self.width(),
ls, Qt.AlignCenter | Qt.TextSingleLine, self.text)
text = _('Hide') if shown else _('Show')
f = self.font()
f.setBold(True)
painter.setFont(f)
painter.drawText(
0, self.height() - ls,
self.width(),
ls, Qt.AlignCenter | Qt.TextSingleLine, text)
x = (self.width() - ICON_SZ) // 2
y = ls + (self.height() - ICON_SZ - 2 * ls) // 2
pmap = self.bright_icon if shown else self.dull_icon
painter.drawPixmap(x, y, pmap)
painter.end()
class LayoutMenu(QMenu):
def __init__(self, parent=None):
QMenu.__init__(self, parent)
self.l = l = QHBoxLayout(self)
l.setSpacing(20)
self.items = []
if parent is None:
buttons = [
QPushButton(QIcon(I(i + '.png')), i, self)
for i in 'search tags cover_flow grid book'.split()]
for b in buttons:
b.setVisible(False), b.setCheckable(True), b.setChecked(b.text() in 'tags grid')
b.label = b.text().capitalize()
else:
buttons = sorted(
parent.layout_buttons, key=lambda b: primary_sort_key(b.label))
for b in buttons:
self.items.append(LayoutItem(b, self))
l.addWidget(self.items[-1])
self.current_item = None
def sizeHint(self):
return QWidget.sizeHint(self)
def paintEvent(self, ev):
return QWidget.paintEvent(self, ev)
def item_for_ev(self, ev):
for item in self.items:
if item.geometry().contains(ev.pos()):
return item
def mousePressEvent(self, ev):
if ev.button() != Qt.LeftButton:
ev.ignore()
return
if (ev.pos().isNull() and not ev.screenPos().isNull()) or not self.rect().contains(ev.pos()):
self.hide()
self.current_item = self.item_for_ev(ev)
if self.current_item is not None:
ev.accept()
else:
ev.ignore()
def mouseReleaseEvent(self, ev):
if ev.button() != Qt.LeftButton:
ev.ignore()
return
item = self.item_for_ev(ev)
if item is not None and item is self.current_item:
ev.accept()
self.hide()
item.button.click()
if __name__ == '__main__':
from calibre.gui2 import Application
app = Application([])
w = LayoutMenu()
w.show()
w.exec_()