diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 170def8064..2b2323275e 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -169,7 +169,7 @@ def main(args=sys.argv): if opts.raise_window: main.raise_() if opts.full_screen: - main.showFullScreen() + main.set_full_screen(True) app.exec_() if listener is not None: diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index e59d2c3c1c..71de3ce9d0 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -11,7 +11,9 @@ from collections import defaultdict from hashlib import sha256 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.constants import config_dir @@ -43,6 +45,7 @@ class EbookViewer(MainWindow): def __init__(self): MainWindow.__init__(self, None) + self.in_full_screen_mode = None try: os.makedirs(annotations_dir) except EnvironmentError: @@ -73,11 +76,13 @@ class EbookViewer(MainWindow): self.web_view.reload_book.connect(self.reload_book) 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.toggle_full_screen.connect(self.toggle_full_screen) self.setCentralWidget(self.web_view) state = vprefs['main_window_state'] if state: self.restoreState(state, self.MAIN_WINDOW_STATE_VERSION) + # IPC {{{ def handle_commandline_arg(self, arg): if arg: if os.path.isfile(arg) and os.access(arg, os.R_OK): @@ -92,7 +97,28 @@ class EbookViewer(MainWindow): return self.load_ebook(path, open_at=open_at) 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): if self.toc_dock.isVisible(): self.toc_dock.setVisible(False) @@ -106,6 +132,9 @@ class EbookViewer(MainWindow): def toc_searched(self, index): item = self.toc_model.itemFromIndex(index) self.web_view.goto_toc_node(item.node_id) + # }}} + + # Load book {{{ def load_ebook(self, pathtoebook, open_at=None, reload_book=False): # TODO: Implement open_at @@ -166,7 +195,9 @@ class EbookViewer(MainWindow): with open(path, 'rb') as f: raw = f.read() merge_annotations(parse_annotations(raw), amap) + # }}} + # CFI management {{{ def initial_cfi_for_current_book(self): lrp = self.current_book_data['annotations_map']['last-read'] if lrp: @@ -179,7 +210,9 @@ class EbookViewer(MainWindow): return self.current_book_data['annotations_map']['last-read'] = [{ 'pos': cfi, 'pos_type': 'epubcfi', 'timestamp': utcnow()}] + # }}} + # State serialization {{{ def save_annotations(self): if not self.current_book_data: return @@ -201,3 +234,4 @@ class EbookViewer(MainWindow): self.save_annotations() self.save_state() return MainWindow.closeEvent(self, ev) + # }}} diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 3608af430b..219e574122 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -171,11 +171,13 @@ class ViewerBridge(Bridge): reload_book = from_js() toggle_toc = from_js() update_current_toc_nodes = from_js(object, object) + toggle_full_screen = from_js() create_view = to_js() show_preparing_message = to_js() start_book_load = to_js() goto_toc_node = to_js() + full_screen_state_changed = to_js() class WebPage(QWebEnginePage): @@ -252,6 +254,7 @@ class WebView(RestartingWebEngineView): reload_book = pyqtSignal() toggle_toc = pyqtSignal() update_current_toc_nodes = pyqtSignal(object, object) + toggle_full_screen = pyqtSignal() def __init__(self, parent=None): self._host_widget = None @@ -267,6 +270,7 @@ class WebView(RestartingWebEngineView): self.bridge.reload_book.connect(self.reload_book) self.bridge.toggle_toc.connect(self.toggle_toc) 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.setPage(self._page) self.setAcceptDrops(False) @@ -343,6 +347,9 @@ class WebView(RestartingWebEngineView): def goto_toc_node(self, 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): if key == '*' and val is None: vprefs['session_data'] = {} diff --git a/src/pyj/read_book/globals.pyj b/src/pyj/read_book/globals.pyj index 51b44619c3..882800c4a2 100644 --- a/src/pyj/read_book/globals.pyj +++ b/src/pyj/read_book/globals.pyj @@ -62,7 +62,8 @@ register_callback(def(): ) runtime = { - 'is_standalone_viewer': False + 'is_standalone_viewer': False, + 'viewer_in_full_screen': False, } diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj index 36a9398500..e069b81f0e 100644 --- a/src/pyj/read_book/overlay.pyj +++ b/src/pyj/read_book/overlay.pyj @@ -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') 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') + 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: reload_actions = E.ul(reload_action) + nav_actions = E.ul(back_action, forward_action) else: 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 - E.ul( - ac(_('Home'), _('Return to list of books'), def(): home();, 'home'), - ac(_('Back'), None, self.back, 'arrow-left'), - ac(_('Forward'), None, self.forward, 'arrow-right'), - ), + nav_actions, E.ul( ac(_('Search'), _('Search for text in this book'), self.overlay.show_search, 'search'), @@ -247,12 +248,19 @@ class MainOverlay: class_=MAIN_OVERLAY_ACTIONS_CLASS ) - if not full_screen_element() and not is_ios: - # No fullscreen on iOS, see http://caniuse.com/#search=fullscreen - actions_div.appendChild( - E.ul( - ac(_('Full screen'), _('Enter full screen mode'), def(): request_full_screen(), self.overlay.hide();, 'full-screen'), - )) + full_screen_actions = [] + if runtime.is_standalone_viewer: + text = _('Exit full screen') if runtime.viewer_in_full_screen else _('Enter full screen') + full_screen_actions.push( + 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 onclick=def (evt):evt.stopPropagation();, diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 53eedf09b6..8af6a76639 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -198,6 +198,11 @@ def 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): if not error_object: # cross domain error @@ -246,6 +251,8 @@ if window is window.top: to_python.toggle_toc() 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) + ui_operations.toggle_full_screen = def(): + to_python.toggle_full_screen() document.body.appendChild(E.div(id='view')) window.onerror = onerror create_modal_container()