E-book viewer: Start work on paged display mode

This commit is contained in:
Kovid Goyal 2012-06-19 12:37:36 +05:30
parent 99f410acf8
commit 4ba810a75f
7 changed files with 397 additions and 56 deletions

Binary file not shown.

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>blank</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
</head>
<body>
<div>&nbsp;</div>
</body>
</html>

View File

@ -0,0 +1,203 @@
#!/usr/bin/env coffee
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
###
Copyright 2012, Kovid Goyal <kovid@kovidgoyal.net>
Released under the GPLv3 License
###
log = (args...) -> # {{{
if args
msg = args.join(' ')
if window?.console?.log
window.console.log(msg)
else if process?.stdout?.write
process.stdout.write(msg + '\n')
# }}}
body_height = () ->
db = document.body
dde = document.documentElement
if db? and dde?
return Math.max(db.scrollHeight, dde.scrollHeight, db.offsetHeight,
dde.offsetHeight, db.clientHeight, dde.clientHeight)
return 0
absleft = (elem) ->
r = elem.getBoundingClientRect()
return r.left + window.pageXOffset
class PagedDisplay
###
This class is a namespace to expose functions via the
window.paged_display object. The most important functions are:
layout(): causes the currently loaded document to be laid out in columns.
###
constructor: () ->
this.set_geometry()
this.page_width = 0
this.screen_width = 0
this.in_paged_mode = false
this.current_margin_side = 0
set_geometry: (cols_per_screen=2, margin_top=20, margin_side=40, margin_bottom=20) ->
this.margin_top = margin_top
this.margin_side = margin_side
this.margin_bottom = margin_bottom
this.cols_per_screen = cols_per_screen
layout: () ->
ww = window.innerWidth
wh = window.innerHeight
body_height = wh - this.margin_bottom = this.margin_top
n = this.cols_per_screen
# Calculate the column width so that cols_per_screen columns fit in the
# window in such a way the right margin of the last column is <=
# side_margin (it may be less if the window width is not a
# multiple of n*(col_width+2*side_margin).
adjust = ww - Math.floor(ww/n)*n
# Ensure that the margins are large enough that the adjustment does not
# cause them to become negative semidefinite
sm = Math.max(2*adjust, this.margin_side)
# Minimum column width, for the cases when the window is too
# narrow
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
this.page_width = col_width + 2*sm
this.screen_width = this.page_width * this.cols_per_screen
body_style = window.getComputedStyle(document.body)
fgcolor = body_style.getPropertyValue('color')
bs = document.body.style
bs.setProperty('-webkit-column-gap', (2*sm)+'px')
bs.setProperty('-webkit-column-width', col_width+'px')
bs.setProperty('-webkit-column-rule-color', fgcolor)
bs.setProperty('overflow', 'visible')
bs.setProperty('height', 'auto')
bs.setProperty('width', 'auto')
bs.setProperty('margin-top', this.margin_top+'px')
bs.setProperty('margin-bottom', this.margin_bottom+'px')
bs.setProperty('margin-left', sm+'px')
bs.setProperty('margin-right', sm+'px')
for edge in ['left', 'right', 'top', 'bottom']
bs.setProperty('padding-'+edge, '0px')
bs.setProperty('border-'+edge+'-width', '0px')
bs.setProperty('min-width', '0')
bs.setProperty('max-width', 'none')
bs.setProperty('min-height', '0')
bs.setProperty('max-height', 'none')
# Ensure that the top margin is correct, otherwise for some documents,
# webkit lays out the body with a lot of space on top
brect = document.body.getBoundingClientRect()
if brect.top > this.margin_top
bs.setProperty('margin-top', (this.margin_top - brect.top)+'px')
brect = document.body.getBoundingClientRect()
this.in_paged_mode = true
this.current_margin_side = sm
return sm
scroll_to_pos: (frac) ->
# Scroll to the position represented by frac (number between 0 and 1)
xpos = Math.floor(document.body.scrollWidth * frac)
this.scroll_to_xpos(xpos)
scroll_to_xpos: (xpos) ->
# Scroll so that the column containing xpos is the left most column in
# the viewport
pos = 0
until (pos <= xpos < pos + this.page_width)
pos += this.page_width
limit = document.body.scrollWidth - this.screen_width
pos = limit if pos > limit
window.scrollTo(pos, 0)
current_pos: (frac) ->
# The current scroll position as a fraction between 0 and 1
limit = document.body.scrollWidth - window.innerWidth
if limit <= 0
return 0.0
return window.pageXOffset / limit
current_column_location: () ->
# The location of the left edge of the left most column currently
# visible in the viewport
x = window.pageXOffset + Math.max(10, this.current_margin_side)
edge = Math.floor(x/this.page_width)
while edge < x
edge += this.page_width
return edge - this.page_width
next_screen_location: () ->
# The position to scroll to for the next screen (which could contain
# more than one pages). Returns -1 if no further scrolling is possible.
cc = this.current_column_location()
ans = cc + this.screen_width
limit = document.body.scrollWidth - window.innerWidth
if ans > limit
ans = if window.pageXOffset < limit then limit else -1
return ans
previous_screen_location: () ->
# The position to scroll to for the previous screen (which could contain
# more than one pages). Returns -1 if no further scrolling is possible.
cc = this.current_column_location()
ans = cc - this.screen_width
if ans < 0
# We ignore small scrolls (less than 15px) when going to previous
# screen
ans = if window.pageXOffset > 15 then 0 else -1
return ans
next_col_location: () ->
# The position to scroll to for the next column (same as
# next_screen_location() if columns per screen == 1). Returns -1 if no
# further scrolling is possible.
cc = this.current_column_location()
ans = cc + this.page_width
limit = document.body.scrollWidth - window.innerWidth
if ans > limit
ans = if window.pageXOffset < limit then limit else -1
return ans
previous_col_location: () ->
# The position to scroll to for the previous column (same as
# previous_screen_location() if columns per screen == 1). Returns -1 if
# no further scrolling is possible.
cc = this.current_column_location()
ans = cc - this.page_width
if ans < 0
ans = if window.pageXOffset > 0 then 0 else -1
return ans
jump_to_anchor: (name) ->
elem = document.getElementById(name)
if !elem
elems = document.getElementsByName(name)
if elems
elem = elems[0]
if !elem
return
elem.scrollIntoView()
if this.in_paged_mode
# Ensure we are scrolled to the column containing elem
this.scroll_to_xpos(absleft(elem)+10)
if window?
window.paged_display = new PagedDisplay()
# TODO:
# jump_to_anchor() and handling of internal links in the same flow and
# different flows
# css pagebreak rules
# CFI and bookmarks
# Go to reference positions
# Indexing
# Resizing of images
# Special handling for identifiable covers (colspan)?
# Full screen mode
# Resizing of viewport

View File

@ -31,12 +31,15 @@ def self_closing_sub(match):
return '<%s %s></%s>'%(match.group(1), match.group(2), match.group(1)) return '<%s %s></%s>'%(match.group(1), match.group(2), match.group(1))
def load_html(path, view, codec='utf-8', mime_type=None, def load_html(path, view, codec='utf-8', mime_type=None,
pre_load_callback=lambda x:None): pre_load_callback=lambda x:None, path_is_html=False):
from PyQt4.Qt import QUrl, QByteArray from PyQt4.Qt import QUrl, QByteArray
if mime_type is None: if mime_type is None:
mime_type = guess_type(path)[0] mime_type = guess_type(path)[0]
with open(path, 'rb') as f: if path_is_html:
html = f.read().decode(codec, 'replace') html = path
else:
with open(path, 'rb') as f:
html = f.read().decode(codec, 'replace')
html = EntityDeclarationProcessor(html).processed_html html = EntityDeclarationProcessor(html).processed_html
has_svg = re.search(r'<[:a-zA-Z]*svg', html) is not None has_svg = re.search(r'<[:a-zA-Z]*svg', html) is not None

View File

@ -22,7 +22,7 @@ from calibre.gui2.viewer.javascript import JavaScriptLoader
from calibre.gui2.viewer.position import PagePosition from calibre.gui2.viewer.position import PagePosition
from calibre.gui2.viewer.config import config, ConfigDialog from calibre.gui2.viewer.config import config, ConfigDialog
from calibre.ebooks.oeb.display.webview import load_html from calibre.ebooks.oeb.display.webview import load_html
from calibre.utils.config import tweaks
# }}} # }}}
def load_builtin_fonts(): def load_builtin_fonts():
@ -59,10 +59,12 @@ class Document(QWebPage): # {{{
def __init__(self, shortcuts, parent=None, debug_javascript=False): def __init__(self, shortcuts, parent=None, debug_javascript=False):
QWebPage.__init__(self, parent) QWebPage.__init__(self, parent)
self.setObjectName("py_bridge") self.setObjectName("py_bridge")
self.in_paged_mode = tweaks.get('viewer_test_paged_mode', False)
# Use this to pass arbitrary JSON encodable objects between python and # Use this to pass arbitrary JSON encodable objects between python and
# javascript. In python get/set the value as: self.bridge_value. In # javascript. In python get/set the value as: self.bridge_value. In
# javascript, get/set the value as: py_bridge.value # javascript, get/set the value as: py_bridge.value
self.bridge_value = None self.bridge_value = None
self.first_load = True
self.debug_javascript = debug_javascript self.debug_javascript = debug_javascript
self.anchor_positions = {} self.anchor_positions = {}
@ -104,6 +106,13 @@ class Document(QWebPage): # {{{
self.mainFrame().javaScriptWindowObjectCleared.connect( self.mainFrame().javaScriptWindowObjectCleared.connect(
self.add_window_objects) self.add_window_objects)
self.turn_off_internal_scrollbars()
def turn_off_internal_scrollbars(self):
mf = self.mainFrame()
mf.setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
mf.setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
def set_user_stylesheet(self): def set_user_stylesheet(self):
raw = config().parse().user_css raw = config().parse().user_css
raw = '::selection {background:#ffff00; color:#000;}\nbody {background-color: white;}\n'+raw raw = '::selection {background:#ffff00; color:#000;}\nbody {background-color: white;}\n'+raw
@ -175,9 +184,12 @@ class Document(QWebPage): # {{{
'document.body.style.marginLeft').toString()) 'document.body.style.marginLeft').toString())
self.initial_right_margin = unicode(self.javascript( self.initial_right_margin = unicode(self.javascript(
'document.body.style.marginRight').toString()) 'document.body.style.marginRight').toString())
if self.in_paged_mode:
self.switch_to_paged_mode()
if self.in_fullscreen_mode: if self.in_fullscreen_mode:
self.switch_to_fullscreen_mode() self.switch_to_fullscreen_mode()
self.read_anchor_positions(use_cache=False) self.read_anchor_positions(use_cache=False)
self.first_load = False
def read_anchor_positions(self, use_cache=True): def read_anchor_positions(self, use_cache=True):
self.bridge_value = tuple(self.index_anchors) self.bridge_value = tuple(self.index_anchors)
@ -190,6 +202,22 @@ class Document(QWebPage): # {{{
self.anchor_positions = {} self.anchor_positions = {}
return self.anchor_positions return self.anchor_positions
def switch_to_paged_mode(self, onresize=False):
side_margin = self.javascript('paged_display.layout()', typ=int)
# Setup the contents size to ensure that there is a right most margin.
# Without this webkit renders the final column with no margin, as the
# columns extend beyond the boundaries (and margin) of body
mf = self.mainFrame()
sz = mf.contentsSize()
if sz.width() > self.window_width:
sz.setWidth(sz.width()+side_margin)
self.setPreferredContentsSize(sz)
def after_resize(self):
if self.in_paged_mode:
self.setPreferredContentsSize(QSize())
self.switch_to_paged_mode(onresize=True)
def switch_to_fullscreen_mode(self): def switch_to_fullscreen_mode(self):
self.in_fullscreen_mode = True self.in_fullscreen_mode = True
self.javascript(''' self.javascript('''
@ -233,20 +261,21 @@ class Document(QWebPage): # {{{
def javascript(self, string, typ=None): def javascript(self, string, typ=None):
ans = self.mainFrame().evaluateJavaScript(string) ans = self.mainFrame().evaluateJavaScript(string)
if typ == 'int': if typ in {'int', int}:
ans = ans.toInt() ans = ans.toInt()
if ans[1]: if ans[1]:
return ans[0] return ans[0]
return 0 return 0
if typ in {'float', float}:
ans = ans.toReal()
return ans[0] if ans[1] else 0.0
if typ == 'string': if typ == 'string':
return unicode(ans.toString()) return unicode(ans.toString())
return ans return ans
def javaScriptConsoleMessage(self, msg, lineno, msgid): def javaScriptConsoleMessage(self, msg, lineno, msgid):
if self.debug_javascript: if self.debug_javascript:
prints( 'JS:', msgid, lineno)
prints(msg) prints(msg)
prints(' ')
else: else:
return QWebPage.javaScriptConsoleMessage(self, msg, lineno, msgid) return QWebPage.javaScriptConsoleMessage(self, msg, lineno, msgid)
@ -263,13 +292,7 @@ class Document(QWebPage): # {{{
self.mainFrame().setScrollPosition(QPoint(x, y)) self.mainFrame().setScrollPosition(QPoint(x, y))
def jump_to_anchor(self, anchor): def jump_to_anchor(self, anchor):
self.javascript('document.location.hash = "%s"'%anchor) self.javascript('paged_display.jump_to_anchor("%s")'%anchor)
def quantize(self):
if self.height > self.window_height:
r = self.height%self.window_height
if r > 0:
self.javascript('document.body.style.paddingBottom = "%dpx"'%r)
def element_ypos(self, elem): def element_ypos(self, elem):
ans, ok = elem.evaluateJavaScript('$(this).offset().top').toInt() ans, ok = elem.evaluateJavaScript('$(this).offset().top').toInt()
@ -314,15 +337,22 @@ class Document(QWebPage): # {{{
@dynamic_property @dynamic_property
def scroll_fraction(self): def scroll_fraction(self):
def fget(self): def fget(self):
try: if self.in_paged_mode:
return abs(float(self.ypos)/(self.height-self.window_height)) return self.javascript('paged_display.current_pos()',
except ZeroDivisionError: typ='float')
return 0. else:
try:
return abs(float(self.ypos)/(self.height-self.window_height))
except ZeroDivisionError:
return 0.
def fset(self, val): def fset(self, val):
npos = val * (self.height - self.window_height) if self.in_paged_mode:
if npos < 0: self.javascript('paged_display.scroll_to_pos(%f)'%val)
npos = 0 else:
self.scroll_to(x=self.xpos, y=npos) npos = val * (self.height - self.window_height)
if npos < 0:
npos = 0
self.scroll_to(x=self.xpos, y=npos)
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
@property @property
@ -363,6 +393,7 @@ class DocumentView(QWebView): # {{{
DISABLED_BRUSH = QBrush(Qt.lightGray, Qt.Dense5Pattern) DISABLED_BRUSH = QBrush(Qt.lightGray, Qt.Dense5Pattern)
def initialize_view(self, debug_javascript=False): def initialize_view(self, debug_javascript=False):
self.setRenderHints(QPainter.Antialiasing|QPainter.TextAntialiasing|QPainter.SmoothPixmapTransform)
self.flipper = SlideFlip(self) self.flipper = SlideFlip(self)
self.is_auto_repeat_event = False self.is_auto_repeat_event = False
self.debug_javascript = debug_javascript self.debug_javascript = debug_javascript
@ -570,7 +601,7 @@ class DocumentView(QWebView): # {{{
if self.manager is not None: if self.manager is not None:
self.manager.load_started() self.manager.load_started()
load_html(path, self, codec=path.encoding, mime_type=getattr(path, load_html(path, self, codec=getattr(path, 'encoding', 'utf-8'), mime_type=getattr(path,
'mime_type', None), pre_load_callback=callback) 'mime_type', None), pre_load_callback=callback)
entries = set() entries = set()
for ie in getattr(path, 'index_entries', []): for ie in getattr(path, 'index_entries', []):
@ -579,10 +610,12 @@ class DocumentView(QWebView): # {{{
if ie.end_anchor: if ie.end_anchor:
entries.add(ie.end_anchor) entries.add(ie.end_anchor)
self.document.index_anchors = entries self.document.index_anchors = entries
self.turn_off_internal_scrollbars()
def initialize_scrollbar(self): def initialize_scrollbar(self):
if getattr(self, 'scrollbar', None) is not None: if getattr(self, 'scrollbar', None) is not None:
if self.document.in_paged_mode:
self.scrollbar.setVisible(False)
return
delta = self.document.width - self.size().width() delta = self.document.width - self.size().width()
if delta > 0: if delta > 0:
self._ignore_scrollbar_signals = True self._ignore_scrollbar_signals = True
@ -623,7 +656,6 @@ class DocumentView(QWebView): # {{{
self.manager.scrolled(self.document.scroll_fraction, self.manager.scrolled(self.document.scroll_fraction,
onload=True) onload=True)
self.turn_off_internal_scrollbars()
if self.flipper.isVisible(): if self.flipper.isVisible():
if self.flipper.running: if self.flipper.running:
self.flipper.setVisible(False) self.flipper.setVisible(False)
@ -631,12 +663,6 @@ class DocumentView(QWebView): # {{{
self.flipper(self.current_page_image(), self.flipper(self.current_page_image(),
duration=self.document.page_flip_duration) duration=self.document.page_flip_duration)
def turn_off_internal_scrollbars(self):
self.document.mainFrame().setScrollBarPolicy(Qt.Vertical, Qt.ScrollBarAlwaysOff)
self.document.mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
@classmethod @classmethod
def test_line(cls, img, y): def test_line(cls, img, y):
'Test if line contains pixels of exactly the same color' 'Test if line contains pixels of exactly the same color'
@ -651,6 +677,7 @@ class DocumentView(QWebView): # {{{
overlap = self.height() overlap = self.height()
img = QImage(self.width(), overlap, QImage.Format_ARGB32_Premultiplied) img = QImage(self.width(), overlap, QImage.Format_ARGB32_Premultiplied)
painter = QPainter(img) painter = QPainter(img)
painter.setRenderHints(self.renderHints())
self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap)) self.document.mainFrame().render(painter, QRegion(0, 0, self.width(), overlap))
painter.end() painter.end()
return img return img
@ -670,6 +697,28 @@ class DocumentView(QWebView): # {{{
return return
epf = self.document.enable_page_flip and not self.is_auto_repeat_event epf = self.document.enable_page_flip and not self.is_auto_repeat_event
if self.document.in_paged_mode:
loc = self.document.javascript(
'paged_display.previous_screen_location()', typ='int')
if loc < 0:
if self.manager is not None:
if epf:
self.flipper.initialize(self.current_page_image(),
forwards=False)
self.manager.previous_document()
else:
if epf:
self.flipper.initialize(self.current_page_image(),
forwards=False)
self.document.scroll_to(x=loc, y=0)
if epf:
self.flipper(self.current_page_image(),
duration=self.document.page_flip_duration)
if self.manager is not None:
self.manager.scrolled(self.scroll_fraction)
return
delta_y = self.document.window_height - 25 delta_y = self.document.window_height - 25
if self.document.at_top: if self.document.at_top:
if self.manager is not None: if self.manager is not None:
@ -700,6 +749,26 @@ class DocumentView(QWebView): # {{{
return return
epf = self.document.enable_page_flip and not self.is_auto_repeat_event epf = self.document.enable_page_flip and not self.is_auto_repeat_event
if self.document.in_paged_mode:
loc = self.document.javascript(
'paged_display.next_screen_location()', typ='int')
if loc < 0:
if self.manager is not None:
if epf:
self.flipper.initialize(self.current_page_image())
self.manager.next_document()
else:
if epf:
self.flipper.initialize(self.current_page_image())
self.document.scroll_to(x=loc, y=0)
if epf:
self.flipper(self.current_page_image(),
duration=self.document.page_flip_duration)
if self.manager is not None:
self.manager.scrolled(self.scroll_fraction)
return
window_height = self.document.window_height window_height = self.document.window_height
document_height = self.document.height document_height = self.document.height
ddelta = document_height - window_height ddelta = document_height - window_height
@ -762,25 +831,38 @@ class DocumentView(QWebView): # {{{
#print 'After all:', self.document.ypos #print 'After all:', self.document.ypos
def scroll_by(self, x=0, y=0, notify=True): def scroll_by(self, x=0, y=0, notify=True):
old_pos = self.document.ypos old_pos = (self.document.xpos if self.document.in_paged_mode else
self.document.ypos)
self.document.scroll_by(x, y) self.document.scroll_by(x, y)
if notify and self.manager is not None and self.document.ypos != old_pos: new_pos = (self.document.xpos if self.document.in_paged_mode else
self.document.ypos)
if notify and self.manager is not None and new_pos != old_pos:
self.manager.scrolled(self.scroll_fraction) self.manager.scrolled(self.scroll_fraction)
def scroll_to(self, pos, notify=True): def scroll_to(self, pos, notify=True):
if self._ignore_scrollbar_signals: if self._ignore_scrollbar_signals:
return return
old_pos = self.document.ypos old_pos = (self.document.xpos if self.document.in_paged_mode else
if isinstance(pos, basestring): self.document.ypos)
self.document.jump_to_anchor(pos) if self.document.in_paged_mode:
else: if isinstance(pos, basestring):
if pos >= 1: self.document.jump_to_anchor(pos)
self.document.scroll_to(0, self.document.height)
else: else:
y = int(math.ceil( self.document.scroll_fraction = pos
pos*(self.document.height-self.document.window_height))) else:
self.document.scroll_to(0, y) if isinstance(pos, basestring):
if notify and self.manager is not None and self.document.ypos != old_pos: self.document.jump_to_anchor(pos)
else:
if pos >= 1:
self.document.scroll_to(0, self.document.height)
else:
y = int(math.ceil(
pos*(self.document.height-self.document.window_height)))
self.document.scroll_to(0, y)
new_pos = (self.document.xpos if self.document.in_paged_mode else
self.document.ypos)
if notify and self.manager is not None and new_pos != old_pos:
self.manager.scrolled(self.scroll_fraction) self.manager.scrolled(self.scroll_fraction)
@dynamic_property @dynamic_property
@ -813,9 +895,8 @@ class DocumentView(QWebView): # {{{
return QWebView.changeEvent(self, event) return QWebView.changeEvent(self, event)
def paintEvent(self, event): def paintEvent(self, event):
self.turn_off_internal_scrollbars()
painter = QPainter(self) painter = QPainter(self)
painter.setRenderHints(self.renderHints())
self.document.mainFrame().render(painter, event.region()) self.document.mainFrame().render(painter, event.region())
if not self.isEnabled(): if not self.isEnabled():
painter.fillRect(event.region().boundingRect(), self.DISABLED_BRUSH) painter.fillRect(event.region().boundingRect(), self.DISABLED_BRUSH)
@ -827,6 +908,18 @@ class DocumentView(QWebView): # {{{
if self.manager is not None and event.delta() != 0: if self.manager is not None and event.delta() != 0:
(self.manager.font_size_larger if event.delta() > 0 else (self.manager.font_size_larger if event.delta() > 0 else
self.manager.font_size_smaller)() self.manager.font_size_smaller)()
return
if self.document.in_paged_mode:
if abs(event.delta()) < 15: return
typ = 'screen' if self.document.wheel_flips_pages else 'col'
direction = 'next' if event.delta() < 0 else 'previous'
loc = self.document.javascript('paged_display.%s_%s_location()'%(
direction, typ), typ='int')
if loc > -1:
self.document.scroll_to(x=loc, y=0)
return
if event.delta() < -14: if event.delta() < -14:
if self.document.wheel_flips_pages: if self.document.wheel_flips_pages:
self.next_page() self.next_page()
@ -866,6 +959,17 @@ class DocumentView(QWebView): # {{{
if not self.handle_key_press(event): if not self.handle_key_press(event):
return QWebView.keyPressEvent(self, event) return QWebView.keyPressEvent(self, event)
def paged_col_scroll(self, forward=True):
dir = 'next' if forward else 'previous'
loc = self.document.javascript(
'paged_display.%s_col_location()'%dir, typ='int')
if loc > -1:
self.document.scroll_to(x=loc, y=0)
self.manager.scrolled(self.document.scroll_fraction)
else:
(self.manager.next_document() if forward else
self.manager.previous_document())
def handle_key_press(self, event): def handle_key_press(self, event):
handled = True handled = True
key = self.shortcuts.get_match(event) key = self.shortcuts.get_match(event)
@ -877,21 +981,33 @@ class DocumentView(QWebView): # {{{
finally: finally:
self.is_auto_repeat_event = False self.is_auto_repeat_event = False
elif key == 'Down': elif key == 'Down':
if (not self.document.line_scrolling_stops_on_pagebreaks and if self.document.in_paged_mode:
self.document.at_bottom): self.paged_col_scroll()
self.manager.next_document()
else: else:
self.scroll_by(y=15) if (not self.document.line_scrolling_stops_on_pagebreaks and
self.document.at_bottom):
self.manager.next_document()
else:
self.scroll_by(y=15)
elif key == 'Up': elif key == 'Up':
if (not self.document.line_scrolling_stops_on_pagebreaks and if self.document.in_paged_mode:
self.document.at_top): self.paged_col_scroll(forward=False)
self.manager.previous_document()
else: else:
self.scroll_by(y=-15) if (not self.document.line_scrolling_stops_on_pagebreaks and
self.document.at_top):
self.manager.previous_document()
else:
self.scroll_by(y=-15)
elif key == 'Left': elif key == 'Left':
self.scroll_by(x=-15) if self.document.in_paged_mode:
self.paged_col_scroll(forward=False)
else:
self.scroll_by(x=-15)
elif key == 'Right': elif key == 'Right':
self.scroll_by(x=15) if self.document.in_paged_mode:
self.paged_col_scroll()
else:
self.scroll_by(x=15)
else: else:
handled = False handled = False
return handled return handled

View File

@ -30,10 +30,11 @@ class JavaScriptLoader(object):
CS = { CS = {
'cfi':'ebooks.oeb.display.cfi', 'cfi':'ebooks.oeb.display.cfi',
'indexing':'ebooks.oeb.display.indexing', 'indexing':'ebooks.oeb.display.indexing',
'paged':'ebooks.oeb.display.paged',
} }
ORDER = ('jquery', 'jquery_scrollTo', 'bookmarks', 'referencing', 'images', ORDER = ('jquery', 'jquery_scrollTo', 'bookmarks', 'referencing', 'images',
'hyphenation', 'hyphenator', 'cfi', 'indexing',) 'hyphenation', 'hyphenator', 'cfi', 'indexing', 'paged')
def __init__(self, dynamic_coffeescript=False): def __init__(self, dynamic_coffeescript=False):

View File

@ -747,6 +747,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
# There hasn't been a resize event for some time # There hasn't been a resize event for some time
# restore the current page position. # restore the current page position.
self.resize_in_progress = False self.resize_in_progress = False
self.view.document.after_resize()
if self.window_mode_changed: if self.window_mode_changed:
# This resize is part of a window mode change, special case it # This resize is part of a window mode change, special case it
self.handle_window_mode_toggle() self.handle_window_mode_toggle()
@ -1003,6 +1004,12 @@ def main(args=sys.argv):
QApplication.setApplicationName(APP_UID) QApplication.setApplicationName(APP_UID)
main = EbookViewer(args[1] if len(args) > 1 else None, main = EbookViewer(args[1] if len(args) > 1 else None,
debug_javascript=opts.debug_javascript, open_at=open_at) debug_javascript=opts.debug_javascript, open_at=open_at)
# This is needed for paged mode. Without it, the first document that is
# loaded will have extra blank space at the bottom, as
# turn_off_internal_scrollbars does not take effect for the first
# rendered document
main.view.load_path(P('viewer/blank.html', allow_user_override=False))
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
main.show() main.show()
if opts.raise_window: if opts.raise_window: