Insert Word equation objects for math output
This commit is contained in:
@@ -2,50 +2,63 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from threading import Lock
|
||||
from typing import Optional
|
||||
from typing import Optional, Sequence
|
||||
|
||||
import keyboard
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TypeAction:
|
||||
kind: str
|
||||
value: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class KeyStepper:
|
||||
text: str
|
||||
text: str | Sequence[TypeAction]
|
||||
delay: float = 0.0
|
||||
_index: int = 0
|
||||
_hook: Optional[object] = None
|
||||
_lock: Lock = field(default_factory=Lock)
|
||||
_injecting: bool = False
|
||||
_actions: list[TypeAction] = field(init=False)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if isinstance(self.text, str):
|
||||
self._actions = [TypeAction("text", char) for char in self.text]
|
||||
else:
|
||||
self._actions = list(self.text)
|
||||
|
||||
def start(self) -> None:
|
||||
if not self.text:
|
||||
if not self._actions:
|
||||
return
|
||||
if self._hook is not None:
|
||||
return
|
||||
self.type_next_character()
|
||||
if self._index >= len(self.text):
|
||||
if self._index >= len(self._actions):
|
||||
return
|
||||
# suppress=True blocks the physical key while the callback types the next AI character.
|
||||
# suppress=True blocks the physical key while the callback types the next AI character/action.
|
||||
self._hook = keyboard.hook(self._on_event, suppress=True)
|
||||
|
||||
@property
|
||||
def remaining_characters(self) -> int:
|
||||
return max(len(self.text) - self._index, 0)
|
||||
return max(len(self._actions) - self._index, 0)
|
||||
|
||||
def type_next_character(self) -> None:
|
||||
with self._lock:
|
||||
if self._index >= len(self.text):
|
||||
if self._index >= len(self._actions):
|
||||
self.stop()
|
||||
return
|
||||
char = self.text[self._index]
|
||||
action = self._actions[self._index]
|
||||
self._index += 1
|
||||
|
||||
self._injecting = True
|
||||
try:
|
||||
self._type_char(char)
|
||||
self._type_action(action)
|
||||
finally:
|
||||
self._injecting = False
|
||||
|
||||
if self._index >= len(self.text):
|
||||
if self._index >= len(self._actions):
|
||||
self.stop()
|
||||
|
||||
def stop(self) -> None:
|
||||
@@ -59,6 +72,20 @@ class KeyStepper:
|
||||
return
|
||||
self.type_next_character()
|
||||
|
||||
def _type_action(self, action: TypeAction) -> None:
|
||||
if action.kind == "equation":
|
||||
self._type_word_equation(action.value)
|
||||
return
|
||||
for char in action.value:
|
||||
self._type_char(char)
|
||||
|
||||
def _type_word_equation(self, expression: str) -> None:
|
||||
keyboard.send("alt+=")
|
||||
keyboard.write(expression, delay=self.delay, exact=True)
|
||||
# Word converts much of UnicodeMath after Space; Right exits the equation object.
|
||||
keyboard.send("space")
|
||||
keyboard.send("right")
|
||||
|
||||
def _type_char(self, char: str) -> None:
|
||||
if char == "\n":
|
||||
keyboard.send("enter")
|
||||
|
||||
Reference in New Issue
Block a user