chore(react/type_widgets): port text touchbar (untested)

This commit is contained in:
Elian Doran 2025-09-25 17:53:48 +03:00
parent 37d33fb975
commit 3ed399a888
No known key found for this signature in database
3 changed files with 109 additions and 79 deletions

View File

@ -25,6 +25,7 @@ interface ButtonProps {
icon?: string;
click: () => void;
enabled?: boolean;
backgroundColor?: string;
}
interface SpacerProps {
@ -129,13 +130,14 @@ export function TouchBarSlider({ label, value, minValue, maxValue, onChange }: S
return <></>;
}
export function TouchBarButton({ label, icon, click, enabled }: ButtonProps) {
export function TouchBarButton({ label, icon, click, enabled, backgroundColor }: ButtonProps) {
const api = useContext(TouchBarContext);
const item = useMemo(() => {
if (!api) return null;
return new api.TouchBar.TouchBarButton({
label, click, enabled,
icon: icon ? buildIcon(api.nativeImage, icon) : undefined
icon: icon ? buildIcon(api.nativeImage, icon) : undefined,
backgroundColor
});
}, [ label, icon ]);
@ -171,6 +173,32 @@ export function TouchBarSegmentedControl({ mode, segments, selectedIndex, onChan
return <></>;
}
export function TouchBarGroup({ children }: { children: ComponentChildren }) {
const remote = dynamicRequire("@electron/remote") as typeof import("@electron/remote");
const items: TouchBarItem[] = [];
const api: TouchBarContextApi = {
TouchBar: remote.TouchBar,
nativeImage: remote.nativeImage,
addItem: (item) => {
items.push(item);
}
};
if (api) {
const item = new api.TouchBar.TouchBarGroup({
items: new api.TouchBar({ items })
});
api.addItem(item);
}
return <>
<TouchBarContext.Provider value={api}>
{children}
</TouchBarContext.Provider>
</>;
}
export function TouchBarSpacer({ size }: SpacerProps) {
const api = useContext(TouchBarContext);

View File

@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "preact/hooks";
import dialog from "../../../services/dialog";
import toast from "../../../services/toast";
import utils, { deferred, isMobile } from "../../../services/utils";
import utils, { deferred, hasTouchBar, isMobile } from "../../../services/utils";
import { useEditorSpacedUpdate, useKeyboardShortcuts, useLegacyImperativeHandlers, useNoteLabel, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "../../react/hooks";
import { TypeWidgetProps } from "../type_widget";
import CKEditorWithWatchdog, { CKEditorApi } from "./CKEditorWithWatchdog";
@ -14,6 +14,9 @@ import getTemplates, { updateTemplateCache } from "./snippets.js";
import appContext from "../../../components/app_context";
import link, { parseNavigationStateFromUrl } from "../../../services/link";
import note_create from "../../../services/note_create";
import TouchBar, { TouchBarButton, TouchBarGroup, TouchBarSegmentedControl } from "../../react/TouchBar";
import { RefObject } from "preact";
import { buildSelectedBackgroundColor } from "../../../components/touch_bar";
/**
* The editor can operate into two distinct modes:
@ -26,6 +29,7 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
const [ content, setContent ] = useState<string>();
const watchdogRef = useRef<EditorWatchdog>(null);
const editorApiRef = useRef<CKEditorApi>(null);
const refreshTouchBarRef = useRef<() => void>(null);
const [ language ] = useNoteLabel(note, "language");
const [ textNoteEditorType ] = useTriliumOption("textNoteEditorType");
const [ codeBlockWordWrap ] = useTriliumOptionBool("codeBlockWordWrap");
@ -201,9 +205,18 @@ export default function EditableText({ note, parentComponent, ntxId, noteContext
setupClassicEditor(editor, parentComponent);
}
if (hasTouchBar) {
const handler = () => refreshTouchBarRef.current?.();
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
editor.commands.get(event)?.on("change", handler);
}
}
initialized.current.resolve();
}}
/>}
<EditableTextTouchBar watchdogRef={watchdogRef} refreshTouchBarRef={refreshTouchBarRef} />
</div>
)
}
@ -304,3 +317,68 @@ function findClassicToolbar(parentComponent: Component): JQuery<HTMLElement> {
return $("body").find(".classic-toolbar-widget");
}
}
function EditableTextTouchBar({ watchdogRef, refreshTouchBarRef }: { watchdogRef: RefObject<EditorWatchdog | null>, refreshTouchBarRef: RefObject<() => void> }) {
const [ headingSelectedIndex, setHeadingSelectedIndex ] = useState<number>();
function refresh() {
let headingSelectedIndex: number | undefined = undefined;
const editor = watchdogRef.current?.editor;
const headingCommand = editor?.commands.get("heading");
const paragraphCommand = editor?.commands.get("paragraph");
if (paragraphCommand?.value) {
headingSelectedIndex = 0;
} else if (headingCommand?.value === "heading2") {
headingSelectedIndex = 1;
} else if (headingCommand?.value === "heading3") {
headingSelectedIndex = 2;
}
setHeadingSelectedIndex(headingSelectedIndex);
}
useEffect(refresh, []);
refreshTouchBarRef.current = refresh;
return (
<TouchBar>
<TouchBarSegmentedControl
segments={[
{ label: "P" },
{ label: "H2" },
{ label: "H3" }
]}
onChange={(selectedIndex) => {
const editor = watchdogRef.current?.editor;
switch (selectedIndex) {
case 0:
editor?.execute("paragraph")
break;
case 1:
editor?.execute("heading", { value: "heading2" });
break;
case 2:
editor?.execute("heading", { value: "heading3" });
break;
}
}}
selectedIndex={headingSelectedIndex}
mode="buttons"
/>
<TouchBarGroup>
<TouchBarCommandButton watchdogRef={watchdogRef} command="bold" icon="NSTouchBarTextBoldTemplate" />
<TouchBarCommandButton watchdogRef={watchdogRef} command="italic" icon="NSTouchBarTextItalicTemplate" />
<TouchBarCommandButton watchdogRef={watchdogRef} command="underline" icon="NSTouchBarTextUnderlineTemplate" />
</TouchBarGroup>
</TouchBar>
)
}
function TouchBarCommandButton({ watchdogRef, icon, command }: { watchdogRef: RefObject<EditorWatchdog | null>, icon: string, command: string }) {
const editor = watchdogRef.current?.editor;
return (<TouchBarButton
icon={icon}
click={() => editor?.execute(command)}
backgroundColor={buildSelectedBackgroundColor(editor?.commands.get(command)?.value as boolean)}
/>);
}

View File

@ -32,23 +32,6 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
super.doRender();
}
async initEditor() {
this.watchdog.setCreator(async (_, editorConfig) => {
// Touch bar integration
if (hasTouchBar) {
for (const event of [ "bold", "italic", "underline", "paragraph", "heading" ]) {
editor.commands.get(event)?.on("change", () => this.triggerCommand("refreshTouchBar"));
}
}
return editor;
});
await this.createEditor();
}
show() { }
getEditor() {
return this.watchdog?.editor;
}
@ -80,64 +63,5 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
await this.reinitialize();
}
buildTouchBarCommand(data: CommandListenerData<"buildTouchBar">) {
const { TouchBar, buildIcon } = data;
const { TouchBarSegmentedControl, TouchBarGroup, TouchBarButton } = TouchBar;
const { editor } = this.watchdog;
if (!editor) {
return;
}
const commandButton = (icon: string, command: string) => new TouchBarButton({
icon: buildIcon(icon),
click: () => editor.execute(command),
backgroundColor: buildSelectedBackgroundColor(editor.commands.get(command)?.value as boolean)
});
let headingSelectedIndex: number | undefined = undefined;
const headingCommand = editor.commands.get("heading");
const paragraphCommand = editor.commands.get("paragraph");
if (paragraphCommand?.value) {
headingSelectedIndex = 0;
} else if (headingCommand?.value === "heading2") {
headingSelectedIndex = 1;
} else if (headingCommand?.value === "heading3") {
headingSelectedIndex = 2;
}
return [
new TouchBarSegmentedControl({
segments: [
{ label: "P" },
{ label: "H2" },
{ label: "H3" }
],
change(selectedIndex: number, isSelected: boolean) {
switch (selectedIndex) {
case 0:
editor.execute("paragraph")
break;
case 1:
editor.execute("heading", { value: "heading2" });
break;
case 2:
editor.execute("heading", { value: "heading3" });
break;
}
},
selectedIndex: headingSelectedIndex
}),
new TouchBarGroup({
items: new TouchBar({
items: [
commandButton("NSTouchBarTextBoldTemplate", "bold"),
commandButton("NSTouchBarTextItalicTemplate", "italic"),
commandButton("NSTouchBarTextUnderlineTemplate", "underline")
]
})
})
];
}
}