Propagate editor UI state transitions to add-ons (#2711)

* Propagate editor UI state transitions to add-ons

* Also set initial Python state to EditorState.INITIAL

---------

Co-authored-by: Glutanimate <glutanimate@users.noreply.github.com>
This commit is contained in:
Aristotelis 2023-10-13 00:47:50 +02:00 committed by GitHub
parent 9e147c6335
commit 7ce1c4439a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 0 deletions

View file

@ -90,6 +90,18 @@ class EditorMode(Enum):
BROWSER = 2 BROWSER = 2
class EditorState(Enum):
"""
Current input state of the editing UI.
"""
INITIAL = -1
FIELDS = 0
IO_PICKER = 1
IO_MASKS = 2
IO_FIELDS = 3
class Editor: class Editor:
"""The screen that embeds an editing widget should listen for changes via """The screen that embeds an editing widget should listen for changes via
the `operation_did_execute` hook, and call set_note() when the editor needs the `operation_did_execute` hook, and call set_note() when the editor needs
@ -124,6 +136,7 @@ class Editor:
self.last_field_index: int | None = None self.last_field_index: int | None = None
# current card, for card layout # current card, for card layout
self.card: Card | None = None self.card: Card | None = None
self.state: EditorState = EditorState.INITIAL
self._init_links() self._init_links()
self.setupOuter() self.setupOuter()
self.add_webview() self.add_webview()
@ -474,6 +487,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
collapsed = collapsed_string == "true" collapsed = collapsed_string == "true"
self.setTagsCollapsed(collapsed) self.setTagsCollapsed(collapsed)
elif cmd.startswith("editorState"):
(_, new_state_id, old_state_id) = cmd.split(":", 2)
self.signal_state_change(
EditorState(int(new_state_id)), EditorState(int(old_state_id))
)
elif cmd in self._links: elif cmd in self._links:
return self._links[cmd](self) return self._links[cmd](self)
@ -483,6 +502,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
def mungeHTML(self, txt: str) -> str: def mungeHTML(self, txt: str) -> str:
return gui_hooks.editor_will_munge_html(txt, self) return gui_hooks.editor_will_munge_html(txt, self)
def signal_state_change(
self, new_state: EditorState, old_state: EditorState
) -> None:
self.state = new_state
gui_hooks.editor_state_did_change(self, new_state, old_state)
# Setting/unsetting the current note # Setting/unsetting the current note
###################################################################### ######################################################################

View file

@ -1152,6 +1152,16 @@ gui_hooks.webview_did_inject_style_into_page.append(mytest)
or by a right-click paste. or by a right-click paste.
""", """,
), ),
Hook(
name="editor_state_did_change",
args=[
"editor: aqt.editor.Editor",
"new_state: aqt.editor.EditorState",
"old_state: aqt.editor.EditorState",
],
doc="""Called when the input state of the editor changes, e.g. when
switching to an image occlusion note type.""",
),
# Tag # Tag
################### ###################
Hook(name="tag_editor_did_process_key", args=["tag_edit: TagEdit", "evt: QEvent"]), Hook(name="tag_editor_did_process_key", args=["tag_edit: TagEdit", "evt: QEvent"]),

View file

@ -69,6 +69,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import RichTextInput, { editingInputIsRichText } from "./rich-text-input"; import RichTextInput, { editingInputIsRichText } from "./rich-text-input";
import RichTextBadge from "./RichTextBadge.svelte"; import RichTextBadge from "./RichTextBadge.svelte";
import type { NotetypeIdAndModTime, SessionOptions } from "./types"; import type { NotetypeIdAndModTime, SessionOptions } from "./types";
import { EditorState } from "./types";
function quoteFontFamily(fontFamily: string): string { function quoteFontFamily(fontFamily: string): string {
// generic families (e.g. sans-serif) must not be quoted // generic families (e.g. sans-serif) must not be quoted
@ -496,6 +497,44 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return false; return false;
} }
// Signal editor UI state changes to add-ons
let editorState: EditorState = EditorState.Initial;
let lastEditorState: EditorState = editorState;
function getEditorState(
ioMaskEditorVisible: boolean,
isImageOcclusion: boolean,
isIOImageLoaded: boolean,
imageOcclusionMode: IOMode | undefined,
): EditorState {
if (isImageOcclusion && ioMaskEditorVisible && !isIOImageLoaded) {
return EditorState.ImageOcclusionPicker;
} else if (imageOcclusionMode && ioMaskEditorVisible) {
return EditorState.ImageOcclusionMasks;
} else if (!ioMaskEditorVisible && isImageOcclusion) {
return EditorState.ImageOcclusionFields;
}
return EditorState.Fields;
}
function signalEditorState(newState: EditorState) {
tick().then(() => {
globalThis.editorState = newState;
bridgeCommand(`editorState:${newState}:${lastEditorState}`);
lastEditorState = newState;
});
}
$: signalEditorState(editorState);
$: editorState = getEditorState(
$ioMaskEditorVisible,
isImageOcclusion,
isIOImageLoaded,
imageOcclusionMode,
);
onMount(() => { onMount(() => {
function wrap(before: string, after: string): void { function wrap(before: string, after: string): void {
if (!$focusedInput || !editingInputIsRichText($focusedInput)) { if (!$focusedInput || !editingInputIsRichText($focusedInput)) {
@ -537,6 +576,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
...oldEditorAdapter, ...oldEditorAdapter,
}); });
editorState = getEditorState(
$ioMaskEditorVisible,
isImageOcclusion,
isIOImageLoaded,
imageOcclusionMode,
);
document.addEventListener("visibilitychange", saveOnPageHide); document.addEventListener("visibilitychange", saveOnPageHide);
return () => document.removeEventListener("visibilitychange", saveOnPageHide); return () => document.removeEventListener("visibilitychange", saveOnPageHide);
}); });

View file

@ -19,3 +19,11 @@ export type NotetypeIdAndModTime = {
id: number; id: number;
modTime: number; modTime: number;
}; };
export enum EditorState {
Initial = -1,
Fields = 0,
ImageOcclusionPicker = 1,
ImageOcclusionMasks = 2,
ImageOcclusionFields = 3,
}