Move settings into a standalone Settings window
The tab-bar popover had outgrown itself. Settings now open in their own native window (a fourth window kind, built like the connection manager but not modal): a sidebar with Appearance, Transfers, Editor, File list and About, holding every popover row plus the visible-columns choice. Each control is an ordinary settings save, so the other windows follow live while it stays open. The gear button in the tab bar, Ctrl+, and the macOS app menu's "Settings..." (Cmd+,) open it. On macOS Cmd+W arrives as the main window's close-tab menu action, so bun closes the focused Settings window first instead of a main-window tab. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+34
-8
@@ -10,6 +10,7 @@ import { ManagerWindow } from "./manager-window";
|
||||
import { setLocale as setBackendLocale } from "./i18n";
|
||||
import { applyApplicationMenu } from "./menu";
|
||||
import { createAppRPC } from "./rpc-handlers";
|
||||
import { SettingsWindow } from "./settings-window";
|
||||
import { ConnectionStore } from "./store/connections";
|
||||
import { migrateLegacyDataDir } from "./store/paths";
|
||||
import { SettingsStore } from "./store/settings";
|
||||
@@ -52,6 +53,7 @@ const events = {
|
||||
openEditor: (_ctx: EditorOpenContext) => {},
|
||||
settingsChanged: (_settings: AppSettings) => {},
|
||||
openConnectionManager: () => {},
|
||||
openSettings: () => {},
|
||||
showAbout: () => {},
|
||||
};
|
||||
const rpc = createAppRPC({ registry, engine, store, settings, edits, watcher, events });
|
||||
@@ -108,29 +110,43 @@ const manager = new ManagerWindow({
|
||||
rebuildMenu: () => void applyApplicationMenu(store),
|
||||
onSettingsSaved: (s) => settingsSaved(s, "manager"),
|
||||
});
|
||||
const settingsWin = new SettingsWindow({
|
||||
settings,
|
||||
url,
|
||||
currentSettings: () => currentSettings,
|
||||
mainFrame: () => win.getFrame(),
|
||||
onSettingsSaved: (s) => settingsSaved(s, "settings"),
|
||||
showAbout: () => showAbout(),
|
||||
});
|
||||
|
||||
events.openEditor = (ctx) => editors.open(ctx);
|
||||
/**
|
||||
* Every settings save lands here, whichever window made it (editor and manager
|
||||
* windows save too — the scale shortcuts): side effects run once, then every
|
||||
* OTHER window follows. The sender is skipped — it already shows the new
|
||||
* values, and an echo arriving behind a newer local change (a held Cmd+) would
|
||||
* drag it backwards.
|
||||
* windows save too — the scale shortcuts — and the Settings window is nothing
|
||||
* but saves): side effects run once, then every OTHER window follows. The
|
||||
* sender is skipped — it already shows the new values, and an echo arriving
|
||||
* behind a newer local change (a held Cmd+) would drag it backwards.
|
||||
*/
|
||||
function settingsSaved(s: AppSettings, sender: "main" | "manager" | { editor: string }): void {
|
||||
function settingsSaved(
|
||||
s: AppSettings,
|
||||
sender: "main" | "manager" | "settings" | { editor: string },
|
||||
): void {
|
||||
engine.setConcurrency(effectiveConcurrency(s));
|
||||
const localeChanged = s.locale !== currentSettings.locale;
|
||||
currentSettings = s; // future windows' preloads bake the fresh theme
|
||||
if (localeChanged) {
|
||||
setBackendLocale(s.locale);
|
||||
void applyApplicationMenu(store); // relabel the native menu in the new language
|
||||
settingsWin.retitle();
|
||||
}
|
||||
if (sender !== "main") rpc.send.settingsChanged({ settings: s });
|
||||
if (sender !== "manager") manager.broadcastSettings(s);
|
||||
if (sender !== "settings") settingsWin.broadcastSettings(s);
|
||||
editors.broadcastSettings(s, typeof sender === "object" ? sender.editor : undefined);
|
||||
}
|
||||
events.settingsChanged = (s) => settingsSaved(s, "main");
|
||||
events.openConnectionManager = () => manager.open();
|
||||
events.openSettings = () => settingsWin.open();
|
||||
|
||||
function showAbout(): void {
|
||||
const { build } = BRANDING;
|
||||
@@ -149,6 +165,7 @@ events.showAbout = showAbout;
|
||||
// and the modal manager has nothing to guard
|
||||
win.on("close", () => {
|
||||
manager.close();
|
||||
settingsWin.close();
|
||||
editors.closeAllForAppExit();
|
||||
});
|
||||
|
||||
@@ -173,6 +190,7 @@ engine.onUpdate = (snapshot) => {
|
||||
if (
|
||||
!windowFocused &&
|
||||
!editors.anyFocused() &&
|
||||
!settingsWin.isFocused() &&
|
||||
(snapshot.state === "completed" || snapshot.state === "failed")
|
||||
) {
|
||||
Utils.showNotification({
|
||||
@@ -230,10 +248,14 @@ void applyApplicationMenu(store);
|
||||
ApplicationMenu.on("application-menu-clicked", (event) => {
|
||||
const action = (event as { data?: { action?: string } }).data?.action;
|
||||
if (!action) return;
|
||||
// ⌘W while an editor window is focused closes that editor (with its dirty
|
||||
// prompt), not a main-window tab. Other menu actions deliberately still
|
||||
// target the main window.
|
||||
// ⌘W while the Settings window or an editor window is focused closes THAT
|
||||
// window (an editor with its dirty prompt), not a main-window tab. Other
|
||||
// menu actions deliberately still target the main window.
|
||||
if (action === "close-tab") {
|
||||
if (settingsWin.isFocused()) {
|
||||
settingsWin.close();
|
||||
return;
|
||||
}
|
||||
const focusedEditor = editors.focusedKey();
|
||||
if (focusedEditor) {
|
||||
editors.requestClose(focusedEditor);
|
||||
@@ -244,6 +266,10 @@ ApplicationMenu.on("application-menu-clicked", (event) => {
|
||||
manager.open();
|
||||
return;
|
||||
}
|
||||
if (action === "settings") {
|
||||
settingsWin.open();
|
||||
return;
|
||||
}
|
||||
if (action === "about") {
|
||||
showAbout();
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user