E-book viewer: A whole new full screen mode, with no toolbars to distract from the text and the ability to set the width of the column of tet via Preferences in the ebook viewer. Fixes #959830 (Feature request for ebook viewer app)

This commit is contained in:
Kovid Goyal 2012-03-20 18:34:24 +05:30
parent c1bb1ab5c6
commit 7658a536cc
4 changed files with 139 additions and 24 deletions

View File

@ -255,7 +255,10 @@
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="3" column="1">
<widget class="QSpinBox" name="max_view_width"> <widget class="QSpinBox" name="max_fs_width">
<property name="toolTip">
<string>Set the maximum width that the book's text and pictures will take when in fullscreen mode. This allows you to read the book text without it becoming too wide.</string>
</property>
<property name="suffix"> <property name="suffix">
<string> px</string> <string> px</string>
</property> </property>
@ -270,10 +273,10 @@
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>Maximum &amp;view width:</string> <string>Maximum text width in &amp;fullscreen:</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>max_view_width</cstring> <cstring>max_fs_width</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -350,7 +353,7 @@
<tabstop>serif_family</tabstop> <tabstop>serif_family</tabstop>
<tabstop>sans_family</tabstop> <tabstop>sans_family</tabstop>
<tabstop>mono_family</tabstop> <tabstop>mono_family</tabstop>
<tabstop>max_view_width</tabstop> <tabstop>max_fs_width</tabstop>
<tabstop>opt_remember_window_size</tabstop> <tabstop>opt_remember_window_size</tabstop>
<tabstop>buttonBox</tabstop> <tabstop>buttonBox</tabstop>
</tabstops> </tabstops>

View File

@ -12,7 +12,7 @@ from PyQt4.Qt import (QSize, QSizePolicy, QUrl, SIGNAL, Qt, QTimer,
QPainter, QPalette, QBrush, QFontDatabase, QDialog, QPainter, QPalette, QBrush, QFontDatabase, QDialog,
QColor, QPoint, QImage, QRegion, QVariant, QIcon, QColor, QPoint, QImage, QRegion, QVariant, QIcon,
QFont, pyqtSignature, QAction, QByteArray, QMenu, QFont, pyqtSignature, QAction, QByteArray, QMenu,
pyqtSignal, QSwipeGesture) pyqtSignal, QSwipeGesture, QApplication)
from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings from PyQt4.QtWebKit import QWebPage, QWebView, QWebSettings
from calibre.utils.config import Config, StringConfig from calibre.utils.config import Config, StringConfig
@ -46,8 +46,10 @@ def config(defaults=None):
help=_('Remember last used window size')) help=_('Remember last used window size'))
c.add_opt('user_css', default='', c.add_opt('user_css', default='',
help=_('Set the user CSS stylesheet. This can be used to customize the look of all books.')) help=_('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
c.add_opt('max_view_width', default=6000, c.add_opt('max_fs_width', default=800,
help=_('Maximum width of the viewer window, in pixels.')) help=_("Set the maximum width that the book's text and pictures will take"
" when in fullscreen mode. This allows you to read the book text"
" without it becoming too wide."))
c.add_opt('fit_images', default=True, c.add_opt('fit_images', default=True,
help=_('Resize images larger than the viewer window to fit inside it')) help=_('Resize images larger than the viewer window to fit inside it'))
c.add_opt('hyphenate', default=False, help=_('Hyphenate text')) c.add_opt('hyphenate', default=False, help=_('Hyphenate text'))
@ -101,7 +103,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
self.standard_font.setCurrentIndex({'serif':0, 'sans':1, 'mono':2}[opts.standard_font]) self.standard_font.setCurrentIndex({'serif':0, 'sans':1, 'mono':2}[opts.standard_font])
self.css.setPlainText(opts.user_css) self.css.setPlainText(opts.user_css)
self.css.setToolTip(_('Set the user CSS stylesheet. This can be used to customize the look of all books.')) self.css.setToolTip(_('Set the user CSS stylesheet. This can be used to customize the look of all books.'))
self.max_view_width.setValue(opts.max_view_width) self.max_fs_width.setValue(opts.max_fs_width)
with zipfile.ZipFile(P('viewer/hyphenate/patterns.zip', with zipfile.ZipFile(P('viewer/hyphenate/patterns.zip',
allow_user_override=False), 'r') as zf: allow_user_override=False), 'r') as zf:
pats = [x.split('.')[0].replace('-', '_') for x in zf.namelist()] pats = [x.split('.')[0].replace('-', '_') for x in zf.namelist()]
@ -144,7 +146,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
c.set('user_css', unicode(self.css.toPlainText())) c.set('user_css', unicode(self.css.toPlainText()))
c.set('remember_window_size', self.opt_remember_window_size.isChecked()) c.set('remember_window_size', self.opt_remember_window_size.isChecked())
c.set('fit_images', self.opt_fit_images.isChecked()) c.set('fit_images', self.opt_fit_images.isChecked())
c.set('max_view_width', int(self.max_view_width.value())) c.set('max_fs_width', int(self.max_fs_width.value()))
c.set('hyphenate', self.hyphenate.isChecked()) c.set('hyphenate', self.hyphenate.isChecked())
c.set('remember_current_page', self.opt_remember_current_page.isChecked()) c.set('remember_current_page', self.opt_remember_current_page.isChecked())
c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked()) c.set('wheel_flips_pages', self.opt_wheel_flips_pages.isChecked())
@ -192,6 +194,8 @@ class Document(QWebPage): # {{{
self.loaded_javascript = False self.loaded_javascript = False
self.js_loader = JavaScriptLoader( self.js_loader = JavaScriptLoader(
dynamic_coffeescript=self.debug_javascript) dynamic_coffeescript=self.debug_javascript)
self.initial_left_margin = self.initial_right_margin = u''
self.in_fullscreen_mode = False
self.setLinkDelegationPolicy(self.DelegateAllLinks) self.setLinkDelegationPolicy(self.DelegateAllLinks)
self.scroll_marks = [] self.scroll_marks = []
@ -239,6 +243,9 @@ class Document(QWebPage): # {{{
self.enable_page_flip = self.page_flip_duration > 0.1 self.enable_page_flip = self.page_flip_duration > 0.1
self.font_magnification_step = opts.font_magnification_step self.font_magnification_step = opts.font_magnification_step
self.wheel_flips_pages = opts.wheel_flips_pages self.wheel_flips_pages = opts.wheel_flips_pages
screen_width = QApplication.desktop().screenGeometry().width()
# Leave some space for the scrollbar and some border
self.max_fs_width = min(opts.max_fs_width, screen_width-50)
def fit_images(self): def fit_images(self):
if self.do_fit_images: if self.do_fit_images:
@ -274,6 +281,30 @@ class Document(QWebPage): # {{{
self.set_bottom_padding(0) self.set_bottom_padding(0)
self.fit_images() self.fit_images()
self.init_hyphenate() self.init_hyphenate()
self.initial_left_margin = unicode(self.javascript(
'document.body.style.marginLeft').toString())
self.initial_right_margin = unicode(self.javascript(
'document.body.style.marginRight').toString())
if self.in_fullscreen_mode:
self.switch_to_fullscreen_mode()
def switch_to_fullscreen_mode(self):
self.in_fullscreen_mode = True
self.javascript('''
var s = document.body.style;
s.maxWidth = "%dpx";
s.marginLeft = "auto";
s.marginRight = "auto";
'''%self.max_fs_width)
def switch_to_window_mode(self):
self.in_fullscreen_mode = False
self.javascript('''
var s = document.body.style;
s.maxWidth = "none";
s.marginLeft = "%s";
s.marginRight = "%s";
'''%(self.initial_left_margin, self.initial_right_margin))
@pyqtSignature("QString") @pyqtSignature("QString")
def debug(self, msg): def debug(self, msg):
@ -581,8 +612,8 @@ class DocumentView(QWebView): # {{{
def config(self, parent=None): def config(self, parent=None):
self.document.do_config(parent) self.document.do_config(parent)
if self.manager is not None: if self.document.in_fullscreen_mode:
self.manager.set_max_width() self.document.switch_to_fullscreen_mode()
self.setFocus(Qt.OtherFocusReason) self.setFocus(Qt.OtherFocusReason)
def bookmark(self): def bookmark(self):
@ -602,6 +633,9 @@ class DocumentView(QWebView): # {{{
menu.insertAction(list(menu.actions())[0], self.search_action) menu.insertAction(list(menu.actions())[0], self.search_action)
menu.addSeparator() menu.addSeparator()
menu.addAction(self.goto_location_action) menu.addAction(self.goto_location_action)
if self.document.in_fullscreen_mode and self.manager is not None:
menu.addSeparator()
menu.addAction(self.manager.toggle_toolbar_action)
menu.exec_(ev.globalPos()) menu.exec_(ev.globalPos())
def lookup(self, *args): def lookup(self, *args):

View File

@ -6,10 +6,10 @@ from functools import partial
from threading import Thread from threading import Thread
from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray, from PyQt4.Qt import (QApplication, Qt, QIcon, QTimer, SIGNAL, QByteArray,
QDoubleSpinBox, QLabel, QTextBrowser, QSize, QDoubleSpinBox, QLabel, QTextBrowser, QPropertyAnimation,
QPainter, QBrush, QColor, QStandardItemModel, QPalette, QPainter, QBrush, QColor, QStandardItemModel, QPalette, QStandardItem,
QStandardItem, QUrl, QRegExpValidator, QRegExp, QLineEdit, QUrl, QRegExpValidator, QRegExp, QLineEdit, QToolButton, QMenu,
QToolButton, QMenu, QInputDialog, QAction, QKeySequence) QInputDialog, QAction, QKeySequence)
from calibre.gui2.viewer.main_ui import Ui_EbookViewer from calibre.gui2.viewer.main_ui import Ui_EbookViewer
from calibre.gui2.viewer.printing import Printing from calibre.gui2.viewer.printing import Printing
@ -55,8 +55,6 @@ class TOC(QStandardItemModel):
self.appendRow(TOCItem(t)) self.appendRow(TOCItem(t))
self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents'))) self.setHorizontalHeaderItem(0, QStandardItem(_('Table of Contents')))
class Worker(Thread): class Worker(Thread):
def run(self): def run(self):
@ -292,6 +290,37 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu) self.tool_bar2.setContextMenuPolicy(Qt.PreventContextMenu)
self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup) self.tool_bar.widgetForAction(self.action_bookmark).setPopupMode(QToolButton.MenuButtonPopup)
self.action_full_screen.setCheckable(True) self.action_full_screen.setCheckable(True)
self.full_screen_label = QLabel('''
<center>
<h1>%s</h1>
<h3>%s</h3>
<h3>%s</h3>
</center>
'''%(_('Full screen mode'),
_('Right click to show controls'),
_('Press Esc to quit')),
self)
self.full_screen_label.setVisible(False)
self.full_screen_label.setStyleSheet('''
QLabel {
text-align: center;
background-color: white;
color: black;
border-width: 1px;
border-style: solid;
border-radius: 20px;
}
''')
self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
self.toggle_toolbar_action.triggered.connect(self.toggle_toolbars)
self.addAction(self.toggle_toolbar_action)
self.full_screen_label_anim = QPropertyAnimation(
self.full_screen_label, 'size')
self.esc_full_screen_action = a = QAction(self)
self.addAction(a)
a.setShortcut(Qt.Key_Escape)
a.setEnabled(False)
a.triggered.connect(self.action_full_screen.trigger)
self.print_menu = QMenu() self.print_menu = QMenu()
self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview')) self.print_menu.addAction(QIcon(I('print-preview.png')), _('Print Preview'))
@ -299,7 +328,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup) self.tool_bar.widgetForAction(self.action_print).setPopupMode(QToolButton.MenuButtonPopup)
self.connect(self.action_print, SIGNAL("triggered(bool)"), partial(self.print_book, preview=False)) self.connect(self.action_print, SIGNAL("triggered(bool)"), partial(self.print_book, preview=False))
self.connect(self.print_menu.actions()[0], SIGNAL("triggered(bool)"), partial(self.print_book, preview=True)) self.connect(self.print_menu.actions()[0], SIGNAL("triggered(bool)"), partial(self.print_book, preview=True))
self.set_max_width()
ca = self.view.copy_action ca = self.view.copy_action
ca.setShortcut(QKeySequence.Copy) ca.setShortcut(QKeySequence.Copy)
self.addAction(ca) self.addAction(ca)
@ -313,6 +341,13 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
w = self.tool_bar.widgetForAction(self.action_open_ebook) w = self.tool_bar.widgetForAction(self.action_open_ebook)
w.setPopupMode(QToolButton.MenuButtonPopup) w.setPopupMode(QToolButton.MenuButtonPopup)
for x in ('tool_bar', 'tool_bar2'):
x = getattr(self, x)
for action in x.actions():
# So that the keyboard shortcuts for these actions will
# continue to function even when the toolbars are hidden
self.addAction(action)
self.restore_state() self.restore_state()
def set_toc_visible(self, yes): def set_toc_visible(self, yes):
@ -339,12 +374,17 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
def closeEvent(self, e): def closeEvent(self, e):
if self.isFullScreen(): if self.isFullScreen():
self.showNormal() self.action_full_screen.trigger()
e.ignore() e.ignore()
return return
self.save_state() self.save_state()
return MainWindow.closeEvent(self, e) return MainWindow.closeEvent(self, e)
def toggle_toolbars(self):
for x in ('tool_bar', 'tool_bar2'):
x = getattr(self, x)
x.setVisible(not x.isVisible())
def save_state(self): def save_state(self):
state = bytearray(self.saveState(self.STATE_VERSION)) state = bytearray(self.saveState(self.STATE_VERSION))
vprefs['viewer_toolbar_state'] = state vprefs['viewer_toolbar_state'] = state
@ -386,11 +426,6 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self._lookup = None self._lookup = None
self.dictionary_view.setHtml(html) self.dictionary_view.setHtml(html)
def set_max_width(self):
from calibre.gui2.viewer.documentview import config
c = config().parse()
self.frame.setMaximumWidth(c.max_view_width)
def get_remember_current_page_opt(self): def get_remember_current_page_opt(self):
from calibre.gui2.viewer.documentview import config from calibre.gui2.viewer.documentview import config
c = config().parse() c = config().parse()
@ -405,6 +440,46 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
else: else:
self.showFullScreen() self.showFullScreen()
def showFullScreen(self):
self.tool_bar.setVisible(False)
self.tool_bar2.setVisible(False)
self._original_frame_margins = (
self.centralwidget.layout().contentsMargins(),
self.frame.layout().contentsMargins())
self.frame.layout().setContentsMargins(0, 0, 0, 0)
self.centralwidget.layout().setContentsMargins(0, 0, 0, 0)
super(EbookViewer, self).showFullScreen()
QTimer.singleShot(10, self.show_full_screen_label)
def show_full_screen_label(self):
f = self.full_screen_label
self.esc_full_screen_action.setEnabled(True)
f.setVisible(True)
height = 200
width = int(0.7*self.view.width())
f.resize(width, height)
f.move((self.view.width() - width)//2, (self.view.height()-height)//2)
a = self.full_screen_label_anim
a.setDuration(500)
a.setStartValue(QSize(width, 0))
a.setEndValue(QSize(width, height))
a.start()
QTimer.singleShot(2750, self.full_screen_label.hide)
self.view.document.switch_to_fullscreen_mode()
def showNormal(self):
self.esc_full_screen_action.setEnabled(False)
self.tool_bar.setVisible(True)
self.tool_bar2.setVisible(True)
self.full_screen_label.setVisible(False)
if hasattr(self, '_original_frame_margins'):
om = self._original_frame_margins
self.centralwidget.layout().setContentsMargins(om[0])
self.frame.layout().setContentsMargins(om[1])
super(EbookViewer, self).showNormal()
self.view.document.switch_to_window_mode()
def goto(self, ref): def goto(self, ref):
if ref: if ref:
tokens = ref.split('.') tokens = ref.split('.')

View File

@ -284,6 +284,9 @@
<property name="text"> <property name="text">
<string>Toggle full screen</string> <string>Toggle full screen</string>
</property> </property>
<property name="toolTip">
<string>Toggle full screen (F11)</string>
</property>
</action> </action>
<action name="action_print"> <action name="action_print">
<property name="icon"> <property name="icon">