mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Implement full screen
This commit is contained in:
parent
146b5dc6e3
commit
0c7bcb0f8c
@ -169,7 +169,7 @@ def main(args=sys.argv):
|
|||||||
if opts.raise_window:
|
if opts.raise_window:
|
||||||
main.raise_()
|
main.raise_()
|
||||||
if opts.full_screen:
|
if opts.full_screen:
|
||||||
main.showFullScreen()
|
main.set_full_screen(True)
|
||||||
|
|
||||||
app.exec_()
|
app.exec_()
|
||||||
if listener is not None:
|
if listener is not None:
|
||||||
|
@ -11,7 +11,9 @@ from collections import defaultdict
|
|||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt5.Qt import QDockWidget, QModelIndex, Qt, QVBoxLayout, QWidget, pyqtSignal
|
from PyQt5.Qt import (
|
||||||
|
QDockWidget, QEvent, QModelIndex, Qt, QVBoxLayout, QWidget, pyqtSignal
|
||||||
|
)
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
@ -43,6 +45,7 @@ class EbookViewer(MainWindow):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
MainWindow.__init__(self, None)
|
MainWindow.__init__(self, None)
|
||||||
|
self.in_full_screen_mode = None
|
||||||
try:
|
try:
|
||||||
os.makedirs(annotations_dir)
|
os.makedirs(annotations_dir)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
@ -73,11 +76,13 @@ class EbookViewer(MainWindow):
|
|||||||
self.web_view.reload_book.connect(self.reload_book)
|
self.web_view.reload_book.connect(self.reload_book)
|
||||||
self.web_view.toggle_toc.connect(self.toggle_toc)
|
self.web_view.toggle_toc.connect(self.toggle_toc)
|
||||||
self.web_view.update_current_toc_nodes.connect(self.toc.update_current_toc_nodes)
|
self.web_view.update_current_toc_nodes.connect(self.toc.update_current_toc_nodes)
|
||||||
|
self.web_view.toggle_full_screen.connect(self.toggle_full_screen)
|
||||||
self.setCentralWidget(self.web_view)
|
self.setCentralWidget(self.web_view)
|
||||||
state = vprefs['main_window_state']
|
state = vprefs['main_window_state']
|
||||||
if state:
|
if state:
|
||||||
self.restoreState(state, self.MAIN_WINDOW_STATE_VERSION)
|
self.restoreState(state, self.MAIN_WINDOW_STATE_VERSION)
|
||||||
|
|
||||||
|
# IPC {{{
|
||||||
def handle_commandline_arg(self, arg):
|
def handle_commandline_arg(self, arg):
|
||||||
if arg:
|
if arg:
|
||||||
if os.path.isfile(arg) and os.access(arg, os.R_OK):
|
if os.path.isfile(arg) and os.access(arg, os.R_OK):
|
||||||
@ -92,7 +97,28 @@ class EbookViewer(MainWindow):
|
|||||||
return
|
return
|
||||||
self.load_ebook(path, open_at=open_at)
|
self.load_ebook(path, open_at=open_at)
|
||||||
self.raise_()
|
self.raise_()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Fullscreen {{{
|
||||||
|
def set_full_screen(self, on):
|
||||||
|
if on:
|
||||||
|
self.showFullScreen()
|
||||||
|
else:
|
||||||
|
self.showNormal()
|
||||||
|
|
||||||
|
def changeEvent(self, ev):
|
||||||
|
if ev.type() == QEvent.WindowStateChange:
|
||||||
|
in_full_screen_mode = self.isFullScreen()
|
||||||
|
if self.in_full_screen_mode is None or self.in_full_screen_mode != in_full_screen_mode:
|
||||||
|
self.in_full_screen_mode = in_full_screen_mode
|
||||||
|
self.web_view.notify_full_screen_state_change(self.in_full_screen_mode)
|
||||||
|
return MainWindow.changeEvent(self, ev)
|
||||||
|
|
||||||
|
def toggle_full_screen(self):
|
||||||
|
self.set_full_screen(not self.isFullScreen())
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# ToC {{{
|
||||||
def toggle_toc(self):
|
def toggle_toc(self):
|
||||||
if self.toc_dock.isVisible():
|
if self.toc_dock.isVisible():
|
||||||
self.toc_dock.setVisible(False)
|
self.toc_dock.setVisible(False)
|
||||||
@ -106,6 +132,9 @@ class EbookViewer(MainWindow):
|
|||||||
def toc_searched(self, index):
|
def toc_searched(self, index):
|
||||||
item = self.toc_model.itemFromIndex(index)
|
item = self.toc_model.itemFromIndex(index)
|
||||||
self.web_view.goto_toc_node(item.node_id)
|
self.web_view.goto_toc_node(item.node_id)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# Load book {{{
|
||||||
|
|
||||||
def load_ebook(self, pathtoebook, open_at=None, reload_book=False):
|
def load_ebook(self, pathtoebook, open_at=None, reload_book=False):
|
||||||
# TODO: Implement open_at
|
# TODO: Implement open_at
|
||||||
@ -166,7 +195,9 @@ class EbookViewer(MainWindow):
|
|||||||
with open(path, 'rb') as f:
|
with open(path, 'rb') as f:
|
||||||
raw = f.read()
|
raw = f.read()
|
||||||
merge_annotations(parse_annotations(raw), amap)
|
merge_annotations(parse_annotations(raw), amap)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# CFI management {{{
|
||||||
def initial_cfi_for_current_book(self):
|
def initial_cfi_for_current_book(self):
|
||||||
lrp = self.current_book_data['annotations_map']['last-read']
|
lrp = self.current_book_data['annotations_map']['last-read']
|
||||||
if lrp:
|
if lrp:
|
||||||
@ -179,7 +210,9 @@ class EbookViewer(MainWindow):
|
|||||||
return
|
return
|
||||||
self.current_book_data['annotations_map']['last-read'] = [{
|
self.current_book_data['annotations_map']['last-read'] = [{
|
||||||
'pos': cfi, 'pos_type': 'epubcfi', 'timestamp': utcnow()}]
|
'pos': cfi, 'pos_type': 'epubcfi', 'timestamp': utcnow()}]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# State serialization {{{
|
||||||
def save_annotations(self):
|
def save_annotations(self):
|
||||||
if not self.current_book_data:
|
if not self.current_book_data:
|
||||||
return
|
return
|
||||||
@ -201,3 +234,4 @@ class EbookViewer(MainWindow):
|
|||||||
self.save_annotations()
|
self.save_annotations()
|
||||||
self.save_state()
|
self.save_state()
|
||||||
return MainWindow.closeEvent(self, ev)
|
return MainWindow.closeEvent(self, ev)
|
||||||
|
# }}}
|
||||||
|
@ -171,11 +171,13 @@ class ViewerBridge(Bridge):
|
|||||||
reload_book = from_js()
|
reload_book = from_js()
|
||||||
toggle_toc = from_js()
|
toggle_toc = from_js()
|
||||||
update_current_toc_nodes = from_js(object, object)
|
update_current_toc_nodes = from_js(object, object)
|
||||||
|
toggle_full_screen = from_js()
|
||||||
|
|
||||||
create_view = to_js()
|
create_view = to_js()
|
||||||
show_preparing_message = to_js()
|
show_preparing_message = to_js()
|
||||||
start_book_load = to_js()
|
start_book_load = to_js()
|
||||||
goto_toc_node = to_js()
|
goto_toc_node = to_js()
|
||||||
|
full_screen_state_changed = to_js()
|
||||||
|
|
||||||
|
|
||||||
class WebPage(QWebEnginePage):
|
class WebPage(QWebEnginePage):
|
||||||
@ -252,6 +254,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
reload_book = pyqtSignal()
|
reload_book = pyqtSignal()
|
||||||
toggle_toc = pyqtSignal()
|
toggle_toc = pyqtSignal()
|
||||||
update_current_toc_nodes = pyqtSignal(object, object)
|
update_current_toc_nodes = pyqtSignal(object, object)
|
||||||
|
toggle_full_screen = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self._host_widget = None
|
self._host_widget = None
|
||||||
@ -267,6 +270,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
self.bridge.reload_book.connect(self.reload_book)
|
self.bridge.reload_book.connect(self.reload_book)
|
||||||
self.bridge.toggle_toc.connect(self.toggle_toc)
|
self.bridge.toggle_toc.connect(self.toggle_toc)
|
||||||
self.bridge.update_current_toc_nodes.connect(self.update_current_toc_nodes)
|
self.bridge.update_current_toc_nodes.connect(self.update_current_toc_nodes)
|
||||||
|
self.bridge.toggle_full_screen.connect(self.toggle_full_screen)
|
||||||
self.pending_bridge_ready_actions = {}
|
self.pending_bridge_ready_actions = {}
|
||||||
self.setPage(self._page)
|
self.setPage(self._page)
|
||||||
self.setAcceptDrops(False)
|
self.setAcceptDrops(False)
|
||||||
@ -343,6 +347,9 @@ class WebView(RestartingWebEngineView):
|
|||||||
def goto_toc_node(self, node_id):
|
def goto_toc_node(self, node_id):
|
||||||
self.execute_when_ready('goto_toc_node', node_id)
|
self.execute_when_ready('goto_toc_node', node_id)
|
||||||
|
|
||||||
|
def notify_full_screen_state_change(self, in_fullscreen_mode):
|
||||||
|
self.execute_when_ready('full_screen_state_changed', in_fullscreen_mode)
|
||||||
|
|
||||||
def set_session_data(self, key, val):
|
def set_session_data(self, key, val):
|
||||||
if key == '*' and val is None:
|
if key == '*' and val is None:
|
||||||
vprefs['session_data'] = {}
|
vprefs['session_data'] = {}
|
||||||
|
@ -62,7 +62,8 @@ register_callback(def():
|
|||||||
)
|
)
|
||||||
|
|
||||||
runtime = {
|
runtime = {
|
||||||
'is_standalone_viewer': False
|
'is_standalone_viewer': False,
|
||||||
|
'viewer_in_full_screen': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -216,17 +216,18 @@ class MainOverlay:
|
|||||||
sync_action = ac(_('Sync'), _('Get last read position and annotations from the server'), self.overlay.sync_book, 'cloud-download')
|
sync_action = ac(_('Sync'), _('Get last read position and annotations from the server'), self.overlay.sync_book, 'cloud-download')
|
||||||
delete_action = ac(_('Delete'), _('Delete this book from the device'), self.overlay.delete_book, 'trash')
|
delete_action = ac(_('Delete'), _('Delete this book from the device'), self.overlay.delete_book, 'trash')
|
||||||
reload_action = ac(_('Reload'), _('Reload this book from the {}').format( _('computer') if runtime.is_standalone_viewer else _('server')), self.overlay.reload_book, 'refresh')
|
reload_action = ac(_('Reload'), _('Reload this book from the {}').format( _('computer') if runtime.is_standalone_viewer else _('server')), self.overlay.reload_book, 'refresh')
|
||||||
|
home_action = ac(_('Home'), _('Return to list of books'), def(): home();, 'home')
|
||||||
|
back_action = ac(_('Back'), None, self.back, 'arrow-left')
|
||||||
|
forward_action = ac(_('Forward'), None, self.forward, 'arrow-right')
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
reload_actions = E.ul(reload_action)
|
reload_actions = E.ul(reload_action)
|
||||||
|
nav_actions = E.ul(back_action, forward_action)
|
||||||
else:
|
else:
|
||||||
reload_actions = E.ul(sync_action, delete_action, reload_action)
|
reload_actions = E.ul(sync_action, delete_action, reload_action)
|
||||||
|
nav_actions = E.ul(home_action, back_action, forward_action)
|
||||||
|
|
||||||
actions_div = E.div( # actions
|
actions_div = E.div( # actions
|
||||||
E.ul(
|
nav_actions,
|
||||||
ac(_('Home'), _('Return to list of books'), def(): home();, 'home'),
|
|
||||||
ac(_('Back'), None, self.back, 'arrow-left'),
|
|
||||||
ac(_('Forward'), None, self.forward, 'arrow-right'),
|
|
||||||
),
|
|
||||||
|
|
||||||
E.ul(
|
E.ul(
|
||||||
ac(_('Search'), _('Search for text in this book'), self.overlay.show_search, 'search'),
|
ac(_('Search'), _('Search for text in this book'), self.overlay.show_search, 'search'),
|
||||||
@ -247,12 +248,19 @@ class MainOverlay:
|
|||||||
|
|
||||||
class_=MAIN_OVERLAY_ACTIONS_CLASS
|
class_=MAIN_OVERLAY_ACTIONS_CLASS
|
||||||
)
|
)
|
||||||
if not full_screen_element() and not is_ios:
|
full_screen_actions = []
|
||||||
# No fullscreen on iOS, see http://caniuse.com/#search=fullscreen
|
if runtime.is_standalone_viewer:
|
||||||
actions_div.appendChild(
|
text = _('Exit full screen') if runtime.viewer_in_full_screen else _('Enter full screen')
|
||||||
E.ul(
|
full_screen_actions.push(
|
||||||
ac(_('Full screen'), _('Enter full screen mode'), def(): request_full_screen(), self.overlay.hide();, 'full-screen'),
|
ac(text, '', def(): self.overlay.hide(), ui_operations.toggle_full_screen();, 'full-screen'))
|
||||||
))
|
else:
|
||||||
|
if not full_screen_element() and not is_ios:
|
||||||
|
# No fullscreen on iOS, see http://caniuse.com/#search=fullscreen
|
||||||
|
full_screen_actions.push(
|
||||||
|
ac(_('Full screen'), _('Enter full screen mode'), def(): request_full_screen(), self.overlay.hide();, 'full-screen')
|
||||||
|
)
|
||||||
|
if full_screen_actions.length:
|
||||||
|
actions_div.appendChild(E.ul(*full_screen_actions))
|
||||||
|
|
||||||
container.appendChild(set_css(E.div(class_=MAIN_OVERLAY_TS_CLASS, # top section
|
container.appendChild(set_css(E.div(class_=MAIN_OVERLAY_TS_CLASS, # top section
|
||||||
onclick=def (evt):evt.stopPropagation();,
|
onclick=def (evt):evt.stopPropagation();,
|
||||||
|
@ -198,6 +198,11 @@ def goto_toc_node(node_id):
|
|||||||
view.goto_toc_node(node_id)
|
view.goto_toc_node(node_id)
|
||||||
|
|
||||||
|
|
||||||
|
@from_python
|
||||||
|
def full_screen_state_changed(viewer_in_full_screen):
|
||||||
|
runtime.viewer_in_full_screen = viewer_in_full_screen
|
||||||
|
|
||||||
|
|
||||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||||
if not error_object:
|
if not error_object:
|
||||||
# cross domain error
|
# cross domain error
|
||||||
@ -246,6 +251,8 @@ if window is window.top:
|
|||||||
to_python.toggle_toc()
|
to_python.toggle_toc()
|
||||||
ui_operations.update_current_toc_nodes = def(current_node_id, top_level_node_id):
|
ui_operations.update_current_toc_nodes = def(current_node_id, top_level_node_id):
|
||||||
to_python.update_current_toc_nodes(current_node_id, top_level_node_id)
|
to_python.update_current_toc_nodes(current_node_id, top_level_node_id)
|
||||||
|
ui_operations.toggle_full_screen = def():
|
||||||
|
to_python.toggle_full_screen()
|
||||||
document.body.appendChild(E.div(id='view'))
|
document.body.appendChild(E.div(id='view'))
|
||||||
window.onerror = onerror
|
window.onerror = onerror
|
||||||
create_modal_container()
|
create_modal_container()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user