import QtQuick import qs.config import qs.services Rectangle { id: root property var hostWindow: null readonly property var myHomeDiagnostics: pageLoader.status === Loader.Ready && pageLoader.item && pageLoader.item.objectName === "my-home-page" ? pageLoader.item.pageDiagnostics : ({}) // The objectName check alone is not enough. This binding re-evaluates // while the loader is swapping pages, and for a moment `item` is the new // page carrying the old objectName -- calling uiDiagnostics() on it threw // a TypeError every time somebody navigated away from System Health. // Asking whether the function is actually there is the only test that // holds during the swap. readonly property var healthDiagnostics: pageLoader.status === Loader.Ready && pageLoader.item && pageLoader.item.objectName === "system-health-page" && typeof pageLoader.item.uiDiagnostics === "function" ? pageLoader.item.uiDiagnostics() : ({}) function requestHealthAction(id: string): bool { if (pageLoader.status !== Loader.Ready || !pageLoader.item || pageLoader.item.objectName !== "system-health-page") return false; return pageLoader.item.activateRenderedAction(id); } color: Theme.bg radius: 18 border.width: 1 border.color: Theme.alpha(Theme.fg, 0.08) clip: true SettingsSidebar { id: sidebar anchors.left: parent.left anchors.top: titlebar.bottom anchors.bottom: parent.bottom selectedPage: ShellState.settingsPage onPageRequested: page => ShellState.openSettings(page) } // The one titlebar Panama draws, so it follows Panama's own titlebar // rules: the close button sits on the configured side, there is no // minimize (Hyprland has no minimize — the button this bar used to show // was a lie), and turning the titlebar off leaves the window pure // Hyprland: Super+Q closes, Super+drag moves, Escape still works. Rectangle { id: titlebar readonly property bool shown: DesktopPreferences.get("panamaTitlebar") !== false readonly property bool buttonsLeft: DesktopPreferences.get("titlebarButtonSide") === "left" anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top height: shown ? 48 : 0 visible: shown color: Theme.alpha(Theme.bgDark, 0.97) border.width: 0 Text { anchors.left: titlebar.buttonsLeft ? undefined : parent.left anchors.right: titlebar.buttonsLeft ? parent.right : undefined anchors.leftMargin: 18 anchors.rightMargin: 18 anchors.verticalCenter: parent.verticalCenter text: "Settings" color: Theme.fg font.family: Theme.fontFamily font.pixelSize: Theme.fontSize font.weight: Font.DemiBold } Rectangle { id: closeButton anchors.left: titlebar.buttonsLeft ? parent.left : undefined anchors.right: titlebar.buttonsLeft ? undefined : parent.right anchors.leftMargin: 12 anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter width: 28 height: 28 radius: 9 color: closeMouse.containsMouse || activeFocus ? Theme.alpha(Theme.danger, 0.17) : Theme.alpha(Theme.fg, 0) border.width: activeFocus ? 2 : 0 border.color: Theme.alpha(Theme.danger, 0.6) activeFocusOnTab: true Accessible.role: Accessible.Button Accessible.name: "Close Settings" Keys.onReturnPressed: ShellState.closeSettings() Keys.onSpacePressed: ShellState.closeSettings() Text { anchors.centerIn: parent text: "×" color: closeMouse.containsMouse || closeButton.activeFocus ? Theme.danger : Theme.fgDim font.family: Theme.fontFamily font.pixelSize: 18 } MouseArea { id: closeMouse anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: ShellState.closeSettings() } } DragHandler { target: null onActiveChanged: if (active && root.hostWindow) root.hostWindow.startSystemMove() } } Rectangle { anchors.left: sidebar.right anchors.top: titlebar.bottom anchors.bottom: parent.bottom width: 1 color: Theme.alpha(Theme.fg, 0.06) } // The tab strip for the current category, rendered above the page so the // leaf pages themselves stay untouched by the grouping. A category with a // single subject earns no strip. readonly property var categoryTabs: SettingsRoutes.tabsFor(ShellState.settingsPage) Item { id: categoryStrip readonly property bool shown: root.categoryTabs.length > 1 anchors.left: sidebar.right anchors.right: parent.right anchors.top: titlebar.bottom anchors.leftMargin: 34 anchors.rightMargin: 34 height: shown ? 48 : 0 visible: shown SettingsTabs { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom tabs: root.categoryTabs.map(tab => ({ value: tab.page, label: tab.label })) current: ShellState.settingsPage onSelected: value => ShellState.openSettings(value) } } Loader { id: pageLoader anchors.left: sidebar.right anchors.right: parent.right anchors.top: categoryStrip.bottom anchors.bottom: parent.bottom sourceComponent: { switch (ShellState.settingsPage) { case "appearance": return appearancePage; case "displays": return displaysPage; case "connectivity": return connectivityPage; case "my-home": return myHomePage; case "phone": return phonePage; case "bar": return barPage; case "dock": return dockPage; case "control-center": return controlCenterPage; case "tiling": return tilingPage; case "workspaces": return workspacesPage; case "sync": return syncPage; case "sound": return soundPage; case "gaming": return gamingPage; case "notifications": return notificationsPage; case "focus": return focusPage; case "screen-intelligence": return screenIntelligencePage; case "shortcuts": return shortcutsPage; case "mouse": return mousePage; case "dictation": return dictationPage; case "privacy": return privacyPage; case "accounts": return onlineAccountsPage; case "accessibility": return accessibilityPage; case "power": return powerPage; case "datetime": return dateTimePage; case "applications": return applicationsPage; case "storage": return storagePage; case "snapshots": return snapshotsPage; case "updates": return updatesPage; case "users": return usersPage; case "sharing": return sharingPage; case "firewall": return firewallPage; case "printers": return printersPage; case "containers": return containersPage; case "ssh-keys": return sshKeysPage; case "services": return healthPage; case "agents": return agentsPage; case "manual": return manualPage; case "about": return aboutPage; default: return homePage; } } } Rectangle { anchors.right: parent.right anchors.rightMargin: 22 anchors.bottom: parent.bottom anchors.bottomMargin: 20 width: Math.min(460, errorCopy.implicitWidth + 34) height: 42 radius: 12 visible: SystemSettings.lastError !== "" color: Theme.mix(Theme.bgPopover, Theme.danger, 0.1) border.width: 1 border.color: Theme.alpha(Theme.danger, 0.28) Text { id: errorCopy anchors.centerIn: parent text: SystemSettings.lastError color: Theme.fg font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: SystemSettings.lastError = "" } } Component { id: homePage; HomePage {} } Component { id: applicationsPage; ApplicationsPage {} } Component { id: storagePage; StoragePage {} } Component { id: snapshotsPage; SnapshotsPage {} } Component { id: updatesPage; UpdatesPage {} } Component { id: usersPage; UsersPage {} } Component { id: sharingPage; SharingPage {} } Component { id: firewallPage; FirewallPage {} } Component { id: containersPage; ContainersPage {} } Component { id: sshKeysPage; SshKeysPage {} } Component { id: printersPage; PrintersPage {} } Component { id: accessibilityPage; AccessibilityPage {} } Component { id: powerPage; PowerPage {} } Component { id: dateTimePage; DateTimePage {} } Component { id: appearancePage; AppearancePage {} } Component { id: displaysPage; DisplaysPage {} } Component { id: connectivityPage; ConnectivityPage {} } Component { id: myHomePage; MyHomePage {} } Component { id: phonePage; PhonePage {} } Component { id: barPage; BarPage {} } Component { id: dockPage; DockPage {} } Component { id: controlCenterPage; ControlCenterPage {} } Component { id: tilingPage; TilingPage {} } Component { id: workspacesPage; WorkspacesPage {} } Component { id: syncPage; SyncPage {} } Component { id: soundPage; SoundPage {} } Component { id: gamingPage; GamingPage {} } Component { id: notificationsPage; NotificationsPage {} } Component { id: focusPage; FocusPage {} } Component { id: screenIntelligencePage; ScreenIntelligencePage {} } Component { id: shortcutsPage; ShortcutsPage {} } Component { id: mousePage; MousePage {} } Component { id: dictationPage; DictationPage {} } Component { id: privacyPage; PrivacyPage {} } Component { id: onlineAccountsPage; OnlineAccountsPage {} } Component { id: healthPage; HealthPage {} } Component { id: agentsPage; AgentsPage {} } Component { id: manualPage; ManualPage {} } Component { id: aboutPage; AboutPage {} } Shortcut { sequence: "Escape" onActivated: ShellState.closeSettings() } }