Live theme changes (#1497)
* Allow theme change at runtime and add hook * Save or restore default palette on theme change * Update aqt widget styles on theme change * styling fixes - drop _light_palette, as default_palette serves the same purpose - save default platform theme, and restore it when switching away from nightmode - update macOS light/dark mode on theme switch - fix unreadable menus on Windows * update night-mode classes on theme change This is the easy part - CSS styling that uses standard_css or our css variables should update automatically. The main remaining issue is JS code that sets colors based on the theme at the time it's run - eg the graph code, and the editor. * switch night mode value on toggle * expose current theme via a store; switch graphs to use it https://github.com/ankitects/anki/issues/1471#issuecomment-972402492 * start using currentTheme in editor/components This fixes basic editing - there are still components that need updating. * add simple xcodeproj for code completion * add helper to get currently-active system theme on macOS * fix setCurrentTheme not being immediately available * live update tag color * style().name() doesn't work on Qt5 * automatic theme switching on Windows/Mac * currentTheme -> pageTheme * Replace `nightModeKey` with `pageTheme` Co-authored-by: Damien Elmes <gpg@ankiweb.net>
This commit is contained in:
parent
63404de5df
commit
f2173fddb0
@ -37,4 +37,8 @@ preferences-you-can-restore-backups-via-fileswitch = You can restore backups via
|
||||
preferences-legacy-timezone-handling = Legacy timezone handling (buggy, but required for AnkiDroid <= 2.14)
|
||||
preferences-default-search-text = Default search text
|
||||
preferences-default-search-text-example = eg. 'deck:current '
|
||||
preferences-theme-label = Theme: { $theme }
|
||||
preferences-theme-follow-system = Follow System
|
||||
preferences-theme-light = Light
|
||||
preferences-theme-dark = Dark
|
||||
preferences-v3-scheduler = V3 scheduler
|
||||
|
@ -19,6 +19,14 @@ class SidebarSearchBar(QLineEdit):
|
||||
self.timer.setInterval(600)
|
||||
self.timer.setSingleShot(True)
|
||||
self.setFrame(False)
|
||||
self.setup_style()
|
||||
|
||||
qconnect(self.timer.timeout, self.onSearch)
|
||||
qconnect(self.textChanged, self.onTextChanged)
|
||||
|
||||
aqt.gui_hooks.theme_did_change.append(self.setup_style)
|
||||
|
||||
def setup_style(self) -> None:
|
||||
border = theme_manager.color(colors.MEDIUM_BORDER)
|
||||
styles = [
|
||||
"padding: 1px",
|
||||
@ -32,9 +40,6 @@ class SidebarSearchBar(QLineEdit):
|
||||
|
||||
self.setStyleSheet("QLineEdit { %s }" % ";".join(styles))
|
||||
|
||||
qconnect(self.timer.timeout, self.onSearch)
|
||||
qconnect(self.textChanged, self.onTextChanged)
|
||||
|
||||
def onTextChanged(self, text: str) -> None:
|
||||
if not self.timer.isActive():
|
||||
self.timer.start()
|
||||
@ -49,3 +54,6 @@ class SidebarSearchBar(QLineEdit):
|
||||
self.onSearch()
|
||||
else:
|
||||
QLineEdit.keyPressEvent(self, evt)
|
||||
|
||||
def cleanup(self) -> None:
|
||||
aqt.gui_hooks.theme_did_change.remove(self.setup_style)
|
||||
|
@ -31,6 +31,7 @@ class SidebarToolbar(QToolBar):
|
||||
self.setIconSize(QSize(16, 16))
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||
self.setStyle(QStyleFactory.create("fusion"))
|
||||
aqt.gui_hooks.theme_did_change.append(self._update_icons)
|
||||
|
||||
def _setup_tools(self) -> None:
|
||||
for row, tool in enumerate(self._tools):
|
||||
@ -48,3 +49,10 @@ class SidebarToolbar(QToolBar):
|
||||
def _on_action_group_triggered(self, action: QAction) -> None:
|
||||
index = self._action_group.actions().index(action)
|
||||
self.sidebar.tool = self._tools[index][0]
|
||||
|
||||
def cleanup(self) -> None:
|
||||
aqt.gui_hooks.theme_did_change.remove(self._update_icons)
|
||||
|
||||
def _update_icons(self) -> None:
|
||||
for idx, action in enumerate(self._action_group.actions()):
|
||||
action.setIcon(theme_manager.icon_from_resources(self._tools[idx][1]))
|
||||
|
@ -91,6 +91,16 @@ class SidebarTreeView(QTreeView):
|
||||
qconnect(self.expanded, self._on_expansion)
|
||||
qconnect(self.collapsed, self._on_collapse)
|
||||
|
||||
self._setup_style()
|
||||
|
||||
# these do not really belong here, they should be in a higher-level class
|
||||
self.toolbar = SidebarToolbar(self)
|
||||
self.searchBar = SidebarSearchBar(self)
|
||||
|
||||
gui_hooks.flag_label_did_change.append(self.refresh)
|
||||
gui_hooks.theme_did_change.append(self._setup_style)
|
||||
|
||||
def _setup_style(self) -> None:
|
||||
# match window background color and tweak style
|
||||
bgcolor = QPalette().window().color().name()
|
||||
border = theme_manager.color(colors.MEDIUM_BORDER)
|
||||
@ -105,14 +115,11 @@ class SidebarTreeView(QTreeView):
|
||||
|
||||
self.setStyleSheet("QTreeView { %s }" % ";".join(styles))
|
||||
|
||||
# these do not really belong here, they should be in a higher-level class
|
||||
self.toolbar = SidebarToolbar(self)
|
||||
self.searchBar = SidebarSearchBar(self)
|
||||
|
||||
gui_hooks.flag_label_did_change.append(self.refresh)
|
||||
|
||||
def cleanup(self) -> None:
|
||||
self.toolbar.cleanup()
|
||||
self.searchBar.cleanup()
|
||||
gui_hooks.flag_label_did_change.remove(self.refresh)
|
||||
gui_hooks.theme_did_change.remove(self._setup_style)
|
||||
|
||||
@property
|
||||
def tool(self) -> SidebarTool:
|
||||
|
@ -59,6 +59,7 @@ class Table:
|
||||
|
||||
def cleanup(self) -> None:
|
||||
self._save_header()
|
||||
gui_hooks.theme_did_change.remove(self._setup_style)
|
||||
|
||||
# Public Methods
|
||||
######################################################################
|
||||
@ -342,17 +343,10 @@ class Table:
|
||||
self._view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self._view.horizontalScrollBar().setSingleStep(10)
|
||||
self._update_font()
|
||||
if not theme_manager.night_mode:
|
||||
self._view.setStyleSheet(
|
||||
"QTableView{ selection-background-color: rgba(150, 150, 150, 50); "
|
||||
"selection-color: black; }"
|
||||
)
|
||||
elif theme_manager.macos_dark_mode():
|
||||
self._view.setStyleSheet(
|
||||
f"QTableView {{ gridline-color: {colors.FRAME_BG} }}"
|
||||
)
|
||||
self._setup_style()
|
||||
self._view.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||
qconnect(self._view.customContextMenuRequested, self._on_context_menu)
|
||||
gui_hooks.theme_did_change.append(self._setup_style)
|
||||
|
||||
def _update_font(self) -> None:
|
||||
# we can't choose different line heights efficiently, so we need
|
||||
@ -365,6 +359,19 @@ class Table:
|
||||
curmax = bsize
|
||||
self._view.verticalHeader().setDefaultSectionSize(curmax + 6)
|
||||
|
||||
def _setup_style(self) -> None:
|
||||
if not theme_manager.night_mode:
|
||||
self._view.setStyleSheet(
|
||||
"QTableView{ selection-background-color: rgba(150, 150, 150, 50); "
|
||||
"selection-color: black; }"
|
||||
)
|
||||
elif theme_manager.macos_dark_mode():
|
||||
self._view.setStyleSheet(
|
||||
f"QTableView {{ gridline-color: {colors.FRAME_BG} }}"
|
||||
)
|
||||
else:
|
||||
self._view.setStyleSheet("")
|
||||
|
||||
def _setup_headers(self) -> None:
|
||||
vh = self._view.verticalHeader()
|
||||
hh = self._view.horizontalHeader()
|
||||
|
@ -55,7 +55,7 @@ class CardLayout(QDialog):
|
||||
self.model = note.note_type()
|
||||
self.templates = self.model["tmpls"]
|
||||
self.fill_empty_action_toggled = fill_empty
|
||||
self.night_mode_is_enabled = self.mw.pm.night_mode()
|
||||
self.night_mode_is_enabled = theme_manager.night_mode
|
||||
self.mobile_emulation_enabled = False
|
||||
self.have_autoplayed = False
|
||||
self.mm._remove_from_cache(self.model["id"])
|
||||
|
@ -55,6 +55,9 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="theme"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="video_driver"/>
|
||||
</item>
|
||||
@ -86,13 +89,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="nightMode">
|
||||
<property name="text">
|
||||
<string>preferences_night_mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="useCurrent">
|
||||
<item>
|
||||
@ -602,12 +598,12 @@
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>lang</tabstop>
|
||||
<tabstop>theme</tabstop>
|
||||
<tabstop>video_driver</tabstop>
|
||||
<tabstop>showPlayButtons</tabstop>
|
||||
<tabstop>interrupt_audio</tabstop>
|
||||
<tabstop>pastePNG</tabstop>
|
||||
<tabstop>paste_strips_formatting</tabstop>
|
||||
<tabstop>nightMode</tabstop>
|
||||
<tabstop>useCurrent</tabstop>
|
||||
<tabstop>default_search_text</tabstop>
|
||||
<tabstop>uiScale</tabstop>
|
||||
|
@ -47,7 +47,7 @@ from aqt.qt import *
|
||||
from aqt.qt import sip
|
||||
from aqt.sync import sync_collection, sync_login
|
||||
from aqt.taskman import TaskManager
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.theme import Theme, theme_manager
|
||||
from aqt.undo import UndoActionsInfo
|
||||
from aqt.utils import (
|
||||
HelpPage,
|
||||
@ -145,11 +145,11 @@ class AnkiQt(QMainWindow):
|
||||
self.setupMediaServer()
|
||||
self.setupSound()
|
||||
self.setupSpellCheck()
|
||||
self.setupProgress()
|
||||
self.setupStyle()
|
||||
self.setupMainWindow()
|
||||
self.setupSystemSpecific()
|
||||
self.setupMenus()
|
||||
self.setupProgress()
|
||||
self.setupErrorHandler()
|
||||
self.setupSignals()
|
||||
self.setupAutoUpdate()
|
||||
@ -1004,8 +1004,14 @@ title="{}" {}>{}</button>""".format(
|
||||
return True
|
||||
|
||||
def setupStyle(self) -> None:
|
||||
theme_manager.night_mode = self.pm.night_mode()
|
||||
theme_manager.apply_style(self.app)
|
||||
theme_manager.apply_style()
|
||||
self.progress.timer(
|
||||
5 * 1000, theme_manager.apply_style_if_system_style_changed, True, False
|
||||
)
|
||||
|
||||
def set_theme(self, theme: Theme) -> None:
|
||||
self.pm.set_theme(theme)
|
||||
self.setupStyle()
|
||||
|
||||
# Key handling
|
||||
##########################################################################
|
||||
|
@ -3,20 +3,40 @@
|
||||
|
||||
"""Platform-specific functionality."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from ctypes import CDLL
|
||||
|
||||
import aqt.utils
|
||||
from anki.utils import isMac
|
||||
from anki.utils import isMac, isWin
|
||||
|
||||
|
||||
def set_dark_mode(enabled: bool) -> bool:
|
||||
def get_windows_dark_mode() -> bool:
|
||||
"True if Windows system is currently in dark mode."
|
||||
if not isWin:
|
||||
return False
|
||||
|
||||
from winreg import ( # pylint: disable=import-error
|
||||
HKEY_CURRENT_USER,
|
||||
OpenKey,
|
||||
QueryValueEx,
|
||||
)
|
||||
|
||||
key = OpenKey(
|
||||
HKEY_CURRENT_USER,
|
||||
r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
|
||||
)
|
||||
return not QueryValueEx(key, "AppsUseLightTheme")[0]
|
||||
|
||||
|
||||
def set_macos_dark_mode(enabled: bool) -> bool:
|
||||
"True if setting successful."
|
||||
if not isMac:
|
||||
return False
|
||||
try:
|
||||
_set_dark_mode(enabled)
|
||||
_ankihelper().set_darkmode_enabled(enabled)
|
||||
return True
|
||||
except Exception as e:
|
||||
# swallow exceptions, as library will fail on macOS 10.13
|
||||
@ -24,9 +44,28 @@ def set_dark_mode(enabled: bool) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _set_dark_mode(enabled: bool) -> None:
|
||||
def get_macos_dark_mode() -> bool:
|
||||
"True if macOS system is currently in dark mode."
|
||||
if not isMac:
|
||||
return False
|
||||
try:
|
||||
return _ankihelper().system_is_dark()
|
||||
except Exception as e:
|
||||
# swallow exceptions, as library will fail on macOS 10.13
|
||||
print(e)
|
||||
return False
|
||||
|
||||
|
||||
_ankihelper_dll: CDLL | None = None
|
||||
|
||||
|
||||
def _ankihelper() -> CDLL:
|
||||
global _ankihelper_dll
|
||||
if _ankihelper_dll:
|
||||
return _ankihelper_dll
|
||||
if getattr(sys, "frozen", False):
|
||||
path = os.path.join(sys.prefix, "libankihelper.dylib")
|
||||
else:
|
||||
path = os.path.join(aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib")
|
||||
CDLL(path).set_darkmode_enabled(enabled)
|
||||
_ankihelper_dll = CDLL(path)
|
||||
return _ankihelper_dll
|
||||
|
@ -11,6 +11,7 @@ from aqt import AnkiQt
|
||||
from aqt.operations.collection import set_preferences
|
||||
from aqt.profiles import VideoDriver
|
||||
from aqt.qt import *
|
||||
from aqt.theme import Theme
|
||||
from aqt.utils import HelpPage, disable_help_button, openHelp, showInfo, showWarning, tr
|
||||
|
||||
|
||||
@ -199,7 +200,17 @@ class Preferences(QDialog):
|
||||
def setup_global(self) -> None:
|
||||
"Setup options global to all profiles."
|
||||
self.form.uiScale.setValue(int(self.mw.pm.uiScale() * 100))
|
||||
self.form.nightMode.setChecked(self.mw.pm.night_mode())
|
||||
themes = [
|
||||
tr.preferences_theme_label(theme=theme)
|
||||
for theme in (
|
||||
tr.preferences_theme_follow_system(),
|
||||
tr.preferences_theme_light(),
|
||||
tr.preferences_theme_dark(),
|
||||
)
|
||||
]
|
||||
self.form.theme.addItems(themes)
|
||||
self.form.theme.setCurrentIndex(self.mw.pm.theme().value)
|
||||
qconnect(self.form.theme.currentIndexChanged, self.on_theme_changed)
|
||||
|
||||
self.setup_language()
|
||||
self.setup_video_driver()
|
||||
@ -216,15 +227,14 @@ class Preferences(QDialog):
|
||||
self.mw.pm.setUiScale(newScale)
|
||||
restart_required = True
|
||||
|
||||
if self.mw.pm.night_mode() != self.form.nightMode.isChecked():
|
||||
self.mw.pm.set_night_mode(not self.mw.pm.night_mode())
|
||||
restart_required = True
|
||||
|
||||
if restart_required:
|
||||
showInfo(tr.preferences_changes_will_take_effect_when_you())
|
||||
|
||||
self.updateOptions()
|
||||
|
||||
def on_theme_changed(self, index: int) -> None:
|
||||
self.mw.set_theme(Theme(index))
|
||||
|
||||
# legacy - one of Henrik's add-ons is currently wrapping them
|
||||
|
||||
def setupOptions(self) -> None:
|
||||
|
@ -23,6 +23,7 @@ from anki.sync import SyncAuth
|
||||
from anki.utils import int_time, isMac, isWin
|
||||
from aqt import appHelpSite
|
||||
from aqt.qt import *
|
||||
from aqt.theme import Theme
|
||||
from aqt.utils import disable_help_button, showWarning, tr
|
||||
|
||||
# Profile handling
|
||||
@ -515,6 +516,12 @@ create table if not exists profiles
|
||||
def set_night_mode(self, on: bool) -> None:
|
||||
self.meta["night_mode"] = on
|
||||
|
||||
def theme(self) -> Theme:
|
||||
return Theme(self.meta.get("theme", 0))
|
||||
|
||||
def set_theme(self, theme: Theme) -> None:
|
||||
self.meta["theme"] = theme.value
|
||||
|
||||
def dark_mode_widgets(self) -> bool:
|
||||
return self.meta.get("dark_mode_widgets", False)
|
||||
|
||||
|
@ -3,12 +3,14 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
import platform
|
||||
from dataclasses import dataclass
|
||||
|
||||
import aqt
|
||||
from anki.utils import isMac
|
||||
from aqt import QApplication, colors, gui_hooks, isWin
|
||||
from aqt.platform import set_dark_mode
|
||||
from aqt.platform import get_macos_dark_mode, get_windows_dark_mode, set_macos_dark_mode
|
||||
from aqt.qt import (
|
||||
QColor,
|
||||
QGuiApplication,
|
||||
@ -37,6 +39,12 @@ class ColoredIcon:
|
||||
return ColoredIcon(path=self.path, color=color)
|
||||
|
||||
|
||||
class Theme(enum.IntEnum):
|
||||
FOLLOW_SYSTEM = 0
|
||||
LIGHT = 1
|
||||
DARK = 2
|
||||
|
||||
|
||||
class ThemeManager:
|
||||
_night_mode_preference = False
|
||||
_icon_cache_light: dict[str, QIcon] = {}
|
||||
@ -44,6 +52,7 @@ class ThemeManager:
|
||||
_icon_size = 128
|
||||
_dark_mode_available: bool | None = None
|
||||
default_palette: QPalette | None = None
|
||||
_default_style: str | None = None
|
||||
|
||||
# Qt applies a gradient to the buttons in dark mode
|
||||
# from about #505050 to #606060.
|
||||
@ -58,7 +67,7 @@ class ThemeManager:
|
||||
return False
|
||||
|
||||
if self._dark_mode_available is None:
|
||||
self._dark_mode_available = set_dark_mode(True)
|
||||
self._dark_mode_available = set_macos_dark_mode(True)
|
||||
|
||||
from aqt import mw
|
||||
|
||||
@ -143,28 +152,57 @@ class ThemeManager:
|
||||
def qcolor(self, colors: tuple[str, str]) -> QColor:
|
||||
return QColor(self.color(colors))
|
||||
|
||||
def apply_style(self, app: QApplication) -> None:
|
||||
self.default_palette = QGuiApplication.palette()
|
||||
def _determine_night_mode(self) -> bool:
|
||||
theme = aqt.mw.pm.theme()
|
||||
if theme == Theme.LIGHT:
|
||||
return False
|
||||
elif theme == Theme.DARK:
|
||||
return True
|
||||
else:
|
||||
if isWin:
|
||||
return get_windows_dark_mode()
|
||||
elif isMac:
|
||||
return get_macos_dark_mode()
|
||||
else:
|
||||
# not supported on Linux
|
||||
return False
|
||||
|
||||
def apply_style_if_system_style_changed(self) -> None:
|
||||
theme = aqt.mw.pm.theme()
|
||||
if theme != Theme.FOLLOW_SYSTEM:
|
||||
return
|
||||
if self._determine_night_mode() != self.night_mode:
|
||||
self.apply_style()
|
||||
|
||||
def apply_style(self) -> None:
|
||||
"Apply currently configured style."
|
||||
app = aqt.mw.app
|
||||
self.night_mode = self._determine_night_mode()
|
||||
if not self.default_palette:
|
||||
self.default_palette = QGuiApplication.palette()
|
||||
self._default_style = app.style().objectName()
|
||||
self._apply_palette(app)
|
||||
self._apply_style(app)
|
||||
gui_hooks.theme_did_change()
|
||||
|
||||
def _apply_style(self, app: QApplication) -> None:
|
||||
buf = ""
|
||||
|
||||
if isWin and platform.release() == "10" and not self.night_mode:
|
||||
# add missing bottom border to menubar
|
||||
buf += """
|
||||
QMenuBar {
|
||||
border-bottom: 1px solid #aaa;
|
||||
background: white;
|
||||
}
|
||||
if isWin and platform.release() == "10":
|
||||
# day mode is missing a bottom border; background must be
|
||||
# also set for border to apply
|
||||
buf += f"""
|
||||
QMenuBar {{
|
||||
border-bottom: 1px solid {self.color(colors.BORDER)};
|
||||
background: {self.color(colors.WINDOW_BG)};
|
||||
}}
|
||||
"""
|
||||
# qt bug? setting the above changes the browser sidebar
|
||||
# to white as well, so set it back
|
||||
buf += """
|
||||
QTreeWidget {
|
||||
background: #eee;
|
||||
}
|
||||
buf += f"""
|
||||
QTreeWidget {{
|
||||
background: {self.color(colors.WINDOW_BG)};
|
||||
}}
|
||||
"""
|
||||
|
||||
if self.night_mode:
|
||||
@ -209,7 +247,11 @@ QTabWidget {{ background-color: {}; }}
|
||||
app.setStyleSheet(buf)
|
||||
|
||||
def _apply_palette(self, app: QApplication) -> None:
|
||||
set_macos_dark_mode(self.night_mode)
|
||||
|
||||
if not self.night_mode:
|
||||
app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore
|
||||
app.setPalette(self.default_palette)
|
||||
return
|
||||
|
||||
if not self.macos_dark_mode():
|
||||
|
@ -252,6 +252,7 @@ class AnkiWebView(QWebEngineView):
|
||||
context=Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
||||
activated=self.onEsc,
|
||||
)
|
||||
gui_hooks.theme_did_change.append(self.on_theme_did_change)
|
||||
|
||||
def set_title(self, title: str) -> None:
|
||||
self.title = title # type: ignore[assignment]
|
||||
@ -445,7 +446,7 @@ div[contenteditable="true"]:focus {{
|
||||
lang_dir = "ltr"
|
||||
|
||||
return f"""
|
||||
body {{ zoom: {zoom}; background-color: {body_bg}; direction: {lang_dir}; }}
|
||||
body {{ zoom: {zoom}; background-color: --window-bg; direction: {lang_dir}; }}
|
||||
html {{ {font} }}
|
||||
{button_style}
|
||||
:root {{ --window-bg: {window_bg_day} }}
|
||||
@ -551,6 +552,8 @@ html {{ {font} }}
|
||||
self._maybeRunActions()
|
||||
|
||||
def _maybeRunActions(self) -> None:
|
||||
if sip.isdeleted(self):
|
||||
return
|
||||
while self._pendingActions and self._domDone:
|
||||
name, args = self._pendingActions.pop(0)
|
||||
|
||||
@ -675,4 +678,29 @@ document.head.appendChild(style);
|
||||
# this will fail when __del__ is called during app shutdown
|
||||
return
|
||||
|
||||
gui_hooks.theme_did_change.remove(self.on_theme_did_change)
|
||||
mw.mediaServer.clear_page_html(id(self))
|
||||
|
||||
def on_theme_did_change(self) -> None:
|
||||
# avoid flashes if page reloaded
|
||||
self._page.setBackgroundColor(
|
||||
self.get_window_bg_color(theme_manager.night_mode)
|
||||
)
|
||||
# update night-mode class, and legacy nightMode/night-mode body classes
|
||||
self.eval(
|
||||
f"""
|
||||
(function() {{
|
||||
const doc = document.documentElement.classList;
|
||||
const body = document.body.classList;
|
||||
if ({1 if theme_manager.night_mode else 0}) {{
|
||||
doc.add("night-mode");
|
||||
body.add("night-mode");
|
||||
body.add("nightMode");
|
||||
}} else {{
|
||||
doc.remove("night-mode");
|
||||
body.remove("night-mode");
|
||||
body.remove("nightMode");
|
||||
}}
|
||||
}})();
|
||||
"""
|
||||
)
|
||||
|
@ -4,6 +4,7 @@
|
||||
@import Foundation;
|
||||
@import AppKit;
|
||||
|
||||
/// Force our app to be either light or dark mode.
|
||||
void set_darkmode_enabled(BOOL enabled) {
|
||||
NSAppearance *appearance;
|
||||
if (enabled) {
|
||||
@ -14,3 +15,20 @@ void set_darkmode_enabled(BOOL enabled) {
|
||||
|
||||
[NSApplication sharedApplication].appearance = appearance;
|
||||
}
|
||||
|
||||
/// True if the system is set to dark mode.
|
||||
BOOL system_is_dark(void) {
|
||||
BOOL styleSet = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleInterfaceStyle"] != nil;
|
||||
return styleSet;
|
||||
// FIXME: confirm whether this is required on 10.15/16 (it
|
||||
// does not appear to be on 11)
|
||||
|
||||
// BOOL autoSwitch = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleInterfaceStyleSwitchesAutomatically"];
|
||||
//
|
||||
// if (@available(macOS 10.15, *)) {
|
||||
// return autoSwitch ? !styleSet : styleSet;
|
||||
// } else {
|
||||
// return styleSet;
|
||||
// }
|
||||
|
||||
}
|
||||
|
280
qt/mac/ankihelper.xcodeproj/project.pbxproj
Normal file
280
qt/mac/ankihelper.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,280 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 55;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1327600A274613D9001D63D7 /* AnkiHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 13276009274613D8001D63D7 /* AnkiHelper.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
13276009274613D8001D63D7 /* AnkiHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AnkiHelper.m; sourceTree = "<group>"; };
|
||||
138B770F2746137F003A3E4F /* libankihelper.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libankihelper.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
138B770D2746137F003A3E4F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
138B77062746137F003A3E4F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13276009274613D8001D63D7 /* AnkiHelper.m */,
|
||||
138B77102746137F003A3E4F /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
138B77102746137F003A3E4F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
138B770F2746137F003A3E4F /* libankihelper.dylib */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
138B770B2746137F003A3E4F /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
138B770E2746137F003A3E4F /* ankihelper */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 138B77182746137F003A3E4F /* Build configuration list for PBXNativeTarget "ankihelper" */;
|
||||
buildPhases = (
|
||||
138B770B2746137F003A3E4F /* Headers */,
|
||||
138B770C2746137F003A3E4F /* Sources */,
|
||||
138B770D2746137F003A3E4F /* Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ankihelper;
|
||||
productName = ankihelper;
|
||||
productReference = 138B770F2746137F003A3E4F /* libankihelper.dylib */;
|
||||
productType = "com.apple.product-type.library.dynamic";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
138B77072746137F003A3E4F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastUpgradeCheck = 1310;
|
||||
TargetAttributes = {
|
||||
138B770E2746137F003A3E4F = {
|
||||
CreatedOnToolsVersion = 13.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 138B770A2746137F003A3E4F /* Build configuration list for PBXProject "ankihelper" */;
|
||||
compatibilityVersion = "Xcode 13.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 138B77062746137F003A3E4F;
|
||||
productRefGroup = 138B77102746137F003A3E4F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
138B770E2746137F003A3E4F /* ankihelper */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
138B770C2746137F003A3E4F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1327600A274613D9001D63D7 /* AnkiHelper.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
138B77162746137F003A3E4F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.6;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
138B77172746137F003A3E4F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.6;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
138B77192746137F003A3E4F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7ZM8SLJM4P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
138B771A2746137F003A3E4F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = 7ZM8SLJM4P;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
138B770A2746137F003A3E4F /* Build configuration list for PBXProject "ankihelper" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
138B77162746137F003A3E4F /* Debug */,
|
||||
138B77172746137F003A3E4F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
138B77182746137F003A3E4F /* Build configuration list for PBXNativeTarget "ankihelper" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
138B77192746137F003A3E4F /* Debug */,
|
||||
138B771A2746137F003A3E4F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 138B77072746137F003A3E4F /* Project object */;
|
||||
}
|
7
qt/mac/ankihelper.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
qt/mac/ankihelper.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>ankihelper.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
@ -540,6 +540,10 @@ hooks = [
|
||||
there are no outstanding ops.
|
||||
""",
|
||||
),
|
||||
Hook(
|
||||
name="theme_did_change",
|
||||
doc="Called after night mode is toggled.",
|
||||
),
|
||||
# Webview
|
||||
###################
|
||||
Hook(
|
||||
|
@ -9,7 +9,6 @@ import { ChangeNotetypeState, getChangeNotetypeInfo, getNotetypeNames } from "./
|
||||
import { setupI18n, ModuleName } from "../lib/i18n";
|
||||
import { checkNightMode } from "../lib/nightmode";
|
||||
import ChangeNotetypePage from "./ChangeNotetypePage.svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
|
||||
export async function changeNotetypePage(
|
||||
target: HTMLDivElement,
|
||||
@ -28,14 +27,11 @@ export async function changeNotetypePage(
|
||||
}),
|
||||
]);
|
||||
|
||||
const nightMode = checkNightMode();
|
||||
const context = new Map();
|
||||
context.set(nightModeKey, nightMode);
|
||||
checkNightMode();
|
||||
|
||||
const state = new ChangeNotetypeState(names, info);
|
||||
return new ChangeNotetypePage({
|
||||
target,
|
||||
props: { state },
|
||||
context,
|
||||
} as any);
|
||||
}
|
||||
|
@ -16,13 +16,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext, setContext } from "svelte";
|
||||
import { setContext } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import Item from "./Item.svelte";
|
||||
import type { Registration } from "./registration";
|
||||
import { sectionKey, nightModeKey } from "./context-keys";
|
||||
import { sectionKey } from "./context-keys";
|
||||
import { insertElement, appendElement } from "./identifier";
|
||||
import { makeInterface } from "./registration";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
let className: string = "";
|
||||
@ -89,15 +90,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
$: if (buttonToolbarRef && api) {
|
||||
createApi();
|
||||
}
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={buttonToolbarRef}
|
||||
{id}
|
||||
class="button-toolbar btn-toolbar {className}"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
{style}
|
||||
role="toolbar"
|
||||
on:focusout
|
||||
|
@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey } from "./context-keys";
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
let className = "";
|
||||
@ -15,8 +15,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
let buttonRef: HTMLButtonElement;
|
||||
|
||||
const nightMode = getContext(nightModeKey);
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
onMount(() => dispatch("mount", { button: buttonRef }));
|
||||
</script>
|
||||
@ -26,8 +24,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
tabindex={tabbable ? 0 : -1}
|
||||
bind:this={buttonRef}
|
||||
class="dropdown-item btn {className}"
|
||||
class:btn-day={!nightMode}
|
||||
class:btn-night={nightMode}
|
||||
class:btn-day={!$pageTheme.isDark}
|
||||
class:btn-night={$pageTheme.isDark}
|
||||
title={tooltip}
|
||||
on:click
|
||||
on:mouseenter
|
||||
|
@ -5,7 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
<script lang="ts">
|
||||
import IconConstrain from "./IconConstrain.svelte";
|
||||
import { getContext, onMount, createEventDispatcher } from "svelte";
|
||||
import { nightModeKey, dropdownKey } from "./context-keys";
|
||||
import { dropdownKey } from "./context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
import type { DropdownProps } from "./dropdown";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
@ -23,7 +24,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
let buttonRef: HTMLButtonElement;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
@ -36,8 +36,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
class="icon-button btn {className}"
|
||||
class:active
|
||||
class:dropdown-toggle={dropdownProps.dropdown}
|
||||
class:btn-day={!nightMode}
|
||||
class:btn-night={nightMode}
|
||||
class:btn-day={!$pageTheme.isDark}
|
||||
class:btn-night={$pageTheme.isDark}
|
||||
title={tooltip}
|
||||
{...dropdownProps}
|
||||
{disabled}
|
||||
|
@ -4,8 +4,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey, dropdownKey } from "./context-keys";
|
||||
import { dropdownKey } from "./context-keys";
|
||||
import type { DropdownProps } from "./dropdown";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
let className: string = "";
|
||||
@ -21,7 +22,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
export let disabled = false;
|
||||
export let tabbable = false;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
||||
|
||||
let buttonRef: HTMLButtonElement;
|
||||
@ -36,8 +36,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
class="label-button {extendClassName(className, theme)}"
|
||||
class:active
|
||||
class:dropdown-toggle={dropdownProps.dropdown}
|
||||
class:btn-day={theme === "anki" && !nightMode}
|
||||
class:btn-night={theme === "anki" && nightMode}
|
||||
class:btn-day={theme === "anki" && !$pageTheme.isDark}
|
||||
class:btn-night={theme === "anki" && $pageTheme.isDark}
|
||||
title={tooltip}
|
||||
{...dropdownProps}
|
||||
{disabled}
|
||||
|
@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey } from "./context-keys";
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
let className = "";
|
||||
@ -13,8 +13,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
export let tooltip: string | undefined = undefined;
|
||||
export let disabled = false;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
||||
let buttonRef: HTMLSelectElement;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
@ -29,9 +27,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
{id}
|
||||
{disabled}
|
||||
class="{className} form-select"
|
||||
class:btn-day={!nightMode}
|
||||
class:btn-night={nightMode}
|
||||
class:visible-down-arrow={nightMode}
|
||||
class:btn-day={!$pageTheme.isDark}
|
||||
class:btn-night={$pageTheme.isDark}
|
||||
class:visible-down-arrow={$pageTheme.isDark}
|
||||
title={tooltip}
|
||||
on:change
|
||||
>
|
||||
|
@ -1,6 +1,5 @@
|
||||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
export const nightModeKey = Symbol("nightMode");
|
||||
export const touchDeviceKey = Symbol("touchDevice");
|
||||
|
||||
export const sectionKey = Symbol("section");
|
||||
|
@ -3,19 +3,16 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let choices: string[];
|
||||
export let value: number = 0;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
</script>
|
||||
|
||||
<select
|
||||
bind:value
|
||||
class:nightMode
|
||||
class:visible-down-arrow={nightMode}
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class:visible-down-arrow={$pageTheme.isDark}
|
||||
class="enum-selector form-select"
|
||||
>
|
||||
{#each choices as choice, idx}
|
||||
|
@ -3,15 +3,12 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let value: number;
|
||||
export let min = 1;
|
||||
export let max = 9999;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
||||
function checkMinMax() {
|
||||
if (value > max) {
|
||||
value = max;
|
||||
@ -29,7 +26,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
{max}
|
||||
bind:value
|
||||
class="spin-box form-control"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
on:blur={checkMinMax}
|
||||
/>
|
||||
|
||||
|
@ -3,8 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let value: number;
|
||||
export let min = 1;
|
||||
@ -13,8 +12,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
let stringValue: string;
|
||||
$: stringValue = value.toFixed(2);
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
||||
function update(this: HTMLInputElement): void {
|
||||
value = Math.min(max, Math.max(min, parseFloat(this.value)));
|
||||
}
|
||||
@ -23,7 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
<input
|
||||
type="number"
|
||||
class="form-control"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
{min}
|
||||
{max}
|
||||
step="0.01"
|
||||
|
@ -3,8 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
import { stepsToString, stringToSteps } from "./steps";
|
||||
|
||||
export let value: number[];
|
||||
@ -12,8 +11,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
let stringValue: string;
|
||||
$: stringValue = stepsToString(value);
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
||||
function update(this: HTMLInputElement): void {
|
||||
value = stringToSteps(this.value);
|
||||
}
|
||||
@ -23,7 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
type="text"
|
||||
value={stringValue}
|
||||
class="form-control"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
on:blur={update}
|
||||
/>
|
||||
|
||||
|
@ -3,14 +3,11 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined;
|
||||
export let value: boolean;
|
||||
export let disabled = false;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
</script>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
@ -18,7 +15,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
{id}
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
bind:checked={value}
|
||||
{disabled}
|
||||
/>
|
||||
|
@ -4,7 +4,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy, getContext } from "svelte";
|
||||
import { nightModeKey, modalsKey } from "../components/context-keys";
|
||||
import { modalsKey } from "../components/context-keys";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
import Modal from "bootstrap/js/dist/modal";
|
||||
|
||||
export let title: string;
|
||||
@ -40,8 +41,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
onDestroy(() => {
|
||||
modalRef.removeEventListener("shown.bs.modal", onShown);
|
||||
});
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
</script>
|
||||
|
||||
<div
|
||||
@ -52,13 +51,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content" class:default-colors={nightMode}>
|
||||
<div class="modal-content" class:default-colors={$pageTheme.isDark}>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="modalLabel">{title}</h5>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-close"
|
||||
class:invert={nightMode}
|
||||
class:invert={$pageTheme.isDark}
|
||||
data-bs-dismiss="modal"
|
||||
aria-label="Close"
|
||||
/>
|
||||
@ -73,7 +72,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
id="prompt-input"
|
||||
bind:this={inputRef}
|
||||
type="text"
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class="form-control"
|
||||
bind:value
|
||||
/>
|
||||
|
@ -11,7 +11,7 @@ import { getDeckOptionsInfo, DeckOptionsState } from "./lib";
|
||||
import { setupI18n, ModuleName } from "../lib/i18n";
|
||||
import { checkNightMode } from "../lib/nightmode";
|
||||
import DeckOptionsPage from "./DeckOptionsPage.svelte";
|
||||
import { nightModeKey, touchDeviceKey, modalsKey } from "../components/context-keys";
|
||||
import { touchDeviceKey, modalsKey } from "../components/context-keys";
|
||||
|
||||
export async function deckOptions(
|
||||
target: HTMLDivElement,
|
||||
@ -29,9 +29,9 @@ export async function deckOptions(
|
||||
}),
|
||||
]);
|
||||
|
||||
checkNightMode();
|
||||
|
||||
const context = new Map();
|
||||
const nightMode = checkNightMode();
|
||||
context.set(nightModeKey, nightMode);
|
||||
|
||||
const modals = new Map();
|
||||
context.set(modalsKey, modals);
|
||||
|
@ -18,8 +18,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { onDestroy, getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { onDestroy } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
import { convertMathjax } from "./mathjax";
|
||||
import { randomUUID } from "../lib/uuid";
|
||||
import { writable } from "svelte/store";
|
||||
@ -30,8 +30,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
export let autofocus = false;
|
||||
export let fontSize = 20;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
$: [converted, title] = convertMathjax(mathjax, nightMode, fontSize);
|
||||
$: [converted, title] = convertMathjax(mathjax, $pageTheme.isDark, fontSize);
|
||||
$: empty = title === "MathJax";
|
||||
$: encoded = encodeURIComponent(converted);
|
||||
|
||||
|
@ -11,7 +11,6 @@ import type { DecoratedElement, DecoratedElementConstructor } from "./decorated"
|
||||
import { nodeIsElement } from "../lib/dom";
|
||||
import { noop } from "../lib/functional";
|
||||
import { placeCaretAfter } from "../domlib/place-caret";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
|
||||
import Mathjax_svelte from "./Mathjax.svelte";
|
||||
|
||||
@ -148,12 +147,6 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
|
||||
this.innerHTML = "";
|
||||
this.style.whiteSpace = "normal";
|
||||
|
||||
const context = new Map();
|
||||
context.set(
|
||||
nightModeKey,
|
||||
document.documentElement.classList.contains("night-mode"),
|
||||
);
|
||||
|
||||
this.component = new Mathjax_svelte({
|
||||
target: this,
|
||||
props: {
|
||||
@ -161,7 +154,6 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
|
||||
block: this.block,
|
||||
autofocus: this.hasAttribute("focusonmount"),
|
||||
},
|
||||
context,
|
||||
} as any);
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let id: string | undefined = undefined;
|
||||
let className = "";
|
||||
@ -12,8 +12,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
export let selected = false;
|
||||
export let active = false;
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let buttonRef: HTMLButtonElement;
|
||||
@ -33,8 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
{id}
|
||||
tabindex="-1"
|
||||
class="autocomplete-item btn {className}"
|
||||
class:btn-day={!nightMode}
|
||||
class:btn-night={nightMode}
|
||||
class:btn-day={!$pageTheme.isDark}
|
||||
class:btn-night={$pageTheme.isDark}
|
||||
class:selected
|
||||
class:active
|
||||
on:mousedown|preventDefault
|
||||
|
@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let offsetX = 0;
|
||||
export let offsetY = 0;
|
||||
@ -13,7 +13,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
export let activeSize = 5;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const nightMode = getContext(nightModeKey);
|
||||
|
||||
const onPointerdown =
|
||||
(north: boolean, west: boolean) =>
|
||||
@ -26,9 +25,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
class="d-contents"
|
||||
style="--offsetX: {offsetX}px; --offsetY: {offsetY}px; --activeSize: {activeSize}px;"
|
||||
>
|
||||
<div class:nightMode class="bordered" on:mousedown|preventDefault />
|
||||
<div
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class="bordered"
|
||||
on:mousedown|preventDefault
|
||||
/>
|
||||
<div
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class:active
|
||||
class="control nw"
|
||||
on:mousedown|preventDefault
|
||||
@ -36,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
on:pointermove
|
||||
/>
|
||||
<div
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class:active
|
||||
class="control ne"
|
||||
on:mousedown|preventDefault
|
||||
@ -44,7 +47,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
on:pointermove
|
||||
/>
|
||||
<div
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class:active
|
||||
class="control sw"
|
||||
on:mousedown|preventDefault
|
||||
@ -52,7 +55,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
on:pointermove
|
||||
/>
|
||||
<div
|
||||
class:nightMode
|
||||
class:nightMode={$pageTheme.isDark}
|
||||
class:active
|
||||
class="control se"
|
||||
on:mousedown|preventDefault
|
||||
|
@ -7,6 +7,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
import type { EditingInputAPI } from "./EditingArea.svelte";
|
||||
import contextProperty from "../sveltelib/context-property";
|
||||
import type { OnNextInsertTrigger } from "../sveltelib/input-manager";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export interface RichTextInputAPI extends EditingInputAPI {
|
||||
name: "rich-text";
|
||||
@ -38,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
import SetContext from "./SetContext.svelte";
|
||||
import ContentEditable from "../editable/ContentEditable.svelte";
|
||||
|
||||
import { onMount, getContext, getAllContexts } from "svelte";
|
||||
import { onMount, getAllContexts } from "svelte";
|
||||
import {
|
||||
nodeIsElement,
|
||||
nodeContainsInlineContent,
|
||||
@ -53,7 +54,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
import { on } from "../lib/events";
|
||||
import { nodeStore } from "../sveltelib/node-store";
|
||||
import type { DecoratedElement } from "../editable/decorated";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
|
||||
export let hidden: boolean;
|
||||
|
||||
@ -235,12 +235,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
unsubscribeToEditingArea();
|
||||
};
|
||||
});
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
</script>
|
||||
|
||||
<RichTextStyles
|
||||
color={nightMode ? "white" : "black"}
|
||||
color={$pageTheme.isDark ? "white" : "black"}
|
||||
let:attachToShadow={attachStyles}
|
||||
let:promise={stylesPromise}
|
||||
let:stylesDidLoad
|
||||
|
@ -3,8 +3,8 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount, getContext, createEventDispatcher } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { onMount, createEventDispatcher } from "svelte";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
let className: string = "";
|
||||
export { className as class };
|
||||
@ -21,8 +21,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
setTimeout(() => (flashing = false), 300);
|
||||
}
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
|
||||
let button: HTMLButtonElement;
|
||||
|
||||
onMount(() => dispatch("mount", { button }));
|
||||
@ -33,8 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
class="tag btn d-inline-flex align-items-center text-nowrap ps-2 pe-1 {className}"
|
||||
class:selected
|
||||
class:flashing
|
||||
class:btn-day={!nightMode}
|
||||
class:btn-night={nightMode}
|
||||
class:btn-day={!$pageTheme.isDark}
|
||||
class:btn-night={$pageTheme.isDark}
|
||||
tabindex="-1"
|
||||
title={tooltip}
|
||||
on:mousemove
|
||||
|
@ -6,10 +6,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
import Tag from "./Tag.svelte";
|
||||
import WithTooltip from "../components/WithTooltip.svelte";
|
||||
|
||||
import { createEventDispatcher, getContext } from "svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { controlPressed, shiftPressed } from "../lib/keys";
|
||||
import { delimChar } from "./tags";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
export let name: string;
|
||||
let className: string = "";
|
||||
@ -58,14 +58,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
function hasMultipleParts(name: string): boolean {
|
||||
return name.split(delimChar).length > 1;
|
||||
}
|
||||
|
||||
const nightMode = getContext<boolean>(nightModeKey);
|
||||
const hoverClass = "tag-icon-hover";
|
||||
</script>
|
||||
|
||||
<svelte:body on:keydown={setControlShift} on:keyup={setControlShift} />
|
||||
|
||||
<div class:select-mode={selectMode} class:night-mode={nightMode}>
|
||||
<div class:select-mode={selectMode} class:night-mode={$pageTheme.isDark}>
|
||||
{#if active}
|
||||
<Tag class={className} on:mousemove={setControlShift} on:click={onClick}>
|
||||
{name}
|
||||
|
@ -62,16 +62,8 @@ export const i18n = setupI18n({
|
||||
|
||||
import OldEditorAdapter from "./OldEditorAdapter.svelte";
|
||||
import type { NoteEditorAPI } from "./OldEditorAdapter.svelte";
|
||||
import { nightModeKey } from "../components/context-keys";
|
||||
|
||||
async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
||||
const context = new Map<symbol, unknown>();
|
||||
|
||||
context.set(
|
||||
nightModeKey,
|
||||
document.documentElement.classList.contains("night-mode"),
|
||||
);
|
||||
|
||||
await i18n;
|
||||
|
||||
const api: Partial<NoteEditorAPI> = {};
|
||||
@ -79,7 +71,6 @@ async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
||||
const noteEditor = new OldEditorAdapter({
|
||||
target: document.body,
|
||||
props: { api: api as NoteEditorAPI },
|
||||
context,
|
||||
});
|
||||
|
||||
Object.assign(globalThis, {
|
||||
|
@ -5,12 +5,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
<script lang="ts">
|
||||
import type { SvelteComponent } from "svelte/internal";
|
||||
import { writable } from "svelte/store";
|
||||
import { pageTheme } from "../sveltelib/theme";
|
||||
|
||||
import { bridgeCommand } from "../lib/bridgecommand";
|
||||
|
||||
import WithGraphData from "./WithGraphData.svelte";
|
||||
|
||||
export let nightMode: boolean;
|
||||
export let graphs: SvelteComponent[];
|
||||
|
||||
export let initialSearch: string;
|
||||
@ -45,7 +45,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
{sourceData}
|
||||
{preferences}
|
||||
{revlogRange}
|
||||
{nightMode}
|
||||
nightMode={$pageTheme.isDark}
|
||||
on:search={browserSearch}
|
||||
/>
|
||||
{/each}
|
||||
|
@ -31,14 +31,12 @@ export function graphs(
|
||||
controller = null as SvelteComponent | null,
|
||||
} = {},
|
||||
): void {
|
||||
const nightMode = checkNightMode();
|
||||
|
||||
checkNightMode();
|
||||
setupI18n({ modules: [ModuleName.STATISTICS, ModuleName.SCHEDULING] }).then(() => {
|
||||
new GraphsPage({
|
||||
target,
|
||||
props: {
|
||||
graphs,
|
||||
nightMode,
|
||||
initialSearch: search,
|
||||
initialDays: days,
|
||||
controller,
|
||||
|
@ -1,8 +1,8 @@
|
||||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
/// Add night-mode class to body if hash location is #night, and return
|
||||
/// true if added.
|
||||
/// Add night-mode class to documentElement if hash location is #night, and
|
||||
/// return true if added.
|
||||
export function checkNightMode(): boolean {
|
||||
const nightMode = window.location.hash == "#night";
|
||||
if (nightMode) {
|
||||
|
35
ts/sveltelib/theme.ts
Normal file
35
ts/sveltelib/theme.ts
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
import { readable, get } from "svelte/store";
|
||||
import { registerPackage } from "../lib/register-package";
|
||||
|
||||
interface ThemeInfo {
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
function getThemeFromRoot(): ThemeInfo {
|
||||
return {
|
||||
isDark: document.documentElement.classList.contains("night-mode"),
|
||||
};
|
||||
}
|
||||
|
||||
let setPageTheme: ((theme: ThemeInfo) => void) | null = null;
|
||||
/// The current theme that applies to this document/shadow root. When
|
||||
/// previewing cards in the card layout screen, this may not match the
|
||||
/// theme Anki is using in its UI.
|
||||
export const pageTheme = readable(getThemeFromRoot(), (set) => {
|
||||
setPageTheme = set;
|
||||
});
|
||||
// ensure setPageTheme is set immediately
|
||||
get(pageTheme);
|
||||
|
||||
// Update theme when root element's class changes.
|
||||
const observer = new MutationObserver((_mutationsList, _observer) => {
|
||||
setPageTheme!(getThemeFromRoot());
|
||||
});
|
||||
observer.observe(document.documentElement, { attributeFilter: ["class"] });
|
||||
|
||||
registerPackage("anki/theme", {
|
||||
pageTheme,
|
||||
});
|
Loading…
Reference in New Issue
Block a user