diff --git a/qt/aqt/data/web/css/toolbar.scss b/qt/aqt/data/web/css/toolbar.scss index dd49ebc75..0bd95fb5f 100644 --- a/qt/aqt/data/web/css/toolbar.scss +++ b/qt/aqt/data/web/css/toolbar.scss @@ -51,22 +51,20 @@ body { -webkit-user-select: none; overflow: hidden; - &:not(.fancy).collapsed { + &:not(.fancy).hidden { opacity: 0; } transition: opacity var(--transition) ease-in-out; &.fancy { - &.collapsed { + margin-bottom: 5px; + + &.hidden { transform: translateY(-100vh); } transition: transform var(--transition) ease-in-out; - .header { - height: 41px; - } .toolbar { - height: 31px; overflow: hidden; border-bottom-left-radius: prop(border-radius-medium); border-bottom-right-radius: prop(border-radius-medium); diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 31c1cffe7..bc1abcda7 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -66,7 +66,7 @@ from aqt.qt import sip from aqt.sync import sync_collection, sync_login from aqt.taskman import TaskManager from aqt.theme import Theme, theme_manager -from aqt.toolbar import Toolbar, ToolbarWebView +from aqt.toolbar import BottomWebView, Toolbar, TopWebView from aqt.undo import UndoActionsInfo from aqt.utils import ( HelpPage, @@ -150,17 +150,17 @@ class MainWebView(AnkiWebView): return handled if evt.type() == QEvent.Type.Leave: - if self.mw.pm.collapse_toolbar(): - # Expand toolbar when mouse moves above main webview - # and automatically collapse it with delay after mouse leaves - if self.mapFromGlobal(QCursor.pos()).y() < self.geometry().y(): - if self.mw.toolbarWeb.collapsed: - self.mw.toolbarWeb.expand() + if self.mw.pm.hide_top_bar(): + # Show toolbar when mouse moves outside main webview + # and automatically hide it with delay after mouse has entered again + self.mw.toolbarWeb.show() + self.mw.bottomWeb.show() return True if evt.type() == QEvent.Type.Enter: - if self.mw.pm.collapse_toolbar(): + if self.mw.pm.hide_top_bar(): self.mw.toolbarWeb.hide_timer.start() + self.mw.bottomWeb.hide_timer.start() return True return False @@ -729,16 +729,21 @@ class AnkiQt(QMainWindow): def _reviewState(self, oldState: MainWindowState) -> None: self.reviewer.show() - if self.pm.collapse_toolbar(): - self.toolbarWeb.collapse() + + if self.pm.hide_top_bar(): + self.toolbarWeb.hide() else: self.toolbarWeb.flatten() + if self.pm.hide_bottom_bar(): + self.bottomWeb.hide() + def _reviewCleanup(self, newState: MainWindowState) -> None: if newState != "resetRequired" and newState != "review": self.reviewer.cleanup() self.toolbarWeb.elevate() - self.toolbarWeb.expand() + self.toolbarWeb.show() + self.bottomWeb.show() # Resetting state ########################################################################## @@ -872,12 +877,12 @@ title="{}" {}>{}""".format( self.form = aqt.forms.main.Ui_MainWindow() self.form.setupUi(self) # toolbar - tweb = self.toolbarWeb = ToolbarWebView(self, title="top toolbar") + tweb = self.toolbarWeb = TopWebView(self, title="top toolbar") self.toolbar = Toolbar(self, tweb) # main area self.web = MainWebView(self) # bottom area - sweb = self.bottomWeb = AnkiWebView(title="bottom toolbar") + sweb = self.bottomWeb = BottomWebView(self, title="bottom toolbar") sweb.setFocusPolicy(Qt.FocusPolicy.WheelFocus) sweb.disable_zoom() # add in a layout @@ -1358,10 +1363,6 @@ title="{}" {}>{}""".format( window.windowState() ^ Qt.WindowState.WindowFullScreen ) - def collapse_toolbar_if_allowed(self) -> None: - if self.pm.collapse_toolbar() and self.state == "review": - self.toolbarWeb.collapse() - # Auto update ########################################################################## diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 8e6ec1d11..8743608ca 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -213,10 +213,11 @@ class Preferences(QDialog): self.form.minimalist_mode.setChecked(self.mw.pm.minimalist_mode()) qconnect(self.form.minimalist_mode.stateChanged, self.mw.pm.set_minimalist_mode) - self.form.collapse_toolbar.setChecked(self.mw.pm.collapse_toolbar()) - qconnect( - self.form.collapse_toolbar.stateChanged, self.mw.pm.set_collapse_toolbar - ) + self.form.hide_top_bar.setChecked(self.mw.pm.hide_top_bar()) + qconnect(self.form.hide_top_bar.stateChanged, self.mw.pm.set_hide_top_bar) + + self.form.hide_bottom_bar.setChecked(self.mw.pm.hide_bottom_bar()) + qconnect(self.form.hide_bottom_bar.stateChanged, self.mw.pm.set_hide_bottom_bar) self.form.uiScale.setValue(int(self.mw.pm.uiScale() * 100)) themes = [ diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 01434c9b0..527ef722b 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -532,13 +532,19 @@ create table if not exists profiles self.meta["tatsumoto_mode"] = on gui_hooks.body_classes_need_update() - def collapse_toolbar(self) -> bool: - return self.meta.get("collapse_toolbar", False) + def hide_top_bar(self) -> bool: + return self.meta.get("hide_top_bar", False) - def set_collapse_toolbar(self, on: bool) -> None: - self.meta["collapse_toolbar"] = on + def set_hide_top_bar(self, on: bool) -> None: + self.meta["hide_top_bar"] = on gui_hooks.body_classes_need_update() + def hide_bottom_bar(self) -> bool: + return self.meta.get("hide_bottom_bar", False) + + def set_hide_bottom_bar(self, on: bool) -> None: + self.meta["hide_bottom_bar"] = on + def last_addon_update_check(self) -> int: return self.meta.get("last_addon_update_check", 0) diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index dda339e93..2a6a5ec8f 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -706,7 +706,6 @@ time = %(time)d; else: maxTime = 0 self.bottom.web.eval("showQuestion(%s,%d);" % (json.dumps(middle), maxTime)) - self.bottom.web.adjustHeightToFit() def _showEaseButtons(self) -> None: middle = self._answerButtons() diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py index 847db8f55..cf7a2b934 100644 --- a/qt/aqt/toolbar.py +++ b/qt/aqt/toolbar.py @@ -3,13 +3,14 @@ from __future__ import annotations import re -from typing import Any, Optional +from typing import Any, Optional, cast import aqt from anki.sync import SyncStatus -from aqt import gui_hooks +from aqt import gui_hooks, props from aqt.qt import * from aqt.sync import get_sync_status +from aqt.theme import theme_manager from aqt.utils import tr from aqt.webview import AnkiWebView @@ -32,13 +33,10 @@ class ToolbarWebView(AnkiWebView): self.mw = mw self.setFocusPolicy(Qt.FocusPolicy.WheelFocus) self.disable_zoom() - self.collapsed = False - self.web_height = 0 - # collapse timer + self.hidden = False self.hide_timer = QTimer() self.hide_timer.setSingleShot(True) self.hide_timer.setInterval(1000) - qconnect(self.hide_timer.timeout, self.mw.collapse_toolbar_if_allowed) def eventFilter(self, obj, evt): if handled := super().eventFilter(obj, evt): @@ -52,29 +50,58 @@ class ToolbarWebView(AnkiWebView): return False + def hide(self) -> None: + self.hidden = True + + def show(self) -> None: + self.hidden = False + + def hide_if_allowed(self) -> None: + if self.mw.state != "review": + return + + if self.hide_condition(): + self.hide() + + +class TopWebView(ToolbarWebView): + def __init__(self, mw: aqt.AnkiQt, title: str) -> None: + super().__init__(mw, title=title) + self.web_height = 0 + self.hide_condition = self.mw.pm.hide_top_bar + qconnect(self.hide_timer.timeout, self.hide_if_allowed) + def on_body_classes_need_update(self) -> None: super().on_body_classes_need_update() - super().adjustHeightToFit() + self.adjustHeightToFit() if self.mw.state == "review": - if self.mw.pm.collapse_toolbar(): + if self.mw.pm.hide_top_bar(): self.eval("""document.body.classList.remove("flat"); """) else: self.flatten() - self.expand() + self.show() def _onHeight(self, qvar: Optional[int]) -> None: super()._onHeight(qvar) self.web_height = int(qvar) - def collapse(self) -> None: - self.collapsed = True - self.eval("""document.body.classList.add("collapsed"); """) + def hide(self) -> None: + super().hide() - def expand(self) -> None: - self.collapsed = False - self.eval("""document.body.classList.remove("collapsed"); """) + self.hidden = True + self.eval( + """document.body.classList.add("hidden"); """, + ) + self.mw.web.eval( + f"""document.body.style.setProperty("--toolbar-height", "0px"); """ + ) + + def show(self) -> None: + super().show() + + self.eval("""document.body.classList.remove("hidden"); """) def flatten(self) -> None: self.eval("""document.body.classList.add("flat"); """) @@ -108,6 +135,51 @@ class ToolbarWebView(AnkiWebView): ) +class BottomWebView(ToolbarWebView): + def __init__(self, mw: aqt.AnkiQt, title: str) -> None: + super().__init__(mw, title=title) + self.hide_condition = self.mw.pm.hide_bottom_bar + qconnect(self.hide_timer.timeout, self.hide_if_allowed) + + def on_body_classes_need_update(self) -> None: + super().on_body_classes_need_update() + + self.adjustHeightToFit() + self.show() + + def _onHeight(self, qvar: Optional[int]) -> None: + self.web_height = int(qvar) + + if qvar is None: + self.mw.progress.single_shot(1000, self.mw.reset) + return + + if self.mw.pm.reduce_motion(): + self.setFixedHeight(int(qvar)) + else: + # Collapse/Expand animation + self.setMinimumHeight(0) + self.animation = QPropertyAnimation( + self, cast(QByteArray, b"maximumHeight") + ) + self.animation.setDuration(int(theme_manager.var(props.TRANSITION))) + self.animation.setStartValue(self.height()) + self.animation.setEndValue(int(qvar)) + qconnect(self.animation.finished, lambda: self.setFixedHeight(int(qvar))) + self.animation.start() + + def hide(self) -> None: + super().hide() + + self._onHeight(1) + + def show(self) -> None: + super().show() + + self.hidden = False + self.adjustHeightToFit() + + class Toolbar: def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None: self.mw = mw