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) QVBoxLayout, QWidget, QSplitter, QToolButton, QIcon, QPainter, QStyleOption)
from calibre.utils.config import prefs 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, from calibre.constants import (isosx, __appname__, preferred_encoding,
get_version) get_version)
from calibre.gui2 import config, is_widescreen, gprefs, error_dialog, open_url 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.tag_browser.ui import TagBrowserWidget
from calibre.gui2.book_details import BookDetails from calibre.gui2.book_details import BookDetails
from calibre.gui2.notify import get_notifier from calibre.gui2.notify import get_notifier
from calibre.gui2.layout_menu import LayoutMenu
_keep_refs = [] _keep_refs = []
@ -591,7 +592,7 @@ class LayoutMixin(object): # {{{
b.setPopupMode(b.InstantPopup) b.setPopupMode(b.InstantPopup)
b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) b.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
b.setText(_('Layout')), b.setIcon(QIcon(I('config.png'))) 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(_( b.setToolTip(_(
'Show and hide various parts of the calibre main window')) 'Show and hide various parts of the calibre main window'))
self.status_bar.addPermanentWidget(b) self.status_bar.addPermanentWidget(b)
@ -599,13 +600,6 @@ class LayoutMixin(object): # {{{
self.setStatusBar(self.status_bar) self.setStatusBar(self.status_bar)
self.status_bar.update_label.linkActivated.connect(self.update_link_clicked) 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): def finalize_layout(self):
self.status_bar.initialize(self.system_tray_icon) self.status_bar.initialize(self.system_tray_icon)
self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info) 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_()