Add the system's native menu to the titlebar, configurable
This commit is contained in:
parent
efbba2a65f
commit
09662fc37e
5 changed files with 109 additions and 3 deletions
57
avia_core/customFrameNativeMenu.js
Normal file
57
avia_core/customFrameNativeMenu.js
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
(function () {
|
||||||
|
if (window.__customFrameNativeMenu) return;
|
||||||
|
window.__customFrameNativeMenu = true;
|
||||||
|
|
||||||
|
function toggleCheckbox(newElem, toggle) {
|
||||||
|
const tmp = newElem.querySelector("mdui-checkbox");
|
||||||
|
if (tmp && tmp !== undefined) {
|
||||||
|
if (toggle) {
|
||||||
|
tmp.setAttribute('checked', '');
|
||||||
|
tmp.setAttribute('value', 'on');
|
||||||
|
} else {
|
||||||
|
tmp.removeAttribute('checked');
|
||||||
|
tmp.setAttribute('value', 'off');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initCFNM() {
|
||||||
|
let elem = document.querySelector("#floating div:not(:empty) div.will-change_transform.flex_1_1_800px div:has(> a) > a:last-child")
|
||||||
|
if (!elem) { return; }
|
||||||
|
|
||||||
|
let title = elem.querySelector("div.flex-g_1 > div")
|
||||||
|
if (!title || title.textContent.trim() !== "Custom window frame") { return; }
|
||||||
|
|
||||||
|
let desc = elem.querySelector("div.flex-g_1 > span")
|
||||||
|
if (!desc || desc.textContent.trim() !== "Let Stoat use its own custom titlebar.") { return; }
|
||||||
|
|
||||||
|
var newElem = elem.cloneNode(true);
|
||||||
|
let nTitle = newElem.querySelector("div.flex-g_1 > div");
|
||||||
|
let nDesc = newElem.querySelector("div.flex-g_1 > span");
|
||||||
|
if (!nTitle || !nDesc) { newElem = null; return; }
|
||||||
|
|
||||||
|
nTitle.textContent = "Native menu on custom window frame";
|
||||||
|
nDesc.textContent = "Use the system's native menu on the custom window frame.";
|
||||||
|
|
||||||
|
let config = window.desktopConfig.get();
|
||||||
|
toggleCheckbox(newElem, config.customFrameNativeMenu);
|
||||||
|
|
||||||
|
newElem.addEventListener("click", e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let config = window.desktopConfig.get();
|
||||||
|
config.customFrameNativeMenu = !config.customFrameNativeMenu;
|
||||||
|
window.desktopConfig.set(config);
|
||||||
|
|
||||||
|
toggleCheckbox(newElem, config.customFrameNativeMenu);
|
||||||
|
});
|
||||||
|
|
||||||
|
elem.parentNode.appendChild(newElem);
|
||||||
|
}
|
||||||
|
|
||||||
|
initCFNM();
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => initCFNM());
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
})();
|
||||||
1
src/config.d.ts
vendored
1
src/config.d.ts
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
declare type DesktopConfig = {
|
declare type DesktopConfig = {
|
||||||
firstLaunch: boolean;
|
firstLaunch: boolean;
|
||||||
customFrame: boolean;
|
customFrame: boolean;
|
||||||
|
customFrameNativeMenu: boolean;
|
||||||
minimiseToTray: boolean;
|
minimiseToTray: boolean;
|
||||||
spellchecker: boolean;
|
spellchecker: boolean;
|
||||||
hardwareAcceleration: boolean;
|
hardwareAcceleration: boolean;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ const loadInject = () => {
|
||||||
"repofrontend.js",
|
"repofrontend.js",
|
||||||
"ButtonFix.js",
|
"ButtonFix.js",
|
||||||
"headliner.js",
|
"headliner.js",
|
||||||
"aviadesktopversion.js"
|
"aviadesktopversion.js",
|
||||||
|
"customFrameNativeMenu.js"
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const plugin of plugins) {
|
for (const plugin of plugins) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ const schema = {
|
||||||
customFrame: {
|
customFrame: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
} as JSONSchema.Boolean,
|
} as JSONSchema.Boolean,
|
||||||
|
customFrameNativeMenu: {
|
||||||
|
type: "boolean",
|
||||||
|
} as JSONSchema.Boolean,
|
||||||
minimiseToTray: {
|
minimiseToTray: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
} as JSONSchema.Boolean,
|
} as JSONSchema.Boolean,
|
||||||
|
|
@ -55,6 +58,7 @@ const store = new Store({
|
||||||
defaults: {
|
defaults: {
|
||||||
firstLaunch: true,
|
firstLaunch: true,
|
||||||
customFrame: true,
|
customFrame: true,
|
||||||
|
customFrameNativeMenu: false,
|
||||||
minimiseToTray: true,
|
minimiseToTray: true,
|
||||||
startMinimisedToTray: false,
|
startMinimisedToTray: false,
|
||||||
spellchecker: true,
|
spellchecker: true,
|
||||||
|
|
@ -78,6 +82,7 @@ class Config {
|
||||||
mainWindow.webContents.send("config", {
|
mainWindow.webContents.send("config", {
|
||||||
firstLaunch: this.firstLaunch,
|
firstLaunch: this.firstLaunch,
|
||||||
customFrame: this.customFrame,
|
customFrame: this.customFrame,
|
||||||
|
customFrameNativeMenu: this.customFrameNativeMenu,
|
||||||
minimiseToTray: this.minimiseToTray,
|
minimiseToTray: this.minimiseToTray,
|
||||||
startMinimisedToTray: this.startMinimisedToTray,
|
startMinimisedToTray: this.startMinimisedToTray,
|
||||||
spellchecker: this.spellchecker,
|
spellchecker: this.spellchecker,
|
||||||
|
|
@ -113,6 +118,19 @@ class Config {
|
||||||
this.sync();
|
this.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get customFrameNativeMenu() {
|
||||||
|
return (store as never as { get(k: string): boolean }).get("customFrameNativeMenu");
|
||||||
|
}
|
||||||
|
|
||||||
|
set customFrameNativeMenu(value: boolean) {
|
||||||
|
(store as never as { set(k: string, value: boolean): void }).set(
|
||||||
|
"customFrameNativeMenu",
|
||||||
|
value,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.sync();
|
||||||
|
}
|
||||||
|
|
||||||
get minimiseToTray() {
|
get minimiseToTray() {
|
||||||
return (store as never as { get(k: string): boolean }).get(
|
return (store as never as { get(k: string): boolean }).get(
|
||||||
"minimiseToTray",
|
"minimiseToTray",
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,14 @@ export function createMainWindow() {
|
||||||
height: 720,
|
height: 720,
|
||||||
backgroundColor: "#191919",
|
backgroundColor: "#191919",
|
||||||
frame: !config.customFrame,
|
frame: !config.customFrame,
|
||||||
|
...(config.customFrame && config.customFrameNativeMenu ? {
|
||||||
|
// remove the default titlebar
|
||||||
|
titleBarStyle: 'hidden',
|
||||||
|
// expose window controls in Windows/Linux
|
||||||
|
...(process.platform !== 'darwin' ? {
|
||||||
|
titleBarOverlay: true
|
||||||
|
} : {})
|
||||||
|
} : {}),
|
||||||
icon: windowIcon,
|
icon: windowIcon,
|
||||||
show: !startHidden,
|
show: !startHidden,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
|
|
@ -141,8 +149,29 @@ export function createMainWindow() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const initialCustomFrame: boolean = config.customFrame;
|
||||||
|
const initialCFNM: boolean = config.customFrameNativeMenu;
|
||||||
|
|
||||||
|
mainWindow.webContents.on("did-finish-load", () => {
|
||||||
// send the config
|
// send the config
|
||||||
mainWindow.webContents.on("did-finish-load", () => config.sync());
|
config.sync();
|
||||||
|
|
||||||
|
// on macOS add margin to the title, and hide custom controls
|
||||||
|
// We only use initial values other the menu can disappear
|
||||||
|
if (process.platform === 'darwin' &&
|
||||||
|
initialCustomFrame && initialCFNM) {
|
||||||
|
mainWindow.webContents.insertCSS(`
|
||||||
|
#root > div[style="display: flex; flex-direction: column; height: 100%;"] > div > div.h_29px {
|
||||||
|
&> div.d_flex:first-child {
|
||||||
|
margin-left: 75px;
|
||||||
|
}
|
||||||
|
&> a.place-items_center {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// configure spellchecker context menu
|
// configure spellchecker context menu
|
||||||
mainWindow.webContents.on("context-menu", (_, params) => {
|
mainWindow.webContents.on("context-menu", (_, params) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue