diff --git a/Modules/Bar/Widgets/ArchUpdater.qml b/Modules/Bar/Widgets/ArchUpdater.qml index 70124ea..576152c 100644 --- a/Modules/Bar/Widgets/ArchUpdater.qml +++ b/Modules/Bar/Widgets/ArchUpdater.qml @@ -66,7 +66,7 @@ NIconButton { if (ArchUpdaterService.updatePackages.length > 0) { // Show confirmation dialog for updates - PanelService.getPanel("archUpdaterPanel").toggle(screen) + PanelService.getPanel("archUpdaterPanel").toggle(screen, this) } else { // Just refresh if no updates available ArchUpdaterService.doPoll() diff --git a/Modules/Bar/Widgets/Bluetooth.qml b/Modules/Bar/Widgets/Bluetooth.qml index c507b22..38c9050 100644 --- a/Modules/Bar/Widgets/Bluetooth.qml +++ b/Modules/Bar/Widgets/Bluetooth.qml @@ -31,5 +31,5 @@ NIconButton { } } tooltipText: "Bluetooth Devices" - onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(screen) + onClicked: PanelService.getPanel("bluetoothPanel")?.toggle(screen, this) } diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index ced71d8..349e47a 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -38,7 +38,7 @@ Rectangle { } onClicked: { tooltip.hide() - PanelService.getPanel("calendarPanel")?.toggle(screen) + PanelService.getPanel("calendarPanel")?.toggle(screen, this) } } } diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml index c3b33bb..8a1e8c3 100644 --- a/Modules/Bar/Widgets/NotificationHistory.qml +++ b/Modules/Bar/Widgets/NotificationHistory.qml @@ -20,5 +20,5 @@ NIconButton { colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent - onClicked: PanelService.getPanel("notificationHistoryPanel")?.toggle(screen) + onClicked: PanelService.getPanel("notificationHistoryPanel")?.toggle(screen, this) } diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index f099710..f4ec9dd 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -32,8 +32,9 @@ NIconButton { icon: { try { - if (NetworkService.ethernet) + if (NetworkService.ethernet) { return "lan" + } let connected = false let signalStrength = 0 for (const net in NetworkService.networks) { @@ -50,12 +51,5 @@ NIconButton { } } tooltipText: "Network / WiFi" - onClicked: { - try { - Logger.log("WiFi", "Button clicked, toggling panel") - PanelService.getPanel("wifiPanel")?.toggle(screen) - } catch (error) { - Logger.error("WiFi", "Error toggling panel:", error) - } - } + onClicked: PanelService.getPanel("wifiPanel")?.toggle(screen, this) } diff --git a/Widgets/NPanel.qml b/Widgets/NPanel.qml index 4d14478..69ea4bd 100644 --- a/Widgets/NPanel.qml +++ b/Widgets/NPanel.qml @@ -10,8 +10,8 @@ Loader { active: false asynchronous: true - readonly property real scaling: ScalingService.scale(screen) property ShellScreen screen + readonly property real scaling: ScalingService.scale(screen) property Component panelContent: null property int panelWidth: 1500 @@ -20,6 +20,12 @@ Loader { property bool panelAnchorLeft: false property bool panelAnchorRight: false + // Properties to support positioning relative to the opener (button) + property bool useButtonPosition: false + property point buttonPosition: Qt.point(0, 0) + property int buttonWidth: 0 + property int buttonHeight: 0 + // Animation properties readonly property real originalScale: 0.7 readonly property real originalOpacity: 0.0 @@ -27,6 +33,8 @@ Loader { property real opacityValue: originalOpacity property alias isClosing: hideTimer.running + readonly property real barHeight: Style.barHeight * scaling + readonly property bool barAtBottom: Settings.data.bar.position === "bottom" signal opened signal closed @@ -36,20 +44,32 @@ Loader { } // ----------------------------------------- - function toggle(aScreen) { + function toggle(aScreen, buttonItem) { if (!active || isClosing) { - open(aScreen) + open(aScreen, buttonItem) } else { close() } } // ----------------------------------------- - function open(aScreen) { + function open(aScreen, buttonItem) { if (aScreen !== null) { screen = aScreen } + // Get t button position if provided + if (buttonItem !== undefined && buttonItem !== null) { + useButtonPosition = true + + var itemPos = buttonItem.mapToItem(null, 0, 0) + buttonPosition = Qt.point(itemPos.x, itemPos.y) + buttonWidth = buttonItem.width + buttonHeight = buttonItem.height + } else { + useButtonPosition = false + } + // Special case if currently closing/animating if (isClosing) { hideTimer.stop() // in case we were closing @@ -74,6 +94,7 @@ Loader { function closeCompleted() { root.closed() active = false + useButtonPosition = false // Reset button position usage } // ----------------------------------------- @@ -113,8 +134,8 @@ Loader { anchors.left: true anchors.right: true anchors.bottom: true - margins.top: Settings.data.bar.position === "top" ? Style.barHeight * scaling : 0 - margins.bottom: Settings.data.bar.position === "bottom" ? Style.barHeight * scaling : 0 + margins.top: !barAtBottom ? barHeight : 0 + margins.bottom: barAtBottom ? barHeight : 0 // Close any panel with Esc without requiring focus Shortcut { @@ -140,21 +161,41 @@ Loader { width: panelWidth height: panelHeight - anchors { - centerIn: panelAnchorCentered ? parent : null - left: !panelAnchorCentered && panelAnchorLeft ? parent.left : parent.center - right: !panelAnchorCentered && panelAnchorRight ? parent.right : parent.center - top: !panelAnchorCentered && (Settings.data.bar.position === "top") ? parent.top : undefined - bottom: !panelAnchorCentered && (Settings.data.bar.position === "bottom") ? parent.bottom : undefined + property int calculatedX: { + if (root.useButtonPosition) { + // Position panel relative to button + var targetX = root.buttonPosition.x + (root.buttonWidth / 2) - (panelWidth / 2) - // margins - topMargin: !panelAnchorCentered - && (Settings.data.bar.position === "top") ? Style.marginS * scaling : undefined - bottomMargin: !panelAnchorCentered - && (Settings.data.bar.position === "bottom") ? Style.marginS * scaling : undefined - rightMargin: !panelAnchorCentered && panelAnchorRight ? Style.marginS * scaling : undefined + // Keep panel within screen bounds + var maxX = panelWindow.width - panelWidth - (Style.marginS * scaling) + var minX = Style.marginS * scaling + + return Math.max(minX, Math.min(targetX, maxX)) + } else if (!panelAnchorCentered && panelAnchorLeft) { + return Style.marginS * scaling + } else if (!panelAnchorCentered && panelAnchorRight) { + return panelWindow.width - panelWidth - (Style.marginS * scaling) + } else { + return (panelWindow.width - panelWidth) / 2 + } } + property int calculatedY: { + // Position panel below or above the bar + if (!panelAnchorCentered) { + if (!barAtBottom) { + return Style.marginS * scaling + } else { + return panelWindow.height - panelHeight - (Style.marginS * scaling) + } + } else { + return (panelWindow.height - panelHeight) / 2 + } + } + + x: calculatedX + y: calculatedY + scale: root.scaleValue opacity: root.opacityValue