diff --git a/imgsrc/bullhorn.svg b/imgsrc/bullhorn.svg new file mode 100644 index 0000000000..7ed591be28 --- /dev/null +++ b/imgsrc/bullhorn.svg @@ -0,0 +1,85 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/resources/images/bullhorn.png b/resources/images/bullhorn.png new file mode 100644 index 0000000000..f94c5f361e Binary files /dev/null and b/resources/images/bullhorn.png differ diff --git a/src/calibre/gui2/viewer/toolbars.py b/src/calibre/gui2/viewer/toolbars.py index 1a23ecf575..b2d81bede6 100644 --- a/src/calibre/gui2/viewer/toolbars.py +++ b/src/calibre/gui2/viewer/toolbars.py @@ -65,6 +65,7 @@ def all_actions(): 'print': Action('print.png', _('Print book'), 'print'), 'preferences': Action('config.png', _('Preferences'), 'preferences'), 'metadata': Action('metadata.png', _('Show book metadata'), 'metadata'), + 'toggle_read_aloud': Action('bullhorn.png', _('Read aloud'), 'toggle_read_aloud'), 'toggle_highlights': Action('highlight_only_on.png', _('Browse highlights in book'), 'toggle_highlights'), } all_actions.ans = Actions(amap) @@ -136,6 +137,7 @@ class ActionsToolBar(ToolBar): web_view.reference_mode_changed.connect(self.update_reference_mode_action) web_view.standalone_misc_settings_changed.connect(self.update_visibility) web_view.autoscroll_state_changed.connect(self.update_autoscroll_action) + web_view.read_aloud_state_changed.connect(self.update_read_aloud_action) web_view.customize_toolbar.connect(self.customize, type=Qt.ConnectionType.QueuedConnection) web_view.view_created.connect(self.on_view_created) @@ -171,6 +173,8 @@ class ActionsToolBar(ToolBar): a.setCheckable(True) self.toggle_highlights_action = self.highlights_action = a = shortcut_action('toggle_highlights') a.setCheckable(True) + self.toggle_read_aloud_action = a = shortcut_action('toggle_read_aloud') + a.setCheckable(True) self.lookup_action = a = shortcut_action('lookup') a.setCheckable(True) self.inspector_action = a = shortcut_action('inspector') @@ -178,6 +182,7 @@ class ActionsToolBar(ToolBar): self.autoscroll_action = a = shortcut_action('autoscroll') a.setCheckable(True) self.update_autoscroll_action(False) + self.update_read_aloud_action(False) self.chrome_action = shortcut_action('chrome') self.mode_action = a = shortcut_action('mode') @@ -223,6 +228,11 @@ class ActionsToolBar(ToolBar): self.autoscroll_action.setToolTip( _('Turn off auto-scrolling') if active else _('Turn on auto-scrolling')) + def update_read_aloud_action(self, active): + self.toggle_read_aloud_action.setChecked(active) + self.autoscroll_action.setToolTip( + _('Stop reading') if active else _('Read the text of the book aloud')) + def update_reference_mode_action(self, enabled): self.reference_action.setChecked(enabled) diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index fc8f6c30f4..694001d703 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -251,6 +251,7 @@ class ViewerBridge(Bridge): ask_for_open = from_js(object) selection_changed = from_js(object, object) autoscroll_state_changed = from_js(object) + read_aloud_state_changed = from_js(object) copy_selection = from_js(object, object) view_image = from_js(object) copy_image = from_js(object) @@ -452,6 +453,7 @@ class WebView(RestartingWebEngineView): ask_for_open = pyqtSignal(object) selection_changed = pyqtSignal(object, object) autoscroll_state_changed = pyqtSignal(object) + read_aloud_state_changed = pyqtSignal(object) view_image = pyqtSignal(object) copy_image = pyqtSignal(object) overlay_visibility_changed = pyqtSignal(object) @@ -508,6 +510,7 @@ class WebView(RestartingWebEngineView): self.bridge.ask_for_open.connect(self.ask_for_open) self.bridge.selection_changed.connect(self.selection_changed) self.bridge.autoscroll_state_changed.connect(self.autoscroll_state_changed) + self.bridge.read_aloud_state_changed.connect(self.read_aloud_state_changed) self.bridge.view_image.connect(self.view_image) self.bridge.copy_image.connect(self.copy_image) self.bridge.overlay_visibility_changed.connect(self.overlay_visibility_changed) diff --git a/src/pyj/read_book/read_aloud.pyj b/src/pyj/read_book/read_aloud.pyj index 6b5eec0b82..42185bb923 100644 --- a/src/pyj/read_book/read_aloud.pyj +++ b/src/pyj/read_book/read_aloud.pyj @@ -69,12 +69,16 @@ class ReadAloud: self.state = HIDDEN self.container.style.display = 'none' self.view.focus_iframe() + if ui_operations.read_aloud_state_changed: + ui_operations.read_aloud_state_changed(False) def show(self): if self.state is HIDDEN: self.container.style.display = 'block' self.state = STOPPED self.focus() + if ui_operations.read_aloud_state_changed: + ui_operations.read_aloud_state_changed(True) def focus(self): self.container.focus() diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 1fa6f6eb34..a964034892 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -507,6 +507,8 @@ class View: self.toggle_reference_mode() elif data.name is 'read_aloud': self.start_read_aloud() + elif data.name is 'toggle_read_aloud': + self.toggle_read_aloud() elif data.name is 'reload_book': ui_operations.reload_book() elif data.name is 'next_section': @@ -680,6 +682,12 @@ class View: if not dont_start_talking: self.read_aloud.play() + def toggle_read_aloud(self): + if self.read_aloud.is_visible: + self.read_aloud.hide() + else: + self.start_read_aloud() + def show_chrome(self, data): elements = {} if data and data.elements: diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 997d0f6fb3..6bf71ed2b8 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -394,6 +394,8 @@ if window is window.top: to_python.customize_toolbar() ui_operations.autoscroll_state_changed = def(active): to_python.autoscroll_state_changed(active) + ui_operations.read_aloud_state_changed = def(active): + to_python.read_aloud_state_changed(active) ui_operations.search_result_not_found = def(sr): to_python.search_result_not_found(sr) ui_operations.scrollbar_context_menu = def(x, y, frac):