diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 4948ddb..bbb3a96 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -133,13 +133,19 @@ Variants { const localCenterX = width / 2 const localCenterY = height / 2 const globalPoint = mapToItem(null, localCenterX, localCenterY) - if (sidePanel.isLoaded) - sidePanel.isLoaded = false - else if (sidePanel.openAt) + if (sidePanel.isLoaded) { + // Call hide() instead of directly setting isLoaded to false + if (sidePanel.item && sidePanel.item.hide) { + sidePanel.item.hide() + } else { + sidePanel.isLoaded = false + } + } else if (sidePanel.openAt) { sidePanel.openAt(globalPoint.x, screen) - else + } else { // Fallback: toggle if API unavailable sidePanel.isLoaded = true + } } } } diff --git a/Modules/Bar/NotificationHistory.qml b/Modules/Bar/NotificationHistory.qml index 2d0b638..ba3fda5 100644 --- a/Modules/Bar/NotificationHistory.qml +++ b/Modules/Bar/NotificationHistory.qml @@ -19,7 +19,17 @@ NIconButton { notificationHistoryPanelLoader.isLoaded = true } if (notificationHistoryPanelLoader.item) { - notificationHistoryPanelLoader.item.visible = !notificationHistoryPanelLoader.item.visible + if (notificationHistoryPanelLoader.item.visible) { + // Panel is visible, hide it with animation + if (notificationHistoryPanelLoader.item.hide) { + notificationHistoryPanelLoader.item.hide() + } else { + notificationHistoryPanelLoader.item.visible = false + } + } else { + // Panel is hidden, show it + notificationHistoryPanelLoader.item.visible = true + } } } diff --git a/Modules/Bar/NotificationHistoryPanel.qml b/Modules/Bar/NotificationHistoryPanel.qml index db1fd42..530cc98 100644 --- a/Modules/Bar/NotificationHistoryPanel.qml +++ b/Modules/Bar/NotificationHistoryPanel.qml @@ -15,17 +15,56 @@ NLoader { NPanel { id: notificationPanel + // Override hide function to animate first + function hide() { + // Start hide animation + notificationRect.scaleValue = 0.8 + notificationRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + Connections { target: notificationPanel ignoreUnknownSignals: true function onDismissed() { + // Start hide animation + notificationRect.scaleValue = 0.8 + notificationRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && notificationRect.opacityValue > 0) { + // Start hide animation + notificationRect.scaleValue = 0.8 + notificationRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { notificationPanel.visible = false + notificationPanel.dismissed() } } WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand Rectangle { + id: notificationRect color: Colors.backgroundSecondary radius: Style.radiusMedium * scaling border.color: Colors.backgroundTertiary @@ -37,6 +76,36 @@ NLoader { anchors.topMargin: Style.marginTiny * scaling anchors.rightMargin: Style.marginTiny * scaling + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + ColumnLayout { anchors.fill: parent anchors.margins: Style.marginLarge * scaling @@ -72,7 +141,7 @@ NLoader { icon: "close" sizeMultiplier: 0.8 onClicked: { - notificationPanel.visible = false + notificationPanel.hide() } } } diff --git a/Modules/Bar/Tray.qml b/Modules/Bar/Tray.qml index f4cc90b..f74c0bd 100644 --- a/Modules/Bar/Tray.qml +++ b/Modules/Bar/Tray.qml @@ -122,16 +122,91 @@ Item { NPanel { id: trayPanel showOverlay: false // no colors overlay even if activated in settings + + // Override hide function to animate first + function hide() { + // Start hide animation + trayMenuRect.scaleValue = 0.8 + trayMenuRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + Connections { target: trayPanel ignoreUnknownSignals: true - function onDismissed() { + function onDismissed() { + // Start hide animation + trayMenuRect.scaleValue = 0.8 + trayMenuRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && trayMenuRect.opacityValue > 0) { + // Start hide animation + trayMenuRect.scaleValue = 0.8 + trayMenuRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { trayPanel.visible = false trayMenu.hideMenu() } } - TrayMenu { - id: trayMenu + + Rectangle { + id: trayMenuRect + color: "transparent" + anchors.fill: parent + + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + + TrayMenu { + id: trayMenu + } } } } diff --git a/Modules/Bar/WiFi.qml b/Modules/Bar/WiFi.qml index f494a76..4148a4a 100644 --- a/Modules/Bar/WiFi.qml +++ b/Modules/Bar/WiFi.qml @@ -29,11 +29,18 @@ NIconButton { wifiMenuLoader.isLoaded = true } if (wifiMenuLoader.item) { - wifiMenuLoader.item.visible = !wifiMenuLoader.item.visible if (wifiMenuLoader.item.visible) { - network.onMenuOpened() + // Panel is visible, hide it with animation + if (wifiMenuLoader.item.hide) { + wifiMenuLoader.item.hide() + } else { + wifiMenuLoader.item.visible = false + network.onMenuClosed() + } } else { - network.onMenuClosed() + // Panel is hidden, show it + wifiMenuLoader.item.visible = true + network.onMenuOpened() } } } diff --git a/Modules/Bar/WiFiMenu.qml b/Modules/Bar/WiFiMenu.qml index 6431007..21d2af5 100644 --- a/Modules/Bar/WiFiMenu.qml +++ b/Modules/Bar/WiFiMenu.qml @@ -18,11 +18,47 @@ NLoader { property string passwordInput: "" property bool showPasswordPrompt: false + function hide() { + wifiMenuRect.scaleValue = 0.8 + wifiMenuRect.opacityValue = 0.0 + + hideTimer.start() + } + + // Connect to NPanel's dismissed signal to handle external close events Connections { target: wifiPanel ignoreUnknownSignals: true function onDismissed() { + // Start hide animation + wifiMenuRect.scaleValue = 0.8 + wifiMenuRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && wifiMenuRect.opacityValue > 0) { + // Start hide animation + wifiMenuRect.scaleValue = 0.8 + wifiMenuRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { wifiPanel.visible = false + wifiPanel.dismissed() network.onMenuClosed() } } @@ -30,6 +66,7 @@ NLoader { WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand Rectangle { + id: wifiMenuRect color: Colors.backgroundSecondary radius: Style.radiusMedium * scaling border.color: Colors.backgroundTertiary @@ -41,6 +78,34 @@ NLoader { anchors.topMargin: Style.marginTiny * scaling anchors.rightMargin: Style.marginTiny * scaling + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + } + } + ColumnLayout { anchors.fill: parent anchors.margins: Style.marginLarge * scaling @@ -87,8 +152,7 @@ NLoader { icon: "close" sizeMultiplier: 0.8 onClicked: { - wifiPanel.visible = false - network.onMenuClosed() + wifiPanel.hide() } } } diff --git a/Modules/Calendar/Calendar.qml b/Modules/Calendar/Calendar.qml index fba498f..532ea02 100644 --- a/Modules/Calendar/Calendar.qml +++ b/Modules/Calendar/Calendar.qml @@ -15,7 +15,54 @@ NLoader { readonly property real scaling: Scaling.scale(screen) + // Override hide function to animate first + function hide() { + // Start hide animation + calendarRect.scaleValue = 0.8 + calendarRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + + // Connect to NPanel's dismissed signal to handle external close events + Connections { + target: calendarPanel + function onDismissed() { + // Start hide animation + calendarRect.scaleValue = 0.8 + calendarRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && calendarRect.opacityValue > 0) { + // Start hide animation + calendarRect.scaleValue = 0.8 + calendarRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { + calendarPanel.visible = false + calendarPanel.dismissed() + } + } + Rectangle { + id: calendarRect color: Colors.backgroundSecondary radius: Style.radiusMedium * scaling border.color: Colors.backgroundTertiary @@ -27,11 +74,41 @@ NLoader { anchors.topMargin: Style.marginTiny * scaling anchors.rightMargin: Style.marginTiny * scaling + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + // Prevent closing when clicking in the panel bg MouseArea { anchors.fill: parent } + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + // Main Column ColumnLayout { anchors.fill: parent diff --git a/Modules/Demo/DemoPanel.qml b/Modules/Demo/DemoPanel.qml index 98ba412..744178f 100644 --- a/Modules/Demo/DemoPanel.qml +++ b/Modules/Demo/DemoPanel.qml @@ -15,9 +15,54 @@ NLoader { readonly property real scaling: Scaling.scale(screen) + // Override hide function to animate first + function hide() { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + + // Connect to NPanel's dismissed signal to handle external close events + Connections { + target: demoPanel + function onDismissed() { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && bgRect.opacityValue > 0) { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { + demoPanel.visible = false + demoPanel.dismissed() + } + } + // Ensure panel shows itself once created Component.onCompleted: { - console.log("[DemoPanel] Component completed, showing panel") show() } @@ -31,11 +76,41 @@ NLoader { height: 700 * scaling anchors.centerIn: parent + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + // Prevent closing when clicking in the panel bg MouseArea { anchors.fill: parent } + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + ColumnLayout { anchors.fill: parent anchors.margins: Style.marginXL * scaling diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index 189e969..831d802 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -16,6 +16,40 @@ NLoader { readonly property real scaling: Scaling.scale(screen) + // Override hide function to animate first + function hide() { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + + // Connect to NPanel's dismissed signal to handle external close events + Connections { + target: panel + function onDismissed() { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { + panel.visible = false + panel.dismissed() + } + } + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand property int currentTabIndex: 0 @@ -65,9 +99,18 @@ NLoader { "source": "Tabs/About.qml" }] + // Combined visibility change handler onVisibleChanged: { - if (visible) + if (visible) { currentTabIndex = 0 + } else if (bgRect.opacityValue > 0) { + // Start hide animation + bgRect.scaleValue = 0.8 + bgRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } } Component.onCompleted: show() @@ -83,10 +126,40 @@ NLoader { height: (screen.height * 0.5) * scaling anchors.centerIn: parent + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + MouseArea { anchors.fill: parent } + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + RowLayout { anchors.fill: parent anchors.margins: Style.marginLarge * scaling @@ -196,7 +269,7 @@ NLoader { tooltipText: "Close" Layout.alignment: Qt.AlignVCenter onClicked: { - settingsPanel.isLoaded = !settingsPanel.isLoaded + panel.hide() } } } diff --git a/Modules/SidePanel/SidePanel.qml b/Modules/SidePanel/SidePanel.qml index 5b0a5c6..c389c23 100644 --- a/Modules/SidePanel/SidePanel.qml +++ b/Modules/SidePanel/SidePanel.qml @@ -41,6 +41,41 @@ NLoader { // Ensure this panel attaches to the intended screen screen: root.targetScreen + // Override hide function to animate first + function hide() { + // Start hide animation + panelBackground.scaleValue = 0.8 + panelBackground.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + + // Connect to NPanel's dismissed signal to handle external close events + Connections { + target: sidePanel + function onDismissed() { + // Start hide animation + panelBackground.scaleValue = 0.8 + panelBackground.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + + // Also handle visibility changes from external sources + onVisibleChanged: { + if (!visible && panelBackground.opacityValue > 0) { + // Start hide animation + panelBackground.scaleValue = 0.8 + panelBackground.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() + } + } + // Ensure panel shows itself once created Component.onCompleted: show() @@ -62,11 +97,54 @@ NLoader { x: Math.max(Style.marginSmall * scaling, Math.min(parent.width - width - Style.marginSmall * scaling, Math.round(anchorX - width / 2))) + // Animation properties + property real scaleValue: 0.8 + property real opacityValue: 0.0 + + scale: scaleValue + opacity: opacityValue + + // Animate in when component is completed + Component.onCompleted: { + scaleValue = 1.0 + opacityValue = 1.0 + } + + + + // Timer to hide panel after animation + Timer { + id: hideTimer + interval: Style.animationSlow + repeat: false + onTriggered: { + sidePanel.visible = false + sidePanel.dismissed() + } + } + // Prevent closing when clicking in the panel bg MouseArea { anchors.fill: parent } + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationSlow + easing.type: Easing.OutExpo + + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + + } + } + // Content wrapper to ensure childrenRect drives implicit height Item { id: content diff --git a/Widgets/NTooltip.qml b/Widgets/NTooltip.qml index 361b2ce..401dfde 100644 --- a/Widgets/NTooltip.qml +++ b/Widgets/NTooltip.qml @@ -55,11 +55,23 @@ Window { x = pos.x - width / 2 + target.width / 2 y = pos.y + 12 // 12 px margin below } + + // Start with animation values + tooltipRect.scaleValue = 0.8 + tooltipRect.opacityValue = 0.0 visible = true + + // Use a timer to trigger the animation after the component is visible + showTimer.start() } function _hideNow() { - visible = false + // Start hide animation + tooltipRect.scaleValue = 0.8 + tooltipRect.opacityValue = 0.0 + + // Hide after animation completes + hideTimer.start() } Connections { @@ -97,7 +109,30 @@ Window { } } + // Timer to hide tooltip after animation + Timer { + id: hideTimer + interval: Style.animationNormal + repeat: false + onTriggered: { + visible = false + } + } + + // Timer to trigger show animation + Timer { + id: showTimer + interval: 10 // Very short delay to ensure component is visible + repeat: false + onTriggered: { + // Animate to final values + tooltipRect.scaleValue = 1.0 + tooltipRect.opacityValue = 1.0 + } + } + Rectangle { + id: tooltipRect anchors.fill: parent radius: Style.radiusMedium * scaling gradient: Gradient { @@ -113,16 +148,37 @@ Window { border.color: Colors.outline border.width: Math.max(1, Style.borderThin * scaling) z: 1 - } - NText { - id: tooltipText - anchors.centerIn: parent - text: root.text - font.pointSize: Style.fontSizeMedium * scaling - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.Wrap - z: 1 + // Animation properties + property real scaleValue: 1.0 + property real opacityValue: 1.0 + + scale: scaleValue + opacity: opacityValue + + // Animation behaviors + Behavior on scale { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutExpo + } + } + + Behavior on opacity { + NumberAnimation { + duration: Style.animationNormal + easing.type: Easing.OutQuad + } + } + + NText { + id: tooltipText + anchors.centerIn: parent + text: root.text + font.pointSize: Style.fontSizeMedium * scaling + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + } } }