mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 07:08:55 +02:00
205 lines
5.3 KiB
TypeScript
205 lines
5.3 KiB
TypeScript
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
|
import { ParentComponent } from "./react_utils";
|
|
import { ComponentChildren, createContext } from "preact";
|
|
import { TouchBarItem } from "../../components/touch_bar";
|
|
import { dynamicRequire, isElectron, isMac } from "../../services/utils";
|
|
|
|
interface TouchBarProps {
|
|
children: ComponentChildren;
|
|
}
|
|
|
|
interface LabelProps {
|
|
label: string;
|
|
}
|
|
|
|
interface SliderProps {
|
|
label: string;
|
|
value: number;
|
|
minValue: number;
|
|
maxValue: number;
|
|
onChange: (newValue: number) => void;
|
|
}
|
|
|
|
interface ButtonProps {
|
|
label?: string;
|
|
icon?: string;
|
|
click: () => void;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
interface SpacerProps {
|
|
size: "flexible" | "large" | "small";
|
|
}
|
|
|
|
interface SegmentedControlProps {
|
|
mode: "single" | "buttons";
|
|
segments: {
|
|
label?: string;
|
|
icon?: string;
|
|
onClick?: () => void;
|
|
}[];
|
|
selectedIndex?: number;
|
|
onChange?: (selectedIndex: number, isSelected: boolean) => void;
|
|
}
|
|
|
|
interface TouchBarContextApi {
|
|
addItem(item: TouchBarItem): void;
|
|
TouchBar: typeof Electron.TouchBar;
|
|
nativeImage: typeof Electron.nativeImage;
|
|
}
|
|
|
|
const TouchBarContext = createContext<TouchBarContextApi | null>(null);
|
|
|
|
export default function TouchBar({ children }: TouchBarProps) {
|
|
if (!isElectron() || !isMac()) {
|
|
return;
|
|
}
|
|
|
|
const [ isFocused, setIsFocused ] = useState(false);
|
|
const parentComponent = useContext(ParentComponent);
|
|
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);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
const el = parentComponent?.$widget[0];
|
|
if (!el) return;
|
|
|
|
function onFocusGained() {
|
|
setIsFocused(true);
|
|
}
|
|
|
|
function onFocusLost() {
|
|
setIsFocused(false);
|
|
}
|
|
|
|
el.addEventListener("focusin", onFocusGained);
|
|
el.addEventListener("focusout", onFocusLost);
|
|
return () => {
|
|
el.removeEventListener("focusin", onFocusGained);
|
|
el.removeEventListener("focusout", onFocusLost);
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (isFocused) {
|
|
remote.getCurrentWindow().setTouchBar(new remote.TouchBar({ items }));
|
|
}
|
|
});
|
|
|
|
return (
|
|
<TouchBarContext.Provider value={api}>
|
|
{children}
|
|
</TouchBarContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function TouchBarLabel({ label }: LabelProps) {
|
|
const api = useContext(TouchBarContext);
|
|
|
|
if (api) {
|
|
const item = new api.TouchBar.TouchBarLabel({
|
|
label
|
|
});
|
|
api.addItem(item);
|
|
}
|
|
|
|
return <></>;
|
|
}
|
|
|
|
export function TouchBarSlider({ label, value, minValue, maxValue, onChange }: SliderProps) {
|
|
const api = useContext(TouchBarContext);
|
|
|
|
if (api) {
|
|
const item = new api.TouchBar.TouchBarSlider({
|
|
label,
|
|
value, minValue, maxValue,
|
|
change: onChange
|
|
});
|
|
api.addItem(item);
|
|
}
|
|
|
|
return <></>;
|
|
}
|
|
|
|
export function TouchBarButton({ label, icon, click, enabled }: 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
|
|
});
|
|
}, [ label, icon ]);
|
|
|
|
if (item && api) {
|
|
api.addItem(item);
|
|
}
|
|
|
|
return <></>;
|
|
}
|
|
|
|
export function TouchBarSegmentedControl({ mode, segments, selectedIndex, onChange }: SegmentedControlProps) {
|
|
const api = useContext(TouchBarContext);
|
|
|
|
if (api) {
|
|
const processedSegments: Electron.SegmentedControlSegment[] = segments.map(({icon, ...restProps}) => ({
|
|
...restProps,
|
|
icon: icon ? buildIcon(api.nativeImage, icon) : undefined
|
|
}));
|
|
const item = new api.TouchBar.TouchBarSegmentedControl({
|
|
mode, selectedIndex,
|
|
segments: processedSegments,
|
|
change: (selectedIndex, isSelected) => {
|
|
if (segments[selectedIndex].onClick) {
|
|
segments[selectedIndex].onClick();
|
|
} else if (onChange) {
|
|
onChange(selectedIndex, isSelected);
|
|
}
|
|
}
|
|
});
|
|
api.addItem(item);
|
|
}
|
|
|
|
return <></>;
|
|
}
|
|
|
|
export function TouchBarSpacer({ size }: SpacerProps) {
|
|
const api = useContext(TouchBarContext);
|
|
|
|
if (api) {
|
|
const item = new api.TouchBar.TouchBarSpacer({
|
|
size
|
|
});
|
|
api.addItem(item);
|
|
}
|
|
|
|
return <></>;
|
|
}
|
|
|
|
function buildIcon(nativeImage: typeof Electron.nativeImage, name: string) {
|
|
const sourceImage = nativeImage.createFromNamedImage(name, [-1, 0, 1]);
|
|
const { width, height } = sourceImage.getSize();
|
|
const newImage = nativeImage.createEmpty();
|
|
newImage.addRepresentation({
|
|
scaleFactor: 1,
|
|
width: width / 2,
|
|
height: height / 2,
|
|
buffer: sourceImage.resize({ height: height / 2 }).toBitmap()
|
|
});
|
|
newImage.addRepresentation({
|
|
scaleFactor: 2,
|
|
width: width,
|
|
height: height,
|
|
buffer: sourceImage.toBitmap()
|
|
});
|
|
return newImage;
|
|
}
|