anki/qt/aqt/toolbar.py
Matthias Metelka 0c340c4f74
Add comments to Sass variables and tweak main window (#2137)
* Prevent multiple inclusion of variables in CSS files

* Use dict instead of tuple for variables

* Add comments to variables

* Improve appearance of main window

* Tweak main window styles

* Use json.dumps over pprint.format

* Make study button primary

* Improve header margin

* Make bottom toolbar slimmer

* Make congrats page more balanced

* Fix type issue

* Replace day/night with light/dark

* Exclude top-level-drag-row from hover effect

* Create dataclass for variables

* Run formatter

* Apply CSS variables from Python side

Why go full-circle with the Sass variables? This way we only need one interface for add-on authors to interact with. It also makes it easier for us to apply additional themes in the future.

* Fix typing

* Fix rgba values in Qt

* Darken button background

* Fix palette not being applied in light theme

For some odd reason this problem arose much later than #2016.

* Tweak default button look

* Reformat

* Apply CSS vars to ts pages

* Include elevation in button_mixins_lib

* Cast opacity to int

* Add some margin to studiedToday info

* Tweak light theme button gradient

* Tweak highlight-bg for light theme

* Add back default button color

as it made the browser sidebar tool icons dark in light theme.

* Reformat

* Tweak light theme buttons once more

Sorry for the back-and-forth. Sass only compiles when there are changes in user files, not when I only change the vars.

* Fix bottom toolbar button indicators

* Make buttons more clicky

* Fix button padding

* Handle macOS separately again

* Decrease elevation effect for main window buttons to 1

* Imitate box-shadow for Qt elements

* Adjust shadow vars

* Adjust primary border color

because the save button in the deck options had a lighter color than its background gradient.

* Boost box-shadow color of primary buttons

* Format

* Adjust Qt box-shadow imitation and shadow colors

* Use more subtle default shadow color

* Add some more padding to top toolbar

* Revert "Apply CSS vars to ts pages"

This reverts commit 5d8e7f6b7ffc8894b6517ecbb8cfba35407fc69a.

* Revert "Apply CSS variables from Python side"

This reverts commit 87db774412fd2bfd75e2630d2c5e782daef96b5f.

* Better match the standard macOS buttons

In the dark theme the standard color is a lighter grey, but at least
the size/shape is similar again.

This doesn't work for the editor buttons.

* Reduce the top margin of the congrats screen

* Fix illegible buttons when changing theme on macOS; match dark button style
2022-10-29 10:48:53 +10:00

234 lines
6.8 KiB
Python

# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import annotations
from typing import Any
import aqt
from anki.sync import SyncStatus
from aqt import gui_hooks
from aqt.qt import *
from aqt.sync import get_sync_status
from aqt.utils import tr
from aqt.webview import AnkiWebView
# wrapper class for set_bridge_command()
class TopToolbar:
def __init__(self, toolbar: Toolbar) -> None:
self.toolbar = toolbar
# wrapper class for set_bridge_command()
class BottomToolbar:
def __init__(self, toolbar: Toolbar) -> None:
self.toolbar = toolbar
class Toolbar:
def __init__(self, mw: aqt.AnkiQt, web: AnkiWebView) -> None:
self.mw = mw
self.web = web
self.link_handlers: dict[str, Callable] = {
"study": self._studyLinkHandler,
}
self.web.setFixedHeight(30)
self.web.requiresCol = False
def draw(
self,
buf: str = "",
web_context: Any | None = None,
link_handler: Callable[[str], Any] | None = None,
) -> None:
web_context = web_context or TopToolbar(self)
link_handler = link_handler or self._linkHandler
self.web.set_bridge_command(link_handler, web_context)
self.web.stdHtml(
self._body % self._centerLinks(),
css=["css/toolbar.css"],
js=["js/vendor/jquery.min.js", "js/toolbar.js"],
context=web_context,
)
self.web.adjustHeightToFit()
def redraw(self) -> None:
self.set_sync_active(self.mw.media_syncer.is_syncing())
self.update_sync_status()
gui_hooks.top_toolbar_did_redraw(self)
# Available links
######################################################################
def create_link(
self,
cmd: str,
label: str,
func: Callable,
tip: str | None = None,
id: str | None = None,
) -> str:
"""Generates HTML link element and registers link handler
Arguments:
cmd {str} -- Command name used for the JS → Python bridge
label {str} -- Display label of the link
func {Callable} -- Callable to be called on clicking the link
Keyword Arguments:
tip {Optional[str]} -- Optional tooltip text to show on hovering
over the link (default: {None})
id: {Optional[str]} -- Optional id attribute to supply the link with
(default: {None})
Returns:
str -- HTML link element
"""
self.link_handlers[cmd] = func
title_attr = f'title="{tip}"' if tip else ""
id_attr = f'id="{id}"' if id else ""
return (
f"""<a class=hitem tabindex="-1" aria-label="{label}" """
f"""{title_attr} {id_attr} href=# onclick="return pycmd('{cmd}')">"""
f"""{label}</a>"""
)
def _centerLinks(self) -> str:
links = [
self.create_link(
"decks",
tr.actions_decks(),
self._deckLinkHandler,
tip=tr.actions_shortcut_key(val="D"),
id="decks",
),
self.create_link(
"add",
tr.actions_add(),
self._addLinkHandler,
tip=tr.actions_shortcut_key(val="A"),
id="add",
),
self.create_link(
"browse",
tr.qt_misc_browse(),
self._browseLinkHandler,
tip=tr.actions_shortcut_key(val="B"),
id="browse",
),
self.create_link(
"stats",
tr.qt_misc_stats(),
self._statsLinkHandler,
tip=tr.actions_shortcut_key(val="T"),
id="stats",
),
]
links.append(self._create_sync_link())
gui_hooks.top_toolbar_did_init_links(links, self)
return "\n".join(links)
# Sync
######################################################################
def _create_sync_link(self) -> str:
name = tr.qt_misc_sync()
title = tr.actions_shortcut_key(val="Y")
label = "sync"
self.link_handlers[label] = self._syncLinkHandler
return f"""
<a class=hitem tabindex="-1" aria-label="{name}" title="{title}" id="{label}" href=# onclick="return pycmd('{label}')"
>{name}<img id=sync-spinner src='/_anki/imgs/refresh.svg'>
</a>"""
def set_sync_active(self, active: bool) -> None:
method = "add" if active else "remove"
self.web.eval(
f"document.getElementById('sync-spinner').classList.{method}('spin')"
)
def set_sync_status(self, status: SyncStatus) -> None:
self.web.eval(f"updateSyncColor({status.required})")
def update_sync_status(self) -> None:
get_sync_status(self.mw, self.mw.toolbar.set_sync_status)
# Link handling
######################################################################
def _linkHandler(self, link: str) -> bool:
if link in self.link_handlers:
self.link_handlers[link]()
return False
def _deckLinkHandler(self) -> None:
self.mw.moveToState("deckBrowser")
def _studyLinkHandler(self) -> None:
# if overview already shown, switch to review
if self.mw.state == "overview":
self.mw.col.startTimebox()
self.mw.moveToState("review")
else:
self.mw.onOverview()
def _addLinkHandler(self) -> None:
self.mw.onAddCard()
def _browseLinkHandler(self) -> None:
self.mw.onBrowse()
def _statsLinkHandler(self) -> None:
self.mw.onStats()
def _syncLinkHandler(self) -> None:
self.mw.on_sync_button_clicked()
# HTML & CSS
######################################################################
_body = """
<center id=outer>
<table id=header>
<tr>
<td class=tdcenter align=center>%s</td>
</tr></table>
</center>
"""
# Bottom bar
######################################################################
class BottomBar(Toolbar):
_centerBody = """
<center id=outer><table width=100%% id=header><tr><td align=center>
%s</td></tr></table></center>
"""
def draw(
self,
buf: str = "",
web_context: Any | None = None,
link_handler: Callable[[str], Any] | None = None,
) -> None:
# note: some screens may override this
web_context = web_context or BottomToolbar(self)
link_handler = link_handler or self._linkHandler
self.web.set_bridge_command(link_handler, web_context)
self.web.stdHtml(
self._centerBody % buf,
css=["css/toolbar.css", "css/toolbar-bottom.css"],
context=web_context,
)
self.web.adjustHeightToFit()