import QtQuick import QtQuick.Controls import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Wayland import Quickshell.Io import "../../Common" import "../../Services" PanelWindow { id: controlCenterPopup visible: root.controlCenterVisible implicitWidth: 600 implicitHeight: 500 WlrLayershell.layer: WlrLayershell.Overlay WlrLayershell.exclusiveZone: -1 WlrLayershell.keyboardFocus: WlrKeyboardFocus.None color: "transparent" anchors { top: true left: true right: true bottom: true } property int currentTab: 0 // 0: Network, 1: Audio, 2: Bluetooth, 3: Display property bool powerOptionsExpanded: false Rectangle { width: Math.min(600, parent.width - Theme.spacingL * 2) height: { let baseHeight = Math.min(500, parent.height - Theme.barHeight - Theme.spacingS * 2) // Expand container when power menu is open if (controlCenterPopup.powerOptionsExpanded) { baseHeight += 70 // Extra space for power options } return baseHeight } x: Math.max(Theme.spacingL, parent.width - width - Theme.spacingL) y: Theme.barHeight + Theme.spacingXS color: Theme.surfaceContainer radius: Theme.cornerRadiusLarge border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: 1 opacity: root.controlCenterVisible ? 1.0 : 0.0 scale: root.controlCenterVisible ? 1.0 : 0.85 Behavior on opacity { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on scale { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on height { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Column { anchors.fill: parent anchors.margins: Theme.spacingL spacing: Theme.spacingM // Elegant User Header Column { width: parent.width spacing: Theme.spacingL // User Info Section - Jony Ive inspired Rectangle { width: parent.width height: 90 radius: Theme.cornerRadiusLarge color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: 1 Row { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: Theme.spacingL anchors.rightMargin: Theme.spacingL spacing: Theme.spacingL // Profile Picture Rectangle { width: 54 height: 54 radius: 27 color: Theme.primary border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12) border.width: 2 Image { anchors.fill: parent anchors.margins: 2 source: UserInfoService.profilePicture fillMode: Image.PreserveAspectCrop visible: UserInfoService.profileAvailable smooth: true layer.enabled: true layer.effect: MultiEffect { maskEnabled: true maskSource: Rectangle { width: parent.width height: parent.height radius: width / 2 visible: false } } } // Fallback icon when no profile picture Text { anchors.centerIn: parent text: "person" font.family: Theme.iconFont font.pixelSize: Theme.iconSize + 4 color: Theme.onPrimary visible: !UserInfoService.profileAvailable } } // User Info Text Column { anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS Text { text: UserInfoService.fullName || UserInfoService.username || "User" font.pixelSize: Theme.fontSizeLarge color: Theme.surfaceText font.weight: Font.Medium } Text { text: "Uptime: " + (UserInfoService.uptime || "Unknown") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText font.weight: Font.Normal } } } // Action Buttons - Power and Settings Row { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: Theme.spacingL spacing: Theme.spacingS // Power Button Rectangle { width: 40 height: 40 radius: 20 color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Rectangle { anchors.centerIn: parent width: parent.width height: parent.height radius: parent.radius color: "transparent" clip: true Text { anchors.centerIn: parent text: controlCenterPopup.powerOptionsExpanded ? "expand_less" : "power_settings_new" font.family: Theme.iconFont font.pixelSize: Theme.iconSize - 2 color: powerButton.containsMouse || controlCenterPopup.powerOptionsExpanded ? Theme.error : Theme.surfaceText Behavior on text { // Smooth icon transition SequentialAnimation { NumberAnimation { target: parent property: "opacity" to: 0.0 duration: Theme.shortDuration / 2 easing.type: Theme.standardEasing } PropertyAction { target: parent; property: "text" } NumberAnimation { target: parent property: "opacity" to: 1.0 duration: Theme.shortDuration / 2 easing.type: Theme.standardEasing } } } } } MouseArea { id: powerButton anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { controlCenterPopup.powerOptionsExpanded = !controlCenterPopup.powerOptionsExpanded } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } // Settings Button Rectangle { width: 40 height: 40 radius: 20 color: settingsButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Text { anchors.centerIn: parent text: "settings" font.family: Theme.iconFont font.pixelSize: Theme.iconSize - 2 color: Theme.surfaceText } MouseArea { id: settingsButton anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { root.controlCenterVisible = false root.settingsVisible = true } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } } // Animated Collapsible Power Options (moved here for better integration) Rectangle { width: parent.width height: controlCenterPopup.powerOptionsExpanded ? 60 : 0 radius: Theme.cornerRadius color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) border.width: controlCenterPopup.powerOptionsExpanded ? 1 : 0 opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0 clip: true Behavior on height { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on opacity { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Behavior on border.width { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } Row { anchors.centerIn: parent spacing: Theme.spacingL opacity: controlCenterPopup.powerOptionsExpanded ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } // Logout Rectangle { width: 100 height: 34 radius: Theme.cornerRadius color: logoutButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Row { anchors.centerIn: parent spacing: Theme.spacingXS Text { text: "logout" font.family: Theme.iconFont font.pixelSize: Theme.fontSizeSmall color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: "Logout" font.pixelSize: Theme.fontSizeSmall color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: logoutButton anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { controlCenterPopup.powerOptionsExpanded = false root.powerConfirmAction = "logout" root.powerConfirmTitle = "Logout" root.powerConfirmMessage = "Are you sure you want to logout?" root.powerConfirmVisible = true } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } // Reboot Rectangle { width: 100 height: 34 radius: Theme.cornerRadius color: rebootButton.containsMouse ? Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Row { anchors.centerIn: parent spacing: Theme.spacingXS Text { text: "restart_alt" font.family: Theme.iconFont font.pixelSize: Theme.fontSizeSmall color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: "Restart" font.pixelSize: Theme.fontSizeSmall color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: rebootButton anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { controlCenterPopup.powerOptionsExpanded = false root.powerConfirmAction = "reboot" root.powerConfirmTitle = "Restart" root.powerConfirmMessage = "Are you sure you want to restart?" root.powerConfirmVisible = true } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } // Shutdown Rectangle { width: 100 height: 34 radius: Theme.cornerRadius color: shutdownButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5) Row { anchors.centerIn: parent spacing: Theme.spacingXS Text { text: "power_settings_new" font.family: Theme.iconFont font.pixelSize: Theme.fontSizeSmall color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: "Shutdown" font.pixelSize: Theme.fontSizeSmall color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText font.weight: Font.Medium anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: shutdownButton anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { controlCenterPopup.powerOptionsExpanded = false root.powerConfirmAction = "poweroff" root.powerConfirmTitle = "Shutdown" root.powerConfirmMessage = "Are you sure you want to shutdown?" root.powerConfirmVisible = true } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } } // Tab buttons Row { width: parent.width spacing: Theme.spacingXS Repeater { model: { let tabs = [ {name: "Network", icon: "wifi", id: "network", available: true} ] // Always show audio tabs.push({name: "Audio", icon: "volume_up", id: "audio", available: true}) // Show Bluetooth only if available if (root.bluetoothAvailable) { tabs.push({name: "Bluetooth", icon: "bluetooth", id: "bluetooth", available: true}) } // Always show display tabs.push({name: "Display", icon: "brightness_6", id: "display", available: true}) return tabs } Rectangle { property int tabCount: { let count = 3 // Network + Audio + Display (always visible) if (root.bluetoothAvailable) count++ return count } width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount height: 40 radius: Theme.cornerRadius color: controlCenterPopup.currentTab === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent" Row { anchors.centerIn: parent spacing: Theme.spacingXS Text { text: modelData.icon font.family: Theme.iconFont font.pixelSize: Theme.iconSize - 4 color: controlCenterPopup.currentTab === index ? Theme.primary : Theme.surfaceText anchors.verticalCenter: parent.verticalCenter } Text { text: modelData.name font.pixelSize: Theme.fontSizeSmall color: controlCenterPopup.currentTab === index ? Theme.primary : Theme.surfaceText font.weight: controlCenterPopup.currentTab === index ? Font.Medium : Font.Normal anchors.verticalCenter: parent.verticalCenter } } MouseArea { id: tabArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { controlCenterPopup.currentTab = index } } Behavior on color { ColorAnimation { duration: Theme.shortDuration easing.type: Theme.standardEasing } } } } } } // Tab content area Rectangle { width: parent.width height: { // More generous height calculation - use most of the available space let baseHeight = parent.height // Subtract only the essential fixed elements baseHeight -= 90 + Theme.spacingL // User header + spacing baseHeight -= 40 + Theme.spacingM // Tab buttons + spacing baseHeight -= Theme.spacingM // Bottom spacing // Subtract power options height when expanded if (controlCenterPopup.powerOptionsExpanded) { baseHeight -= 60 + Theme.spacingL } return Math.max(300, baseHeight) // Higher minimum height for better content display } radius: Theme.cornerRadius color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) Behavior on height { NumberAnimation { duration: Theme.mediumDuration easing.type: Theme.emphasizedEasing } } // Network Tab NetworkTab { anchors.fill: parent anchors.margins: Theme.spacingM visible: controlCenterPopup.currentTab === 0 // Bind properties from root networkStatus: root.networkStatus wifiAvailable: root.wifiAvailable wifiEnabled: root.wifiEnabled ethernetIP: root.ethernetIP currentWifiSSID: root.currentWifiSSID wifiIP: root.wifiIP wifiSignalStrength: root.wifiSignalStrength wifiNetworks: root.wifiNetworks wifiConnectionStatus: root.wifiConnectionStatus wifiPasswordSSID: root.wifiPasswordSSID wifiPasswordInput: root.wifiPasswordInput wifiPasswordDialogVisible: root.wifiPasswordDialogVisible // Bind the auto-refresh flag onWifiAutoRefreshEnabledChanged: { root.wifiAutoRefreshEnabled = wifiAutoRefreshEnabled } } // Audio Tab AudioTab { anchors.fill: parent anchors.margins: Theme.spacingM visible: controlCenterPopup.currentTab === 1 // Bind properties from root volumeLevel: root.volumeLevel micLevel: root.micLevel currentAudioSink: root.currentAudioSink currentAudioSource: root.currentAudioSource audioSinks: root.audioSinks audioSources: root.audioSources } // Bluetooth Tab BluetoothTab { anchors.fill: parent anchors.margins: Theme.spacingM visible: controlCenterPopup.currentTab === 2 // Bind properties from root bluetoothEnabled: root.bluetoothEnabled bluetoothDevices: root.bluetoothDevices } // Display Tab DisplayTab { anchors.fill: parent anchors.margins: Theme.spacingM visible: controlCenterPopup.currentTab === 3 } } } } // Click outside to close MouseArea { anchors.fill: parent z: -1 onClicked: { root.controlCenterVisible = false } } }