feat: overrides by each icon type
This commit is contained in:
parent
12e9e61b1e
commit
f1c210edb3
8 changed files with 138 additions and 100 deletions
|
@ -165,6 +165,8 @@ handle(IpcEvents.SET_TRAY_ICON, (_, iconURI) => setTrayIcon(iconURI));
|
|||
handle(IpcEvents.GET_TRAY_ICON, (_, iconPath) => getTrayIconFile(iconPath));
|
||||
handleSync(IpcEvents.GET_TRAY_ICON_SYNC, (_, iconPath) => getTrayIconFileSync(iconPath));
|
||||
handle(IpcEvents.GET_SYSTEM_ACCENT_COLOR, () => `#${systemPreferences.getAccentColor?.() || ""}`);
|
||||
handle(IpcEvents.CREATE_TRAY_ICON_RESPONSE, (_, iconName, dataURL) => createTrayIcon(iconName, dataURL));
|
||||
handle(IpcEvents.CREATE_TRAY_ICON_RESPONSE, (_, iconName, dataURL, isCustomIcon) =>
|
||||
createTrayIcon(iconName, dataURL, isCustomIcon)
|
||||
);
|
||||
handle(IpcEvents.GENERATE_TRAY_ICONS, () => generateTrayIcons());
|
||||
handle(IpcEvents.SELECT_TRAY_ICON, async (_, iconName) => pickTrayIcon(iconName));
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
dialog,
|
||||
Menu,
|
||||
MenuItemConstructorOptions,
|
||||
NativeImage,
|
||||
nativeImage,
|
||||
nativeTheme,
|
||||
screen,
|
||||
|
@ -39,7 +40,7 @@ import {
|
|||
} from "./constants";
|
||||
import { Settings, State, VencordSettings } from "./settings";
|
||||
import { createSplashWindow } from "./splash";
|
||||
import { generateTrayIcons } from "./tray";
|
||||
import { generateTrayIcons, isCustomIcon, statusToSettingsKey } from "./tray";
|
||||
import { makeLinksOpenExternally } from "./utils/makeLinksOpenExternally";
|
||||
import { applyDeckKeyboardFix, askToApplySteamLayout, isDeckGameMode } from "./utils/steamOS";
|
||||
import { downloadVencordFiles, ensureVencordFiles } from "./utils/vencordLoader";
|
||||
|
@ -127,10 +128,12 @@ function initTray(win: BrowserWindow) {
|
|||
|
||||
tray = new Tray(ICON_PATH);
|
||||
try {
|
||||
if (Settings.store.trayCustom) tray.setImage(join(ICONS_DIR, "icon.png"));
|
||||
if (Settings.store.trayMainOverride) tray.setImage(join(ICONS_DIR, "icon_custom.png"));
|
||||
else tray.setImage(join(ICONS_DIR, "icon.png"));
|
||||
} catch (error) {
|
||||
console.log("Error while loading custom tray image. Recreating new ones.");
|
||||
generateTrayIcons(true);
|
||||
Settings.store.trayMainOverride = false;
|
||||
generateTrayIcons();
|
||||
}
|
||||
tray.setToolTip("Vesktop");
|
||||
tray.setContextMenu(trayMenu);
|
||||
|
@ -513,17 +516,30 @@ export async function createWindows() {
|
|||
export async function setTrayIcon(iconName: string) {
|
||||
if (!tray || tray.isDestroyed()) return;
|
||||
const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]);
|
||||
if (Icons.has(iconName)) {
|
||||
|
||||
if (!Icons.has(iconName)) return;
|
||||
try {
|
||||
var trayImage = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
||||
var trayImage: NativeImage;
|
||||
if (isCustomIcon(iconName)) {
|
||||
trayImage = nativeImage.createFromPath(join(ICONS_DIR, iconName + "_custom.png"));
|
||||
if (trayImage.isEmpty()) {
|
||||
const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey];
|
||||
Settings.store[iconKey] = false;
|
||||
generateTrayIcons();
|
||||
return;
|
||||
}
|
||||
} else trayImage = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
||||
if (trayImage.isEmpty()) {
|
||||
generateTrayIcons();
|
||||
return;
|
||||
}
|
||||
if (process.platform === "darwin") {
|
||||
trayImage = trayImage.resize({ width: 16, height: 16 });
|
||||
}
|
||||
tray.setImage(trayImage);
|
||||
} catch (error) {
|
||||
console.log("Error: ", error, "Regenerating tray icons.");
|
||||
generateTrayIcons(true);
|
||||
generateTrayIcons(); // TODO: pass here only one icon request
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 2023 Vendicated and Vencord contributors
|
||||
*/
|
||||
|
||||
import { dialog, nativeImage } from "electron";
|
||||
import { dialog, NativeImage, nativeImage } from "electron";
|
||||
import { copyFileSync, mkdirSync, writeFileSync } from "fs";
|
||||
import { readFile } from "fs/promises";
|
||||
import { join } from "path";
|
||||
|
@ -14,6 +14,19 @@ import { ICONS_DIR, STATIC_DIR } from "shared/paths";
|
|||
import { mainWin } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
|
||||
export const statusToSettingsKey = {
|
||||
speaking: "traySpeakingOverride",
|
||||
muted: "trayMutedOverride",
|
||||
deafened: "trayDeafenedOverride",
|
||||
idle: "trayIdleOverride",
|
||||
icon: "trayMainOverride"
|
||||
};
|
||||
|
||||
export const isCustomIcon = (status: string) => {
|
||||
const settingKey = statusToSettingsKey[status as keyof typeof statusToSettingsKey];
|
||||
return Settings.store[settingKey];
|
||||
};
|
||||
|
||||
export async function getTrayIconFile(iconName: string) {
|
||||
const Icons = new Set(["speaking", "muted", "deafened", "idle"]);
|
||||
if (!Icons.has(iconName)) {
|
||||
|
@ -26,32 +39,45 @@ export async function getTrayIconFile(iconName: string) {
|
|||
export function getTrayIconFileSync(iconName: string) {
|
||||
// returns dataURL of image from TrayIcons folder
|
||||
const Icons = new Set(["speaking", "muted", "deafened", "idle", "icon"]);
|
||||
|
||||
if (Icons.has(iconName)) {
|
||||
const img = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png")).resize({ width: 128, height: 128 });
|
||||
if (img.isEmpty()) return nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png")).toDataURL();
|
||||
var img: NativeImage;
|
||||
if (isCustomIcon(iconName)) {
|
||||
img = nativeImage.createFromPath(join(ICONS_DIR, iconName + "_custom.png"));
|
||||
} else img = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
||||
img = img.resize({ width: 128, height: 128 });
|
||||
if (img.isEmpty()) {
|
||||
console.log("Can't open icon file. Regenerating.");
|
||||
generateTrayIcons();
|
||||
img = nativeImage.createFromPath(join(ICONS_DIR, iconName + ".png"));
|
||||
const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey];
|
||||
Settings.store[iconKey] = false;
|
||||
}
|
||||
return img.toDataURL();
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTrayIcon(iconName: string, iconDataURL: string) {
|
||||
export async function createTrayIcon(iconName: string, iconDataURL: string, isCustomIcon: boolean = false) {
|
||||
// creates .png at config/TrayIcons/iconName.png from given iconDataURL
|
||||
// primarily called from renderer using CREATE_TRAY_ICON_RESPONSE IPC call
|
||||
iconDataURL = iconDataURL.replace(/^data:image\/png;base64,/, "");
|
||||
if (isCustomIcon) {
|
||||
writeFileSync(join(ICONS_DIR, iconName + "_custom.png"), iconDataURL, "base64");
|
||||
} else {
|
||||
writeFileSync(join(ICONS_DIR, iconName + ".png"), iconDataURL, "base64");
|
||||
}
|
||||
mainWin.webContents.send(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON);
|
||||
}
|
||||
|
||||
export async function generateTrayIcons(force = false) {
|
||||
export async function generateTrayIcons() {
|
||||
// this function generates tray icons as .png's in Vesktop cache for future use
|
||||
mkdirSync(ICONS_DIR, { recursive: true });
|
||||
if (force || !Settings.store.trayCustom) {
|
||||
const Icons = ["speaking", "muted", "deafened", "idle"];
|
||||
for (const icon of Icons) {
|
||||
mainWin.webContents.send(IpcEvents.CREATE_TRAY_ICON_REQUEST, icon);
|
||||
}
|
||||
copyFileSync(join(STATIC_DIR, "icon.png"), join(ICONS_DIR, "icon.png"));
|
||||
mainWin.webContents.send(IpcEvents.SET_CURRENT_VOICE_TRAY_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
export async function pickTrayIcon(iconName: string) {
|
||||
|
@ -67,6 +93,6 @@ export async function pickTrayIcon(iconName: string) {
|
|||
// add .svg !!
|
||||
const image = nativeImage.createFromPath(dir);
|
||||
if (image.isEmpty()) return "invalid";
|
||||
copyFileSync(dir, join(ICONS_DIR, iconName + ".png"));
|
||||
copyFileSync(dir, join(ICONS_DIR, iconName + "_custom.png"));
|
||||
return dir;
|
||||
}
|
||||
|
|
|
@ -86,8 +86,8 @@ export const VesktopNative = {
|
|||
setIcon: (iconURI: string) => invoke<void>(IpcEvents.SET_TRAY_ICON, iconURI),
|
||||
getIcon: (iconName: string) => invoke<string>(IpcEvents.GET_TRAY_ICON, iconName),
|
||||
getIconSync: (iconName: string) => sendSync<string>(IpcEvents.GET_TRAY_ICON_SYNC, iconName),
|
||||
createIconResponse: (iconName: string, iconDataURL: string) =>
|
||||
invoke<void>(IpcEvents.CREATE_TRAY_ICON_RESPONSE, iconName, iconDataURL),
|
||||
createIconResponse: (iconName: string, iconDataURL: string, isCustomIcon: boolean = true) =>
|
||||
invoke<void>(IpcEvents.CREATE_TRAY_ICON_RESPONSE, iconName, iconDataURL, isCustomIcon),
|
||||
createIconRequest: (listener: (iconName: string) => void) => {
|
||||
ipcRenderer.on(IpcEvents.CREATE_TRAY_ICON_REQUEST, (_, iconPath: string) => listener(iconPath));
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ import "./traySetting.css";
|
|||
|
||||
import { Margins, Modals, ModalSize, openModal } from "@vencord/types/utils";
|
||||
import { findByCodeLazy, findByPropsLazy } from "@vencord/types/webpack";
|
||||
import { Forms, Select, Switch, Toasts } from "@vencord/types/webpack/common";
|
||||
import { Button, Forms, Select, Switch, Toasts } from "@vencord/types/webpack/common";
|
||||
import { setCurrentTrayIcon } from "renderer/patches/tray";
|
||||
import { useSettings } from "renderer/settings";
|
||||
import { isLinux } from "renderer/utils";
|
||||
|
@ -35,7 +35,16 @@ if (!isLinux)
|
|||
if (color) presets.unshift(color);
|
||||
});
|
||||
|
||||
const statusToSettingsKey = {
|
||||
icon: { key: "trayMainOverride", label: "Main Icon" },
|
||||
idle: { key: "trayIdleOverride", label: "Idle icon" },
|
||||
speaking: { key: "traySpeakingOverride", label: "Speaking icon" },
|
||||
muted: { key: "trayMutedOverride", label: "Muted icon" },
|
||||
deafened: { key: "trayDeafenedOverride", label: "Deafened icon" }
|
||||
};
|
||||
|
||||
function trayEditButton(iconName: string) {
|
||||
const Settings = useSettings();
|
||||
return (
|
||||
<div className="vcd-tray-icon-wrap">
|
||||
<img
|
||||
|
@ -63,15 +72,15 @@ function trayEditButton(iconName: string) {
|
|||
});
|
||||
return;
|
||||
}
|
||||
console.log("choice:", choice);
|
||||
// copy image and reload
|
||||
// settings.trayIconPath = choice;
|
||||
|
||||
const iconKey = statusToSettingsKey[iconName as keyof typeof statusToSettingsKey].key;
|
||||
Settings[iconKey] = true;
|
||||
const iconDataURL = VesktopNative.tray.getIconSync(iconName);
|
||||
const img = document.getElementById(iconName) as HTMLImageElement;
|
||||
if (img) {
|
||||
img.src = iconDataURL;
|
||||
}
|
||||
VesktopNative.tray.createIconResponse(iconName, iconDataURL);
|
||||
setCurrentTrayIcon();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -80,6 +89,7 @@ function trayEditButton(iconName: string) {
|
|||
|
||||
function TrayModalComponent({ modalProps, close }: { modalProps: any; close: () => void }) {
|
||||
const Settings = useSettings();
|
||||
|
||||
return (
|
||||
<Modals.ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<Modals.ModalHeader className="vcd-custom-tray-header">
|
||||
|
@ -87,45 +97,27 @@ function TrayModalComponent({ modalProps, close }: { modalProps: any; close: ()
|
|||
<Modals.ModalCloseButton onClick={close} />
|
||||
</Modals.ModalHeader>
|
||||
<Modals.ModalContent className="vcd-custom-tray-modal">
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
{Object.entries(statusToSettingsKey).map(([status, { key, label }]) => (
|
||||
<div key={status}>
|
||||
<Forms.FormSection className="vcd-custom-tray-icon-section">
|
||||
<Forms.FormText className={Margins.top16 + " vcd-custom-tray-icon-form-text"}>
|
||||
Main icon
|
||||
</Forms.FormText>
|
||||
{trayEditButton("icon")}
|
||||
</Forms.FormSection>
|
||||
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
<Forms.FormSection className="vcd-custom-tray-icon-section">
|
||||
<Forms.FormText className={Margins.top16 + " vcd-custom-tray-icon-form-text"}>
|
||||
Idle icon
|
||||
</Forms.FormText>
|
||||
{trayEditButton("idle")}
|
||||
</Forms.FormSection>
|
||||
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
<Forms.FormSection className="vcd-custom-tray-icon-section">
|
||||
<Forms.FormText className={Margins.top16 + " vcd-custom-tray-icon-form-text"}>
|
||||
Speaking icon
|
||||
</Forms.FormText>
|
||||
{trayEditButton("speaking")}
|
||||
</Forms.FormSection>
|
||||
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
<Forms.FormSection className="vcd-custom-tray-icon-section">
|
||||
<Forms.FormText className={Margins.top16 + " vcd-custom-tray-icon-form-text"}>
|
||||
Muted icon
|
||||
</Forms.FormText>
|
||||
{trayEditButton("muted")}
|
||||
</Forms.FormSection>
|
||||
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
<Forms.FormSection className="vcd-custom-tray-icon-section">
|
||||
<Forms.FormText className={Margins.top16 + " vcd-custom-tray-icon-form-text"}>
|
||||
Deafened icon
|
||||
</Forms.FormText>
|
||||
{trayEditButton("deafened")}
|
||||
<div className="vcd-custom-tray-icon-label">
|
||||
{trayEditButton(status)}
|
||||
<Forms.FormText>{label}</Forms.FormText>
|
||||
</div>
|
||||
{Settings[key] && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
Settings[key] = false;
|
||||
setCurrentTrayIcon();
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
)}
|
||||
</Forms.FormSection>
|
||||
<Forms.FormDivider className={`${Margins.top8} ${Margins.bottom8}`} />
|
||||
</div>
|
||||
))}
|
||||
</Modals.ModalContent>
|
||||
<Modals.ModalFooter></Modals.ModalFooter>
|
||||
</Modals.ModalRoot>
|
||||
|
@ -156,37 +148,28 @@ export const CustomizeTraySwitch: SettingsComponent = ({ settings }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div id="vcd-tray-setting">
|
||||
<div className="vcd-tray-setting-switch">
|
||||
<Switch
|
||||
key="tray"
|
||||
value={settings.trayCustom ?? false}
|
||||
onChange={v => (settings.trayCustom = v)}
|
||||
note={"Use custom default and voice status tray icons."}
|
||||
>
|
||||
Use custom tray icons
|
||||
</Switch>
|
||||
<div className="vcd-tray-settings">
|
||||
<div className="vcd-tray-container">
|
||||
<div className="vcd-tray-settings-labels">
|
||||
<Forms.FormTitle tag="h3">Custom tray icons</Forms.FormTitle>
|
||||
<Forms.FormText>Use custom default and voice status tray icons.</Forms.FormText>
|
||||
</div>
|
||||
<div className="vcd-tray-setting-customize">
|
||||
<Forms.FormText>
|
||||
<a
|
||||
href="about:blank"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
<Button
|
||||
onClick={async () => {
|
||||
openTrayModal();
|
||||
}}
|
||||
>
|
||||
Configure
|
||||
</a>
|
||||
</Forms.FormText>
|
||||
</Button>
|
||||
</div>
|
||||
<Forms.FormDivider className={Margins.top20 + " " + Margins.bottom20} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const TrayIconPicker: SettingsComponent = ({ settings }) => {
|
||||
if (!settings.tray || settings.trayCustom) return null;
|
||||
if (!settings.tray) return null;
|
||||
return (
|
||||
<div className="vcd-tray-settings">
|
||||
<div className="vcd-tray-container">
|
||||
|
@ -211,7 +194,7 @@ export const TrayIconPicker: SettingsComponent = ({ settings }) => {
|
|||
};
|
||||
|
||||
export const TrayFillColorSwitch: SettingsComponent = ({ settings }) => {
|
||||
if (!settings.tray || settings.trayCustom) return null;
|
||||
if (!settings.tray) return null;
|
||||
return (
|
||||
<div className="vcd-tray-settings">
|
||||
<div className="vcd-tray-container">
|
||||
|
|
|
@ -76,8 +76,15 @@
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.vcd-custom-tray-icon-form-text {
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
.vcd-custom-tray-icon-label {
|
||||
margin-top: 0.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
|
@ -49,7 +49,7 @@ VesktopNative.tray.createIconRequest(async (iconName: string) => {
|
|||
if (ctx) {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const dataURL = canvas.toDataURL("image/png");
|
||||
VesktopNative.tray.createIconResponse(iconName, dataURL);
|
||||
VesktopNative.tray.createIconResponse(iconName, dataURL, false);
|
||||
}
|
||||
};
|
||||
img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
||||
|
|
6
src/shared/settings.d.ts
vendored
6
src/shared/settings.d.ts
vendored
|
@ -13,7 +13,11 @@ export interface Settings {
|
|||
tray?: boolean;
|
||||
trayColor?: string;
|
||||
trayAutoFill?: "auto" | "white" | "black";
|
||||
trayCustom?: boolean;
|
||||
trayMainOverride?: boolean;
|
||||
trayIdleOverride?: boolean;
|
||||
trayMutedOverride?: boolean;
|
||||
traySpeakingOverride?: boolean;
|
||||
trayDeafenedOverride?: boolean;
|
||||
minimizeToTray?: boolean;
|
||||
openLinksWithElectron?: boolean;
|
||||
staticTitle?: boolean;
|
||||
|
|
Loading…
Reference in a new issue