Skip to content
This repository was archived by the owner on Nov 30, 2023. It is now read-only.

Commit 34a20cd

Browse files
committed
fix: display tones in newer anki
fixes #201
1 parent c45c286 commit 34a20cd

File tree

1 file changed

+94
-5
lines changed

1 file changed

+94
-5
lines changed

chinese/edit.py

+94-5
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@
1717
# You should have received a copy of the GNU General Public License along with
1818
# Chinese Support Redux. If not, see <https://www.gnu.org/licenses/>.
1919

20+
import re
21+
import json
22+
23+
import anki.buildinfo
2024
from anki.hooks import addHook
25+
from aqt.utils import showWarning
2126
from aqt import mw
27+
from aqt.editor import Editor
2228

2329
from .behavior import update_fields
2430
from .main import config
@@ -80,12 +86,95 @@ def onFocusLost(self, _, note, index):
8086
return False
8187

8288

83-
def append_tone_styling(editor):
84-
js = 'var css = document.styleSheets[0];'
89+
TONE_CSS_RULE = re.compile("(\\.tone\\d) *\\{([^}]*)\\}")
90+
91+
# append_tone_styling(editor: Editor)
92+
#
93+
# Extracts the CSS rules for tones (i.e. matching TONE_CSS_RULE) from the
94+
# user defined CSS style sheet. For the sake of simplicity, a tone CSS rule
95+
# must be a one liner.
96+
#
97+
# IMPLEMENTATION NOTES:
98+
#
99+
# The code makes heavily use of internal APIs in Anki that may change in
100+
# future releases. Hopefully, these notes are useful to adapt the code to
101+
# new releases in case of breaking.
102+
#
103+
# The solution is based on Anki 2.1.54.
104+
#
105+
# The Javascript code being evaluated in the QWebView executes the following steps:
106+
# 1. Wait until the UI has been loaded. The code for that is based on [1].
107+
# 2. Loop through all RichTextInput Svelte component instances. They are
108+
# reachable via "require" because they have been registered before here [2].
109+
# Unfortunately, this method is only available since 2.1.54.
110+
# 3. Using the RichTextInputAPI [3], we can query the associated CustomStyles
111+
# instance. A CustomStyles instance has a `styleMap` [4] that contains an
112+
# "userBase" entry, which wraps a <style> HTML element. This style element's
113+
# intended function is to apply color, font family, font size, etc. [5,6].
114+
# It is the perfect place to add our own CSS tone rules.
115+
#
116+
# [1] https://github.com/ankitects/anki/blob/2.1.54/qt/aqt/editor.py#L184
117+
# [2] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextInput.svelte#L40
118+
# [3] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextInput.svelte#L21
119+
# [4] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/CustomStyles.svelte#L37
120+
# [5] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextStyles.svelte#L17
121+
# [6] https://github.com/ankitects/anki/blob/2.1.54/ts/editor/rich-text-input/RichTextStyles.svelte#L33
122+
123+
def append_tone_styling_anki2_1_54(editor: Editor):
124+
rules = []
125+
for line in editor.note.note_type()['css'].split('\n'):
126+
if '.tone' in line:
127+
m = TONE_CSS_RULE.search(line)
128+
if m:
129+
rules.append(line)
130+
else:
131+
showWarning("WARN: could not parse CSS tone rule. "
132+
"Currently, tone CSS rules need to be one liners.")
133+
134+
js = f"var CSSRULES = {json.dumps(rules)};"
135+
js += """
136+
require("anki/ui").loaded.then(() =>
137+
require("anki/RichTextInput").instances.forEach(inst =>
138+
inst.customStyles.then(styles => {
139+
var sheet = styles.styleMap.get("userBase").element.sheet;
140+
CSSRULES.forEach(rule =>
141+
sheet.insertRule(rule)
142+
);
143+
})
144+
)
145+
);
146+
"""
147+
editor.web.eval(js)
85148

86-
for line in editor.note.model()['css'].split('\n'):
149+
def append_tone_styling_anki2_1_49(editor):
150+
rules = []
151+
for line in editor.note.note_type()['css'].split('\n'):
87152
if line.startswith('.tone'):
88-
js += 'css.insertRule("{}", css.cssRules.length);'.format(
89-
line.rstrip())
153+
m = TONE_CSS_RULE.search(line)
154+
if m:
155+
rules.append((m.group(1), m.group(2)))
156+
else:
157+
showWarning("WARN: could not parse CSS tone rule. "
158+
"Currently, tone CSS rules need to be one liners.")
159+
160+
inner_js = ""
161+
for rulename, ruledef in rules:
162+
for part in ruledef.split(';'):
163+
if ':' in part:
164+
[property, value] = part.split(':', 1)
165+
inner_js += f"jQuery('{rulename.strip()}', this.shadowRoot).css('{property.strip()}', '{value.strip()}');\n"
166+
js = "jQuery('div.field').each(function () {\n%s})" % inner_js
90167

91168
editor.web.eval(js)
169+
170+
171+
__version = [int(x) for x in anki.buildinfo.version.split('.')]
172+
if __version < [2,1,50]:
173+
append_tone_styling = append_tone_styling_anki2_1_49
174+
elif __version >= [2,1,54]:
175+
append_tone_styling = append_tone_styling_anki2_1_54
176+
else:
177+
showWarning("Chinese tone styling has not been implemented for your current Anki version. "
178+
"Supported versions are Anki 2.1.49 as well as 2.1.54 and later.")
179+
def append_tone_styling(editor):
180+
pass

0 commit comments

Comments
 (0)