mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
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
46 changed files with 659 additions and 182 deletions
|
@ -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-legacy-timezone-handling = Legacy timezone handling (buggy, but required for AnkiDroid <= 2.14)
|
||||||
preferences-default-search-text = Default search text
|
preferences-default-search-text = Default search text
|
||||||
preferences-default-search-text-example = eg. 'deck:current '
|
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
|
preferences-v3-scheduler = V3 scheduler
|
||||||
|
|
|
@ -19,6 +19,14 @@ class SidebarSearchBar(QLineEdit):
|
||||||
self.timer.setInterval(600)
|
self.timer.setInterval(600)
|
||||||
self.timer.setSingleShot(True)
|
self.timer.setSingleShot(True)
|
||||||
self.setFrame(False)
|
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)
|
border = theme_manager.color(colors.MEDIUM_BORDER)
|
||||||
styles = [
|
styles = [
|
||||||
"padding: 1px",
|
"padding: 1px",
|
||||||
|
@ -32,9 +40,6 @@ class SidebarSearchBar(QLineEdit):
|
||||||
|
|
||||||
self.setStyleSheet("QLineEdit { %s }" % ";".join(styles))
|
self.setStyleSheet("QLineEdit { %s }" % ";".join(styles))
|
||||||
|
|
||||||
qconnect(self.timer.timeout, self.onSearch)
|
|
||||||
qconnect(self.textChanged, self.onTextChanged)
|
|
||||||
|
|
||||||
def onTextChanged(self, text: str) -> None:
|
def onTextChanged(self, text: str) -> None:
|
||||||
if not self.timer.isActive():
|
if not self.timer.isActive():
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
|
@ -49,3 +54,6 @@ class SidebarSearchBar(QLineEdit):
|
||||||
self.onSearch()
|
self.onSearch()
|
||||||
else:
|
else:
|
||||||
QLineEdit.keyPressEvent(self, evt)
|
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.setIconSize(QSize(16, 16))
|
||||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed)
|
||||||
self.setStyle(QStyleFactory.create("fusion"))
|
self.setStyle(QStyleFactory.create("fusion"))
|
||||||
|
aqt.gui_hooks.theme_did_change.append(self._update_icons)
|
||||||
|
|
||||||
def _setup_tools(self) -> None:
|
def _setup_tools(self) -> None:
|
||||||
for row, tool in enumerate(self._tools):
|
for row, tool in enumerate(self._tools):
|
||||||
|
@ -48,3 +49,10 @@ class SidebarToolbar(QToolBar):
|
||||||
def _on_action_group_triggered(self, action: QAction) -> None:
|
def _on_action_group_triggered(self, action: QAction) -> None:
|
||||||
index = self._action_group.actions().index(action)
|
index = self._action_group.actions().index(action)
|
||||||
self.sidebar.tool = self._tools[index][0]
|
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.expanded, self._on_expansion)
|
||||||
qconnect(self.collapsed, self._on_collapse)
|
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
|
# match window background color and tweak style
|
||||||
bgcolor = QPalette().window().color().name()
|
bgcolor = QPalette().window().color().name()
|
||||||
border = theme_manager.color(colors.MEDIUM_BORDER)
|
border = theme_manager.color(colors.MEDIUM_BORDER)
|
||||||
|
@ -105,14 +115,11 @@ class SidebarTreeView(QTreeView):
|
||||||
|
|
||||||
self.setStyleSheet("QTreeView { %s }" % ";".join(styles))
|
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:
|
def cleanup(self) -> None:
|
||||||
|
self.toolbar.cleanup()
|
||||||
|
self.searchBar.cleanup()
|
||||||
gui_hooks.flag_label_did_change.remove(self.refresh)
|
gui_hooks.flag_label_did_change.remove(self.refresh)
|
||||||
|
gui_hooks.theme_did_change.remove(self._setup_style)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tool(self) -> SidebarTool:
|
def tool(self) -> SidebarTool:
|
||||||
|
|
|
@ -59,6 +59,7 @@ class Table:
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
self._save_header()
|
self._save_header()
|
||||||
|
gui_hooks.theme_did_change.remove(self._setup_style)
|
||||||
|
|
||||||
# Public Methods
|
# Public Methods
|
||||||
######################################################################
|
######################################################################
|
||||||
|
@ -342,17 +343,10 @@ class Table:
|
||||||
self._view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
self._view.setHorizontalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||||
self._view.horizontalScrollBar().setSingleStep(10)
|
self._view.horizontalScrollBar().setSingleStep(10)
|
||||||
self._update_font()
|
self._update_font()
|
||||||
if not theme_manager.night_mode:
|
self._setup_style()
|
||||||
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._view.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
self._view.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
||||||
qconnect(self._view.customContextMenuRequested, self._on_context_menu)
|
qconnect(self._view.customContextMenuRequested, self._on_context_menu)
|
||||||
|
gui_hooks.theme_did_change.append(self._setup_style)
|
||||||
|
|
||||||
def _update_font(self) -> None:
|
def _update_font(self) -> None:
|
||||||
# we can't choose different line heights efficiently, so we need
|
# we can't choose different line heights efficiently, so we need
|
||||||
|
@ -365,6 +359,19 @@ class Table:
|
||||||
curmax = bsize
|
curmax = bsize
|
||||||
self._view.verticalHeader().setDefaultSectionSize(curmax + 6)
|
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:
|
def _setup_headers(self) -> None:
|
||||||
vh = self._view.verticalHeader()
|
vh = self._view.verticalHeader()
|
||||||
hh = self._view.horizontalHeader()
|
hh = self._view.horizontalHeader()
|
||||||
|
|
|
@ -55,7 +55,7 @@ class CardLayout(QDialog):
|
||||||
self.model = note.note_type()
|
self.model = note.note_type()
|
||||||
self.templates = self.model["tmpls"]
|
self.templates = self.model["tmpls"]
|
||||||
self.fill_empty_action_toggled = fill_empty
|
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.mobile_emulation_enabled = False
|
||||||
self.have_autoplayed = False
|
self.have_autoplayed = False
|
||||||
self.mm._remove_from_cache(self.model["id"])
|
self.mm._remove_from_cache(self.model["id"])
|
||||||
|
|
|
@ -55,6 +55,9 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="theme"/>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="video_driver"/>
|
<widget class="QComboBox" name="video_driver"/>
|
||||||
</item>
|
</item>
|
||||||
|
@ -86,13 +89,6 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="nightMode">
|
|
||||||
<property name="text">
|
|
||||||
<string>preferences_night_mode</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="useCurrent">
|
<widget class="QComboBox" name="useCurrent">
|
||||||
<item>
|
<item>
|
||||||
|
@ -602,12 +598,12 @@
|
||||||
</widget>
|
</widget>
|
||||||
<tabstops>
|
<tabstops>
|
||||||
<tabstop>lang</tabstop>
|
<tabstop>lang</tabstop>
|
||||||
|
<tabstop>theme</tabstop>
|
||||||
<tabstop>video_driver</tabstop>
|
<tabstop>video_driver</tabstop>
|
||||||
<tabstop>showPlayButtons</tabstop>
|
<tabstop>showPlayButtons</tabstop>
|
||||||
<tabstop>interrupt_audio</tabstop>
|
<tabstop>interrupt_audio</tabstop>
|
||||||
<tabstop>pastePNG</tabstop>
|
<tabstop>pastePNG</tabstop>
|
||||||
<tabstop>paste_strips_formatting</tabstop>
|
<tabstop>paste_strips_formatting</tabstop>
|
||||||
<tabstop>nightMode</tabstop>
|
|
||||||
<tabstop>useCurrent</tabstop>
|
<tabstop>useCurrent</tabstop>
|
||||||
<tabstop>default_search_text</tabstop>
|
<tabstop>default_search_text</tabstop>
|
||||||
<tabstop>uiScale</tabstop>
|
<tabstop>uiScale</tabstop>
|
||||||
|
|
|
@ -47,7 +47,7 @@ from aqt.qt import *
|
||||||
from aqt.qt import sip
|
from aqt.qt import sip
|
||||||
from aqt.sync import sync_collection, sync_login
|
from aqt.sync import sync_collection, sync_login
|
||||||
from aqt.taskman import TaskManager
|
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.undo import UndoActionsInfo
|
||||||
from aqt.utils import (
|
from aqt.utils import (
|
||||||
HelpPage,
|
HelpPage,
|
||||||
|
@ -145,11 +145,11 @@ class AnkiQt(QMainWindow):
|
||||||
self.setupMediaServer()
|
self.setupMediaServer()
|
||||||
self.setupSound()
|
self.setupSound()
|
||||||
self.setupSpellCheck()
|
self.setupSpellCheck()
|
||||||
|
self.setupProgress()
|
||||||
self.setupStyle()
|
self.setupStyle()
|
||||||
self.setupMainWindow()
|
self.setupMainWindow()
|
||||||
self.setupSystemSpecific()
|
self.setupSystemSpecific()
|
||||||
self.setupMenus()
|
self.setupMenus()
|
||||||
self.setupProgress()
|
|
||||||
self.setupErrorHandler()
|
self.setupErrorHandler()
|
||||||
self.setupSignals()
|
self.setupSignals()
|
||||||
self.setupAutoUpdate()
|
self.setupAutoUpdate()
|
||||||
|
@ -1004,8 +1004,14 @@ title="{}" {}>{}</button>""".format(
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setupStyle(self) -> None:
|
def setupStyle(self) -> None:
|
||||||
theme_manager.night_mode = self.pm.night_mode()
|
theme_manager.apply_style()
|
||||||
theme_manager.apply_style(self.app)
|
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
|
# Key handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -3,20 +3,40 @@
|
||||||
|
|
||||||
"""Platform-specific functionality."""
|
"""Platform-specific functionality."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from ctypes import CDLL
|
from ctypes import CDLL
|
||||||
|
|
||||||
import aqt.utils
|
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."
|
"True if setting successful."
|
||||||
if not isMac:
|
if not isMac:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
_set_dark_mode(enabled)
|
_ankihelper().set_darkmode_enabled(enabled)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# swallow exceptions, as library will fail on macOS 10.13
|
# swallow exceptions, as library will fail on macOS 10.13
|
||||||
|
@ -24,9 +44,28 @@ def set_dark_mode(enabled: bool) -> bool:
|
||||||
return False
|
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):
|
if getattr(sys, "frozen", False):
|
||||||
path = os.path.join(sys.prefix, "libankihelper.dylib")
|
path = os.path.join(sys.prefix, "libankihelper.dylib")
|
||||||
else:
|
else:
|
||||||
path = os.path.join(aqt.utils.aqt_data_folder(), "lib", "libankihelper.dylib")
|
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.operations.collection import set_preferences
|
||||||
from aqt.profiles import VideoDriver
|
from aqt.profiles import VideoDriver
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
from aqt.theme import Theme
|
||||||
from aqt.utils import HelpPage, disable_help_button, openHelp, showInfo, showWarning, tr
|
from aqt.utils import HelpPage, disable_help_button, openHelp, showInfo, showWarning, tr
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,7 +200,17 @@ class Preferences(QDialog):
|
||||||
def setup_global(self) -> None:
|
def setup_global(self) -> None:
|
||||||
"Setup options global to all profiles."
|
"Setup options global to all profiles."
|
||||||
self.form.uiScale.setValue(int(self.mw.pm.uiScale() * 100))
|
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_language()
|
||||||
self.setup_video_driver()
|
self.setup_video_driver()
|
||||||
|
@ -216,15 +227,14 @@ class Preferences(QDialog):
|
||||||
self.mw.pm.setUiScale(newScale)
|
self.mw.pm.setUiScale(newScale)
|
||||||
restart_required = True
|
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:
|
if restart_required:
|
||||||
showInfo(tr.preferences_changes_will_take_effect_when_you())
|
showInfo(tr.preferences_changes_will_take_effect_when_you())
|
||||||
|
|
||||||
self.updateOptions()
|
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
|
# legacy - one of Henrik's add-ons is currently wrapping them
|
||||||
|
|
||||||
def setupOptions(self) -> None:
|
def setupOptions(self) -> None:
|
||||||
|
|
|
@ -23,6 +23,7 @@ from anki.sync import SyncAuth
|
||||||
from anki.utils import int_time, isMac, isWin
|
from anki.utils import int_time, isMac, isWin
|
||||||
from aqt import appHelpSite
|
from aqt import appHelpSite
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
from aqt.theme import Theme
|
||||||
from aqt.utils import disable_help_button, showWarning, tr
|
from aqt.utils import disable_help_button, showWarning, tr
|
||||||
|
|
||||||
# Profile handling
|
# Profile handling
|
||||||
|
@ -515,6 +516,12 @@ create table if not exists profiles
|
||||||
def set_night_mode(self, on: bool) -> None:
|
def set_night_mode(self, on: bool) -> None:
|
||||||
self.meta["night_mode"] = on
|
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:
|
def dark_mode_widgets(self) -> bool:
|
||||||
return self.meta.get("dark_mode_widgets", False)
|
return self.meta.get("dark_mode_widgets", False)
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,14 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import enum
|
||||||
import platform
|
import platform
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import aqt
|
||||||
from anki.utils import isMac
|
from anki.utils import isMac
|
||||||
from aqt import QApplication, colors, gui_hooks, isWin
|
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 (
|
from aqt.qt import (
|
||||||
QColor,
|
QColor,
|
||||||
QGuiApplication,
|
QGuiApplication,
|
||||||
|
@ -37,6 +39,12 @@ class ColoredIcon:
|
||||||
return ColoredIcon(path=self.path, color=color)
|
return ColoredIcon(path=self.path, color=color)
|
||||||
|
|
||||||
|
|
||||||
|
class Theme(enum.IntEnum):
|
||||||
|
FOLLOW_SYSTEM = 0
|
||||||
|
LIGHT = 1
|
||||||
|
DARK = 2
|
||||||
|
|
||||||
|
|
||||||
class ThemeManager:
|
class ThemeManager:
|
||||||
_night_mode_preference = False
|
_night_mode_preference = False
|
||||||
_icon_cache_light: dict[str, QIcon] = {}
|
_icon_cache_light: dict[str, QIcon] = {}
|
||||||
|
@ -44,6 +52,7 @@ class ThemeManager:
|
||||||
_icon_size = 128
|
_icon_size = 128
|
||||||
_dark_mode_available: bool | None = None
|
_dark_mode_available: bool | None = None
|
||||||
default_palette: QPalette | None = None
|
default_palette: QPalette | None = None
|
||||||
|
_default_style: str | None = None
|
||||||
|
|
||||||
# Qt applies a gradient to the buttons in dark mode
|
# Qt applies a gradient to the buttons in dark mode
|
||||||
# from about #505050 to #606060.
|
# from about #505050 to #606060.
|
||||||
|
@ -58,7 +67,7 @@ class ThemeManager:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self._dark_mode_available is None:
|
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
|
from aqt import mw
|
||||||
|
|
||||||
|
@ -143,28 +152,57 @@ class ThemeManager:
|
||||||
def qcolor(self, colors: tuple[str, str]) -> QColor:
|
def qcolor(self, colors: tuple[str, str]) -> QColor:
|
||||||
return QColor(self.color(colors))
|
return QColor(self.color(colors))
|
||||||
|
|
||||||
def apply_style(self, app: QApplication) -> None:
|
def _determine_night_mode(self) -> bool:
|
||||||
self.default_palette = QGuiApplication.palette()
|
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_palette(app)
|
||||||
self._apply_style(app)
|
self._apply_style(app)
|
||||||
|
gui_hooks.theme_did_change()
|
||||||
|
|
||||||
def _apply_style(self, app: QApplication) -> None:
|
def _apply_style(self, app: QApplication) -> None:
|
||||||
buf = ""
|
buf = ""
|
||||||
|
|
||||||
if isWin and platform.release() == "10" and not self.night_mode:
|
if isWin and platform.release() == "10":
|
||||||
# add missing bottom border to menubar
|
# day mode is missing a bottom border; background must be
|
||||||
buf += """
|
# also set for border to apply
|
||||||
QMenuBar {
|
buf += f"""
|
||||||
border-bottom: 1px solid #aaa;
|
QMenuBar {{
|
||||||
background: white;
|
border-bottom: 1px solid {self.color(colors.BORDER)};
|
||||||
}
|
background: {self.color(colors.WINDOW_BG)};
|
||||||
|
}}
|
||||||
"""
|
"""
|
||||||
# qt bug? setting the above changes the browser sidebar
|
# qt bug? setting the above changes the browser sidebar
|
||||||
# to white as well, so set it back
|
# to white as well, so set it back
|
||||||
buf += """
|
buf += f"""
|
||||||
QTreeWidget {
|
QTreeWidget {{
|
||||||
background: #eee;
|
background: {self.color(colors.WINDOW_BG)};
|
||||||
}
|
}}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.night_mode:
|
if self.night_mode:
|
||||||
|
@ -209,7 +247,11 @@ QTabWidget {{ background-color: {}; }}
|
||||||
app.setStyleSheet(buf)
|
app.setStyleSheet(buf)
|
||||||
|
|
||||||
def _apply_palette(self, app: QApplication) -> None:
|
def _apply_palette(self, app: QApplication) -> None:
|
||||||
|
set_macos_dark_mode(self.night_mode)
|
||||||
|
|
||||||
if not self.night_mode:
|
if not self.night_mode:
|
||||||
|
app.setStyle(QStyleFactory.create(self._default_style)) # type: ignore
|
||||||
|
app.setPalette(self.default_palette)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.macos_dark_mode():
|
if not self.macos_dark_mode():
|
||||||
|
|
|
@ -252,6 +252,7 @@ class AnkiWebView(QWebEngineView):
|
||||||
context=Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
context=Qt.ShortcutContext.WidgetWithChildrenShortcut,
|
||||||
activated=self.onEsc,
|
activated=self.onEsc,
|
||||||
)
|
)
|
||||||
|
gui_hooks.theme_did_change.append(self.on_theme_did_change)
|
||||||
|
|
||||||
def set_title(self, title: str) -> None:
|
def set_title(self, title: str) -> None:
|
||||||
self.title = title # type: ignore[assignment]
|
self.title = title # type: ignore[assignment]
|
||||||
|
@ -445,7 +446,7 @@ div[contenteditable="true"]:focus {{
|
||||||
lang_dir = "ltr"
|
lang_dir = "ltr"
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
body {{ zoom: {zoom}; background-color: {body_bg}; direction: {lang_dir}; }}
|
body {{ zoom: {zoom}; background-color: --window-bg; direction: {lang_dir}; }}
|
||||||
html {{ {font} }}
|
html {{ {font} }}
|
||||||
{button_style}
|
{button_style}
|
||||||
:root {{ --window-bg: {window_bg_day} }}
|
:root {{ --window-bg: {window_bg_day} }}
|
||||||
|
@ -551,6 +552,8 @@ html {{ {font} }}
|
||||||
self._maybeRunActions()
|
self._maybeRunActions()
|
||||||
|
|
||||||
def _maybeRunActions(self) -> None:
|
def _maybeRunActions(self) -> None:
|
||||||
|
if sip.isdeleted(self):
|
||||||
|
return
|
||||||
while self._pendingActions and self._domDone:
|
while self._pendingActions and self._domDone:
|
||||||
name, args = self._pendingActions.pop(0)
|
name, args = self._pendingActions.pop(0)
|
||||||
|
|
||||||
|
@ -675,4 +678,29 @@ document.head.appendChild(style);
|
||||||
# this will fail when __del__ is called during app shutdown
|
# this will fail when __del__ is called during app shutdown
|
||||||
return
|
return
|
||||||
|
|
||||||
|
gui_hooks.theme_did_change.remove(self.on_theme_did_change)
|
||||||
mw.mediaServer.clear_page_html(id(self))
|
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 Foundation;
|
||||||
@import AppKit;
|
@import AppKit;
|
||||||
|
|
||||||
|
/// Force our app to be either light or dark mode.
|
||||||
void set_darkmode_enabled(BOOL enabled) {
|
void set_darkmode_enabled(BOOL enabled) {
|
||||||
NSAppearance *appearance;
|
NSAppearance *appearance;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
|
@ -14,3 +15,20 @@ void set_darkmode_enabled(BOOL enabled) {
|
||||||
|
|
||||||
[NSApplication sharedApplication].appearance = appearance;
|
[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.
|
there are no outstanding ops.
|
||||||
""",
|
""",
|
||||||
),
|
),
|
||||||
|
Hook(
|
||||||
|
name="theme_did_change",
|
||||||
|
doc="Called after night mode is toggled.",
|
||||||
|
),
|
||||||
# Webview
|
# Webview
|
||||||
###################
|
###################
|
||||||
Hook(
|
Hook(
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { ChangeNotetypeState, getChangeNotetypeInfo, getNotetypeNames } from "./
|
||||||
import { setupI18n, ModuleName } from "../lib/i18n";
|
import { setupI18n, ModuleName } from "../lib/i18n";
|
||||||
import { checkNightMode } from "../lib/nightmode";
|
import { checkNightMode } from "../lib/nightmode";
|
||||||
import ChangeNotetypePage from "./ChangeNotetypePage.svelte";
|
import ChangeNotetypePage from "./ChangeNotetypePage.svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export async function changeNotetypePage(
|
export async function changeNotetypePage(
|
||||||
target: HTMLDivElement,
|
target: HTMLDivElement,
|
||||||
|
@ -28,14 +27,11 @@ export async function changeNotetypePage(
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const nightMode = checkNightMode();
|
checkNightMode();
|
||||||
const context = new Map();
|
|
||||||
context.set(nightModeKey, nightMode);
|
|
||||||
|
|
||||||
const state = new ChangeNotetypeState(names, info);
|
const state = new ChangeNotetypeState(names, info);
|
||||||
return new ChangeNotetypePage({
|
return new ChangeNotetypePage({
|
||||||
target,
|
target,
|
||||||
props: { state },
|
props: { state },
|
||||||
context,
|
|
||||||
} as any);
|
} as any);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, setContext } from "svelte";
|
import { setContext } from "svelte";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import Item from "./Item.svelte";
|
import Item from "./Item.svelte";
|
||||||
import type { Registration } from "./registration";
|
import type { Registration } from "./registration";
|
||||||
import { sectionKey, nightModeKey } from "./context-keys";
|
import { sectionKey } from "./context-keys";
|
||||||
import { insertElement, appendElement } from "./identifier";
|
import { insertElement, appendElement } from "./identifier";
|
||||||
import { makeInterface } from "./registration";
|
import { makeInterface } from "./registration";
|
||||||
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
export let id: string | undefined = undefined;
|
||||||
let className: string = "";
|
let className: string = "";
|
||||||
|
@ -89,15 +90,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
$: if (buttonToolbarRef && api) {
|
$: if (buttonToolbarRef && api) {
|
||||||
createApi();
|
createApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={buttonToolbarRef}
|
bind:this={buttonToolbarRef}
|
||||||
{id}
|
{id}
|
||||||
class="button-toolbar btn-toolbar {className}"
|
class="button-toolbar btn-toolbar {className}"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
{style}
|
{style}
|
||||||
role="toolbar"
|
role="toolbar"
|
||||||
on:focusout
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
import { onMount, createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "./context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
export let id: string | undefined = undefined;
|
||||||
let className = "";
|
let className = "";
|
||||||
|
@ -15,8 +15,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
let buttonRef: HTMLButtonElement;
|
let buttonRef: HTMLButtonElement;
|
||||||
|
|
||||||
const nightMode = getContext(nightModeKey);
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
onMount(() => dispatch("mount", { button: buttonRef }));
|
onMount(() => dispatch("mount", { button: buttonRef }));
|
||||||
</script>
|
</script>
|
||||||
|
@ -26,8 +24,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
tabindex={tabbable ? 0 : -1}
|
tabindex={tabbable ? 0 : -1}
|
||||||
bind:this={buttonRef}
|
bind:this={buttonRef}
|
||||||
class="dropdown-item btn {className}"
|
class="dropdown-item btn {className}"
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!$pageTheme.isDark}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={$pageTheme.isDark}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
on:click
|
on:click
|
||||||
on:mouseenter
|
on:mouseenter
|
||||||
|
|
|
@ -5,7 +5,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import IconConstrain from "./IconConstrain.svelte";
|
import IconConstrain from "./IconConstrain.svelte";
|
||||||
import { getContext, onMount, createEventDispatcher } from "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";
|
import type { DropdownProps } from "./dropdown";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
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;
|
let buttonRef: HTMLButtonElement;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
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="icon-button btn {className}"
|
||||||
class:active
|
class:active
|
||||||
class:dropdown-toggle={dropdownProps.dropdown}
|
class:dropdown-toggle={dropdownProps.dropdown}
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!$pageTheme.isDark}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={$pageTheme.isDark}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...dropdownProps}
|
{...dropdownProps}
|
||||||
{disabled}
|
{disabled}
|
||||||
|
|
|
@ -4,8 +4,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||||
import { nightModeKey, dropdownKey } from "./context-keys";
|
import { dropdownKey } from "./context-keys";
|
||||||
import type { DropdownProps } from "./dropdown";
|
import type { DropdownProps } from "./dropdown";
|
||||||
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
export let id: string | undefined = undefined;
|
||||||
let className: string = "";
|
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 disabled = false;
|
||||||
export let tabbable = false;
|
export let tabbable = false;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
const dropdownProps = getContext<DropdownProps>(dropdownKey) ?? { dropdown: false };
|
||||||
|
|
||||||
let buttonRef: HTMLButtonElement;
|
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="label-button {extendClassName(className, theme)}"
|
||||||
class:active
|
class:active
|
||||||
class:dropdown-toggle={dropdownProps.dropdown}
|
class:dropdown-toggle={dropdownProps.dropdown}
|
||||||
class:btn-day={theme === "anki" && !nightMode}
|
class:btn-day={theme === "anki" && !$pageTheme.isDark}
|
||||||
class:btn-night={theme === "anki" && nightMode}
|
class:btn-night={theme === "anki" && $pageTheme.isDark}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
{...dropdownProps}
|
{...dropdownProps}
|
||||||
{disabled}
|
{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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
import { onMount, createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "./context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
export let id: string | undefined = undefined;
|
||||||
let className = "";
|
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 tooltip: string | undefined = undefined;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
|
|
||||||
let buttonRef: HTMLSelectElement;
|
let buttonRef: HTMLSelectElement;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
@ -29,9 +27,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
{id}
|
{id}
|
||||||
{disabled}
|
{disabled}
|
||||||
class="{className} form-select"
|
class="{className} form-select"
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!$pageTheme.isDark}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={$pageTheme.isDark}
|
||||||
class:visible-down-arrow={nightMode}
|
class:visible-down-arrow={$pageTheme.isDark}
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
on:change
|
on:change
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// 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 touchDeviceKey = Symbol("touchDevice");
|
||||||
|
|
||||||
export const sectionKey = Symbol("section");
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export let choices: string[];
|
export let choices: string[];
|
||||||
export let value: number = 0;
|
export let value: number = 0;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
bind:value
|
bind:value
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
class:visible-down-arrow={nightMode}
|
class:visible-down-arrow={$pageTheme.isDark}
|
||||||
class="enum-selector form-select"
|
class="enum-selector form-select"
|
||||||
>
|
>
|
||||||
{#each choices as choice, idx}
|
{#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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export let value: number;
|
export let value: number;
|
||||||
export let min = 1;
|
export let min = 1;
|
||||||
export let max = 9999;
|
export let max = 9999;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
|
|
||||||
function checkMinMax() {
|
function checkMinMax() {
|
||||||
if (value > max) {
|
if (value > max) {
|
||||||
value = max;
|
value = max;
|
||||||
|
@ -29,7 +26,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
{max}
|
{max}
|
||||||
bind:value
|
bind:value
|
||||||
class="spin-box form-control"
|
class="spin-box form-control"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
on:blur={checkMinMax}
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export let value: number;
|
export let value: number;
|
||||||
export let min = 1;
|
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;
|
let stringValue: string;
|
||||||
$: stringValue = value.toFixed(2);
|
$: stringValue = value.toFixed(2);
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
|
|
||||||
function update(this: HTMLInputElement): void {
|
function update(this: HTMLInputElement): void {
|
||||||
value = Math.min(max, Math.max(min, parseFloat(this.value)));
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
{min}
|
{min}
|
||||||
{max}
|
{max}
|
||||||
step="0.01"
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
import { stepsToString, stringToSteps } from "./steps";
|
import { stepsToString, stringToSteps } from "./steps";
|
||||||
|
|
||||||
export let value: number[];
|
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;
|
let stringValue: string;
|
||||||
$: stringValue = stepsToString(value);
|
$: stringValue = stepsToString(value);
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
|
|
||||||
function update(this: HTMLInputElement): void {
|
function update(this: HTMLInputElement): void {
|
||||||
value = stringToSteps(this.value);
|
value = stringToSteps(this.value);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +20,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
type="text"
|
type="text"
|
||||||
value={stringValue}
|
value={stringValue}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
on:blur={update}
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext } from "svelte";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export let id: string | undefined;
|
export let id: string | undefined;
|
||||||
export let value: boolean;
|
export let value: boolean;
|
||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="form-check form-switch">
|
<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}
|
{id}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
bind:checked={value}
|
bind:checked={value}
|
||||||
{disabled}
|
{disabled}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,7 +4,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, onDestroy, getContext } from "svelte";
|
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";
|
import Modal from "bootstrap/js/dist/modal";
|
||||||
|
|
||||||
export let title: string;
|
export let title: string;
|
||||||
|
@ -40,8 +41,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
modalRef.removeEventListener("shown.bs.modal", onShown);
|
modalRef.removeEventListener("shown.bs.modal", onShown);
|
||||||
});
|
});
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -52,13 +51,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
>
|
>
|
||||||
<div class="modal-dialog">
|
<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">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="modalLabel">{title}</h5>
|
<h5 class="modal-title" id="modalLabel">{title}</h5>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-close"
|
class="btn-close"
|
||||||
class:invert={nightMode}
|
class:invert={$pageTheme.isDark}
|
||||||
data-bs-dismiss="modal"
|
data-bs-dismiss="modal"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
/>
|
/>
|
||||||
|
@ -73,7 +72,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
id="prompt-input"
|
id="prompt-input"
|
||||||
bind:this={inputRef}
|
bind:this={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
class="form-control"
|
class="form-control"
|
||||||
bind:value
|
bind:value
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { getDeckOptionsInfo, DeckOptionsState } from "./lib";
|
||||||
import { setupI18n, ModuleName } from "../lib/i18n";
|
import { setupI18n, ModuleName } from "../lib/i18n";
|
||||||
import { checkNightMode } from "../lib/nightmode";
|
import { checkNightMode } from "../lib/nightmode";
|
||||||
import DeckOptionsPage from "./DeckOptionsPage.svelte";
|
import DeckOptionsPage from "./DeckOptionsPage.svelte";
|
||||||
import { nightModeKey, touchDeviceKey, modalsKey } from "../components/context-keys";
|
import { touchDeviceKey, modalsKey } from "../components/context-keys";
|
||||||
|
|
||||||
export async function deckOptions(
|
export async function deckOptions(
|
||||||
target: HTMLDivElement,
|
target: HTMLDivElement,
|
||||||
|
@ -29,9 +29,9 @@ export async function deckOptions(
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
checkNightMode();
|
||||||
|
|
||||||
const context = new Map();
|
const context = new Map();
|
||||||
const nightMode = checkNightMode();
|
|
||||||
context.set(nightModeKey, nightMode);
|
|
||||||
|
|
||||||
const modals = new Map();
|
const modals = new Map();
|
||||||
context.set(modalsKey, modals);
|
context.set(modalsKey, modals);
|
||||||
|
|
|
@ -18,8 +18,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onDestroy, getContext } from "svelte";
|
import { onDestroy } from "svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
import { convertMathjax } from "./mathjax";
|
import { convertMathjax } from "./mathjax";
|
||||||
import { randomUUID } from "../lib/uuid";
|
import { randomUUID } from "../lib/uuid";
|
||||||
import { writable } from "svelte/store";
|
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 autofocus = false;
|
||||||
export let fontSize = 20;
|
export let fontSize = 20;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
$: [converted, title] = convertMathjax(mathjax, $pageTheme.isDark, fontSize);
|
||||||
$: [converted, title] = convertMathjax(mathjax, nightMode, fontSize);
|
|
||||||
$: empty = title === "MathJax";
|
$: empty = title === "MathJax";
|
||||||
$: encoded = encodeURIComponent(converted);
|
$: encoded = encodeURIComponent(converted);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import type { DecoratedElement, DecoratedElementConstructor } from "./decorated"
|
||||||
import { nodeIsElement } from "../lib/dom";
|
import { nodeIsElement } from "../lib/dom";
|
||||||
import { noop } from "../lib/functional";
|
import { noop } from "../lib/functional";
|
||||||
import { placeCaretAfter } from "../domlib/place-caret";
|
import { placeCaretAfter } from "../domlib/place-caret";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
import Mathjax_svelte from "./Mathjax.svelte";
|
import Mathjax_svelte from "./Mathjax.svelte";
|
||||||
|
|
||||||
|
@ -148,12 +147,6 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
|
||||||
this.innerHTML = "";
|
this.innerHTML = "";
|
||||||
this.style.whiteSpace = "normal";
|
this.style.whiteSpace = "normal";
|
||||||
|
|
||||||
const context = new Map();
|
|
||||||
context.set(
|
|
||||||
nightModeKey,
|
|
||||||
document.documentElement.classList.contains("night-mode"),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.component = new Mathjax_svelte({
|
this.component = new Mathjax_svelte({
|
||||||
target: this,
|
target: this,
|
||||||
props: {
|
props: {
|
||||||
|
@ -161,7 +154,6 @@ export const Mathjax: DecoratedElementConstructor = class Mathjax
|
||||||
block: this.block,
|
block: this.block,
|
||||||
autofocus: this.hasAttribute("focusonmount"),
|
autofocus: this.hasAttribute("focusonmount"),
|
||||||
},
|
},
|
||||||
context,
|
|
||||||
} as any);
|
} 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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, createEventDispatcher, getContext } from "svelte";
|
import { onMount, createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let id: string | undefined = undefined;
|
export let id: string | undefined = undefined;
|
||||||
let className = "";
|
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 selected = false;
|
||||||
export let active = false;
|
export let active = false;
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
let buttonRef: HTMLButtonElement;
|
let buttonRef: HTMLButtonElement;
|
||||||
|
@ -33,8 +31,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
{id}
|
{id}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
class="autocomplete-item btn {className}"
|
class="autocomplete-item btn {className}"
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!$pageTheme.isDark}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={$pageTheme.isDark}
|
||||||
class:selected
|
class:selected
|
||||||
class:active
|
class:active
|
||||||
on:mousedown|preventDefault
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, getContext } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let offsetX = 0;
|
export let offsetX = 0;
|
||||||
export let offsetY = 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;
|
export let activeSize = 5;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
const nightMode = getContext(nightModeKey);
|
|
||||||
|
|
||||||
const onPointerdown =
|
const onPointerdown =
|
||||||
(north: boolean, west: boolean) =>
|
(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"
|
class="d-contents"
|
||||||
style="--offsetX: {offsetX}px; --offsetY: {offsetY}px; --activeSize: {activeSize}px;"
|
style="--offsetX: {offsetX}px; --offsetY: {offsetY}px; --activeSize: {activeSize}px;"
|
||||||
>
|
>
|
||||||
<div class:nightMode class="bordered" on:mousedown|preventDefault />
|
|
||||||
<div
|
<div
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
|
class="bordered"
|
||||||
|
on:mousedown|preventDefault
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class:nightMode={$pageTheme.isDark}
|
||||||
class:active
|
class:active
|
||||||
class="control nw"
|
class="control nw"
|
||||||
on:mousedown|preventDefault
|
on:mousedown|preventDefault
|
||||||
|
@ -36,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
on:pointermove
|
on:pointermove
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
class:active
|
class:active
|
||||||
class="control ne"
|
class="control ne"
|
||||||
on:mousedown|preventDefault
|
on:mousedown|preventDefault
|
||||||
|
@ -44,7 +47,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
on:pointermove
|
on:pointermove
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
class:active
|
class:active
|
||||||
class="control sw"
|
class="control sw"
|
||||||
on:mousedown|preventDefault
|
on:mousedown|preventDefault
|
||||||
|
@ -52,7 +55,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
on:pointermove
|
on:pointermove
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class:nightMode
|
class:nightMode={$pageTheme.isDark}
|
||||||
class:active
|
class:active
|
||||||
class="control se"
|
class="control se"
|
||||||
on:mousedown|preventDefault
|
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 type { EditingInputAPI } from "./EditingArea.svelte";
|
||||||
import contextProperty from "../sveltelib/context-property";
|
import contextProperty from "../sveltelib/context-property";
|
||||||
import type { OnNextInsertTrigger } from "../sveltelib/input-manager";
|
import type { OnNextInsertTrigger } from "../sveltelib/input-manager";
|
||||||
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export interface RichTextInputAPI extends EditingInputAPI {
|
export interface RichTextInputAPI extends EditingInputAPI {
|
||||||
name: "rich-text";
|
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 SetContext from "./SetContext.svelte";
|
||||||
import ContentEditable from "../editable/ContentEditable.svelte";
|
import ContentEditable from "../editable/ContentEditable.svelte";
|
||||||
|
|
||||||
import { onMount, getContext, getAllContexts } from "svelte";
|
import { onMount, getAllContexts } from "svelte";
|
||||||
import {
|
import {
|
||||||
nodeIsElement,
|
nodeIsElement,
|
||||||
nodeContainsInlineContent,
|
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 { on } from "../lib/events";
|
||||||
import { nodeStore } from "../sveltelib/node-store";
|
import { nodeStore } from "../sveltelib/node-store";
|
||||||
import type { DecoratedElement } from "../editable/decorated";
|
import type { DecoratedElement } from "../editable/decorated";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
export let hidden: boolean;
|
export let hidden: boolean;
|
||||||
|
|
||||||
|
@ -235,12 +235,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
unsubscribeToEditingArea();
|
unsubscribeToEditingArea();
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<RichTextStyles
|
<RichTextStyles
|
||||||
color={nightMode ? "white" : "black"}
|
color={$pageTheme.isDark ? "white" : "black"}
|
||||||
let:attachToShadow={attachStyles}
|
let:attachToShadow={attachStyles}
|
||||||
let:promise={stylesPromise}
|
let:promise={stylesPromise}
|
||||||
let:stylesDidLoad
|
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
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, getContext, createEventDispatcher } from "svelte";
|
import { onMount, createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
let className: string = "";
|
let className: string = "";
|
||||||
export { className as class };
|
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);
|
setTimeout(() => (flashing = false), 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
|
|
||||||
let button: HTMLButtonElement;
|
let button: HTMLButtonElement;
|
||||||
|
|
||||||
onMount(() => dispatch("mount", { button }));
|
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="tag btn d-inline-flex align-items-center text-nowrap ps-2 pe-1 {className}"
|
||||||
class:selected
|
class:selected
|
||||||
class:flashing
|
class:flashing
|
||||||
class:btn-day={!nightMode}
|
class:btn-day={!$pageTheme.isDark}
|
||||||
class:btn-night={nightMode}
|
class:btn-night={$pageTheme.isDark}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
title={tooltip}
|
title={tooltip}
|
||||||
on:mousemove
|
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 Tag from "./Tag.svelte";
|
||||||
import WithTooltip from "../components/WithTooltip.svelte";
|
import WithTooltip from "../components/WithTooltip.svelte";
|
||||||
|
|
||||||
import { createEventDispatcher, getContext } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
import { controlPressed, shiftPressed } from "../lib/keys";
|
import { controlPressed, shiftPressed } from "../lib/keys";
|
||||||
import { delimChar } from "./tags";
|
import { delimChar } from "./tags";
|
||||||
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
export let name: string;
|
export let name: string;
|
||||||
let className: 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 {
|
function hasMultipleParts(name: string): boolean {
|
||||||
return name.split(delimChar).length > 1;
|
return name.split(delimChar).length > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nightMode = getContext<boolean>(nightModeKey);
|
|
||||||
const hoverClass = "tag-icon-hover";
|
const hoverClass = "tag-icon-hover";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:body on:keydown={setControlShift} on:keyup={setControlShift} />
|
<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}
|
{#if active}
|
||||||
<Tag class={className} on:mousemove={setControlShift} on:click={onClick}>
|
<Tag class={className} on:mousemove={setControlShift} on:click={onClick}>
|
||||||
{name}
|
{name}
|
||||||
|
|
|
@ -62,16 +62,8 @@ export const i18n = setupI18n({
|
||||||
|
|
||||||
import OldEditorAdapter from "./OldEditorAdapter.svelte";
|
import OldEditorAdapter from "./OldEditorAdapter.svelte";
|
||||||
import type { NoteEditorAPI } from "./OldEditorAdapter.svelte";
|
import type { NoteEditorAPI } from "./OldEditorAdapter.svelte";
|
||||||
import { nightModeKey } from "../components/context-keys";
|
|
||||||
|
|
||||||
async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
||||||
const context = new Map<symbol, unknown>();
|
|
||||||
|
|
||||||
context.set(
|
|
||||||
nightModeKey,
|
|
||||||
document.documentElement.classList.contains("night-mode"),
|
|
||||||
);
|
|
||||||
|
|
||||||
await i18n;
|
await i18n;
|
||||||
|
|
||||||
const api: Partial<NoteEditorAPI> = {};
|
const api: Partial<NoteEditorAPI> = {};
|
||||||
|
@ -79,7 +71,6 @@ async function setupNoteEditor(): Promise<NoteEditorAPI> {
|
||||||
const noteEditor = new OldEditorAdapter({
|
const noteEditor = new OldEditorAdapter({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
props: { api: api as NoteEditorAPI },
|
props: { api: api as NoteEditorAPI },
|
||||||
context,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Object.assign(globalThis, {
|
Object.assign(globalThis, {
|
||||||
|
|
|
@ -5,12 +5,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { SvelteComponent } from "svelte/internal";
|
import type { SvelteComponent } from "svelte/internal";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
|
import { pageTheme } from "../sveltelib/theme";
|
||||||
|
|
||||||
import { bridgeCommand } from "../lib/bridgecommand";
|
import { bridgeCommand } from "../lib/bridgecommand";
|
||||||
|
|
||||||
import WithGraphData from "./WithGraphData.svelte";
|
import WithGraphData from "./WithGraphData.svelte";
|
||||||
|
|
||||||
export let nightMode: boolean;
|
|
||||||
export let graphs: SvelteComponent[];
|
export let graphs: SvelteComponent[];
|
||||||
|
|
||||||
export let initialSearch: string;
|
export let initialSearch: string;
|
||||||
|
@ -45,7 +45,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
{sourceData}
|
{sourceData}
|
||||||
{preferences}
|
{preferences}
|
||||||
{revlogRange}
|
{revlogRange}
|
||||||
{nightMode}
|
nightMode={$pageTheme.isDark}
|
||||||
on:search={browserSearch}
|
on:search={browserSearch}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -31,14 +31,12 @@ export function graphs(
|
||||||
controller = null as SvelteComponent | null,
|
controller = null as SvelteComponent | null,
|
||||||
} = {},
|
} = {},
|
||||||
): void {
|
): void {
|
||||||
const nightMode = checkNightMode();
|
checkNightMode();
|
||||||
|
|
||||||
setupI18n({ modules: [ModuleName.STATISTICS, ModuleName.SCHEDULING] }).then(() => {
|
setupI18n({ modules: [ModuleName.STATISTICS, ModuleName.SCHEDULING] }).then(() => {
|
||||||
new GraphsPage({
|
new GraphsPage({
|
||||||
target,
|
target,
|
||||||
props: {
|
props: {
|
||||||
graphs,
|
graphs,
|
||||||
nightMode,
|
|
||||||
initialSearch: search,
|
initialSearch: search,
|
||||||
initialDays: days,
|
initialDays: days,
|
||||||
controller,
|
controller,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// 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
|
/// Add night-mode class to documentElement if hash location is #night, and
|
||||||
/// true if added.
|
/// return true if added.
|
||||||
export function checkNightMode(): boolean {
|
export function checkNightMode(): boolean {
|
||||||
const nightMode = window.location.hash == "#night";
|
const nightMode = window.location.hash == "#night";
|
||||||
if (nightMode) {
|
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 a new issue