diff options
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/bar/Bar.qml | 270 | ||||
| -rw-r--r-- | modules/bar/BarWrapper.qml | 9 | ||||
| -rw-r--r-- | modules/bar/components/ActiveWindow.qml | 97 | ||||
| -rw-r--r-- | modules/bar/components/Power.qml | 28 | ||||
| -rw-r--r-- | modules/bar/components/StatusIcons.qml | 73 | ||||
| -rw-r--r-- | modules/bar/components/workspaces/Workspaces.qml | 90 | ||||
| -rw-r--r-- | modules/drawers/Drawers.qml | 16 | ||||
| -rw-r--r-- | modules/drawers/Interactions.qml | 13 | ||||
| -rw-r--r-- | modules/drawers/Panels.qml | 2 |
9 files changed, 291 insertions, 307 deletions
diff --git a/modules/bar/Bar.qml b/modules/bar/Bar.qml index 22f21dd..cc191de 100644 --- a/modules/bar/Bar.qml +++ b/modules/bar/Bar.qml @@ -1,5 +1,5 @@ -import qs.components -import qs.components.controls +pragma ComponentBehavior: Bound + import qs.services import qs.config import "popouts" as BarPopouts @@ -7,170 +7,176 @@ import "components" import "components/workspaces" import Quickshell import QtQuick +import QtQuick.Layouts -Item { +ColumnLayout { id: root required property ShellScreen screen required property PersistentProperties visibilities required property BarPopouts.Wrapper popouts + readonly property int padding: Math.max(Appearance.padding.smaller, Config.border.thickness) + readonly property int vPadding: Appearance.padding.large function checkPopout(y: real): void { - const spacing = Appearance.spacing.small; - const aw = activeWindow.child; - const awy = activeWindow.y + aw.y; - - const ty = tray.y; - const th = tray.implicitHeight; - const trayItems = tray.items; - - // Check status icons hover areas - let statusIconFound = false; - for (const area of statusIconsInner.hoverAreas) { - if (!area.enabled) - continue; + const ch = childAt(width / 2, y) as WrappedLoader; + if (!ch) { + popouts.hasCurrent = false; + return; + } - const item = area.item; - const itemY = statusIcons.y + statusIconsInner.y + item.y - spacing / 2; - const itemHeight = item.implicitHeight + spacing; + const id = ch.id; + const top = ch.y; + const item = ch.item; + const itemHeight = item.implicitHeight; - if (y >= itemY && y <= itemY + itemHeight) { - popouts.currentName = area.name; - popouts.currentCenter = Qt.binding(() => statusIcons.y + statusIconsInner.y + item.y + item.implicitHeight / 2); + if (id === "statusIcons") { + const items = item.items; + const icon = items.childAt(items.width / 2, mapToItem(items, 0, y).y); + if (icon) { + popouts.currentName = icon.name; + popouts.currentCenter = Qt.binding(() => icon.mapToItem(root, 0, icon.implicitHeight / 2).y); popouts.hasCurrent = true; - statusIconFound = true; - break; } - } - - if (y >= awy && y <= awy + aw.implicitHeight) { - popouts.currentName = "activewindow"; - popouts.currentCenter = Qt.binding(() => activeWindow.y + aw.y + aw.implicitHeight / 2); - popouts.hasCurrent = true; - } else if (y > ty && y < ty + th) { - const index = Math.floor(((y - ty) / th) * trayItems.count); - const item = trayItems.itemAt(index); - - popouts.currentName = `traymenu${index}`; - popouts.currentCenter = Qt.binding(() => tray.y + item.y + item.implicitHeight / 2); + } else if (id === "tray") { + const index = Math.floor(((y - top) / itemHeight) * item.items.count); + const trayItem = item.items.itemAt(index); + if (trayItem) { + popouts.currentName = `traymenu${index}`; + popouts.currentCenter = Qt.binding(() => trayItem.mapToItem(root, 0, trayItem.implicitHeight / 2).y); + popouts.hasCurrent = true; + } + } else if (id === "activeWindow") { + popouts.currentName = id.toLowerCase(); + popouts.currentCenter = item.mapToItem(root, 0, itemHeight / 2).y; popouts.hasCurrent = true; - } else if (!statusIconFound) { - popouts.hasCurrent = false; } } - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.left: parent.left - - implicitWidth: child.implicitWidth + Math.max(Appearance.padding.smaller, Config.border.thickness) * 2 - - Item { - id: child - - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - - implicitWidth: Math.max(osIcon.implicitWidth, workspaces.implicitWidth, activeWindow.implicitWidth, tray.implicitWidth, clock.implicitWidth, statusIcons.implicitWidth, power.implicitWidth) - - OsIcon { - id: osIcon - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: parent.top - anchors.topMargin: Appearance.padding.large + function handleWheel(y: real, angleDelta: point): void { + const ch = childAt(width / 2, y) as WrappedLoader; + if (ch?.id === "workspaces") { + // Workspace scroll + const activeWs = Hyprland.activeToplevel?.workspace?.name; + if (activeWs?.startsWith("special:")) + Hyprland.dispatch(`togglespecialworkspace ${activeWs.slice(8)}`); + else if (angleDelta.y < 0 || Hyprland.activeWsId > 1) + Hyprland.dispatch(`workspace r${angleDelta.y > 0 ? "-" : "+"}1`); + } else if (y < screen.height / 2) { + // Volume scroll on top half + if (angleDelta.y > 0) + Audio.incrementVolume(); + else if (angleDelta.y < 0) + Audio.decrementVolume(); + } else { + // Brightness scroll on bottom half + const monitor = Brightness.getMonitorForScreen(screen); + if (angleDelta.y > 0) + monitor.setBrightness(monitor.brightness + 0.1); + else if (angleDelta.y < 0) + monitor.setBrightness(monitor.brightness - 0.1); } + } - StyledRect { - id: workspaces - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: osIcon.bottom - anchors.topMargin: Appearance.spacing.normal + spacing: Appearance.spacing.normal - radius: Appearance.rounding.full - color: Colours.tPalette.m3surfaceContainer + Repeater { + id: repeater - implicitWidth: workspacesInner.implicitWidth + Appearance.padding.small * 2 - implicitHeight: workspacesInner.implicitHeight + Appearance.padding.small * 2 + model: Config.bar.entries - CustomMouseArea { - anchors.fill: parent - anchors.leftMargin: -Math.max(Appearance.padding.smaller, Config.border.thickness) - anchors.rightMargin: -Math.max(Appearance.padding.smaller, Config.border.thickness) + DelegateChooser { + role: "id" - function onWheel(event: WheelEvent): void { - const activeWs = Hyprland.activeToplevel?.workspace?.name; - if (activeWs?.startsWith("special:")) - Hyprland.dispatch(`togglespecialworkspace ${activeWs.slice(8)}`); - else if (event.angleDelta.y < 0 || Hyprland.activeWsId > 1) - Hyprland.dispatch(`workspace r${event.angleDelta.y > 0 ? "-" : "+"}1`); + DelegateChoice { + roleValue: "spacer" + delegate: WrappedLoader { + Layout.fillHeight: enabled } } - - Workspaces { - id: workspacesInner - - anchors.centerIn: parent + DelegateChoice { + roleValue: "logo" + delegate: WrappedLoader { + sourceComponent: OsIcon {} + } + } + DelegateChoice { + roleValue: "workspaces" + delegate: WrappedLoader { + sourceComponent: Workspaces {} + } + } + DelegateChoice { + roleValue: "activeWindow" + delegate: WrappedLoader { + sourceComponent: ActiveWindow { + bar: root + monitor: Brightness.getMonitorForScreen(root.screen) + } + } + } + DelegateChoice { + roleValue: "tray" + delegate: WrappedLoader { + sourceComponent: Tray {} + } + } + DelegateChoice { + roleValue: "clock" + delegate: WrappedLoader { + sourceComponent: Clock {} + } + } + DelegateChoice { + roleValue: "statusIcons" + delegate: WrappedLoader { + sourceComponent: StatusIcons {} + } + } + DelegateChoice { + roleValue: "power" + delegate: WrappedLoader { + sourceComponent: Power { + visibilities: root.visibilities + } + } } } + } - ActiveWindow { - id: activeWindow - - anchors.horizontalCenter: parent.horizontalCenter - anchors.top: workspaces.bottom - anchors.bottom: tray.top - anchors.margins: Appearance.spacing.large - - monitor: Brightness.getMonitorForScreen(root.screen) - } - - Tray { - id: tray - - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: clock.top - anchors.bottomMargin: Appearance.spacing.larger - } - - Clock { - id: clock + component WrappedLoader: Loader { + required property bool enabled + required property string id + required property int index - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: statusIcons.top - anchors.bottomMargin: Appearance.spacing.normal + function findFirstEnabled(): Item { + const count = repeater.count; + for (let i = 0; i < count; i++) { + const item = repeater.itemAt(i); + if (item?.enabled) + return item; + } + return null; } - StyledRect { - id: statusIcons - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: power.top - anchors.bottomMargin: Appearance.spacing.normal - - radius: Appearance.rounding.full - color: Colours.tPalette.m3surfaceContainer - - implicitHeight: statusIconsInner.implicitHeight + Appearance.padding.normal * 2 - - StatusIcons { - id: statusIconsInner - - anchors.centerIn: parent + function findLastEnabled(): Item { + for (let i = repeater.count - 1; i >= 0; i--) { + const item = repeater.itemAt(i); + if (item?.enabled) + return item; } + return null; } - Power { - id: power + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: root.padding + Layout.rightMargin: root.padding - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: Appearance.padding.large + // Cursed ahh thing to add padding to first and last enabled components + Layout.topMargin: findFirstEnabled() === this ? root.vPadding : 0 + Layout.bottomMargin: findLastEnabled() === this ? root.vPadding : 0 - visibilities: root.visibilities - } + visible: enabled + active: enabled } } diff --git a/modules/bar/BarWrapper.qml b/modules/bar/BarWrapper.qml index 7752446..88c3892 100644 --- a/modules/bar/BarWrapper.qml +++ b/modules/bar/BarWrapper.qml @@ -14,19 +14,24 @@ Item { required property BarPopouts.Wrapper popouts readonly property int exclusiveZone: Config.bar.persistent || visibilities.bar ? content.implicitWidth : Config.border.thickness + readonly property bool shouldBeVisible: Config.bar.persistent || visibilities.bar || isHovered property bool isHovered function checkPopout(y: real): void { content.item?.checkPopout(y); } + function handleWheel(y: real, angleDelta: point): void { + content.item?.handleWheel(y, angleDelta); + } + visible: width > Config.border.thickness implicitWidth: Config.border.thickness implicitHeight: content.implicitHeight states: State { name: "visible" - when: Config.bar.persistent || root.visibilities.bar || root.isHovered + when: root.shouldBeVisible PropertyChanges { root.implicitWidth: content.implicitWidth @@ -63,7 +68,7 @@ Item { Loader { id: content - Component.onCompleted: active = Qt.binding(() => Config.bar.persistent || root.visibilities.bar || root.isHovered || root.visible) + Component.onCompleted: active = Qt.binding(() => root.shouldBeVisible || root.visible) anchors.top: parent.top anchors.bottom: parent.bottom diff --git a/modules/bar/components/ActiveWindow.qml b/modules/bar/components/ActiveWindow.qml index de0f0ad..3bea956 100644 --- a/modules/bar/components/ActiveWindow.qml +++ b/modules/bar/components/ActiveWindow.qml @@ -9,73 +9,62 @@ import QtQuick Item { id: root + required property var bar required property Brightness.Monitor monitor property color colour: Colours.palette.m3primary - readonly property Item child: child - implicitWidth: child.implicitWidth - implicitHeight: child.implicitHeight - - Item { - id: child - - property Item current: text1 - - anchors.centerIn: parent + readonly property int maxHeight: { + const otherModules = bar.children.filter(c => c.id && c.item !== this && c.id !== "spacer"); + const otherHeight = otherModules.reduce((acc, curr) => acc + curr.height, 0); + // Length - 2 cause repeater counts as a child + return bar.height - otherHeight - bar.spacing * (bar.children.length - 1) - bar.vPadding * 2; + } + property Title current: text1 - clip: true - implicitWidth: Math.max(icon.implicitWidth, current.implicitHeight) - implicitHeight: icon.implicitHeight + current.implicitWidth + current.anchors.topMargin + clip: true + implicitWidth: Math.max(icon.implicitWidth, current.implicitHeight) + implicitHeight: icon.implicitHeight + current.implicitWidth + current.anchors.topMargin - MaterialIcon { - id: icon + MaterialIcon { + id: icon - animate: true - text: Icons.getAppCategoryIcon(Hyprland.activeToplevel?.lastIpcObject.class, "desktop_windows") - color: root.colour + anchors.horizontalCenter: parent.horizontalCenter - anchors.horizontalCenter: parent.horizontalCenter - } + animate: true + text: Icons.getAppCategoryIcon(Hyprland.activeToplevel?.lastIpcObject.class, "desktop_windows") + color: root.colour + } - Title { - id: text1 - } + Title { + id: text1 + } - Title { - id: text2 - } + Title { + id: text2 + } - TextMetrics { - id: metrics + TextMetrics { + id: metrics - text: Hyprland.activeToplevel?.title ?? qsTr("Desktop") - font.pointSize: Appearance.font.size.smaller - font.family: Appearance.font.family.mono - elide: Qt.ElideRight - elideWidth: root.height - icon.height + text: Hyprland.activeToplevel?.title ?? qsTr("Desktop") + font.pointSize: Appearance.font.size.smaller + font.family: Appearance.font.family.mono + elide: Qt.ElideRight + elideWidth: root.maxHeight - icon.height - onTextChanged: { - const next = child.current === text1 ? text2 : text1; - next.text = elidedText; - child.current = next; - } - onElideWidthChanged: child.current.text = elidedText - } - - Behavior on implicitWidth { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.emphasized - } + onTextChanged: { + const next = root.current === text1 ? text2 : text1; + next.text = elidedText; + root.current = next; } + onElideWidthChanged: root.current.text = elidedText + } - Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.emphasized - } + Behavior on implicitHeight { + NumberAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.emphasized } } @@ -89,7 +78,7 @@ Item { font.pointSize: metrics.font.pointSize font.family: metrics.font.family color: root.colour - opacity: child.current === this ? 1 : 0 + opacity: root.current === this ? 1 : 0 transform: Rotation { angle: 90 diff --git a/modules/bar/components/Power.qml b/modules/bar/components/Power.qml index 71dc296..917bdf7 100644 --- a/modules/bar/components/Power.qml +++ b/modules/bar/components/Power.qml @@ -2,24 +2,22 @@ import qs.components import qs.services import qs.config import Quickshell +import QtQuick -MaterialIcon { +Item { id: root required property PersistentProperties visibilities - text: "power_settings_new" - color: Colours.palette.m3error - font.bold: true - font.pointSize: Appearance.font.size.normal + implicitWidth: icon.implicitHeight + Appearance.padding.small * 2 + implicitHeight: icon.implicitHeight StateLayer { + // Cursed workaround to make the height larger than the parent anchors.fill: undefined anchors.centerIn: parent - anchors.horizontalCenterOffset: 1 - - implicitWidth: parent.implicitHeight + Appearance.padding.small * 2 - implicitHeight: implicitWidth + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.small * 2 radius: Appearance.rounding.full @@ -27,4 +25,16 @@ MaterialIcon { root.visibilities.session = !root.visibilities.session; } } + + MaterialIcon { + id: icon + + anchors.centerIn: parent + anchors.horizontalCenterOffset: -1 + + text: "power_settings_new" + color: Colours.palette.m3error + font.bold: true + font.pointSize: Appearance.font.size.normal + } } diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml index c364d6b..f7d9828 100644 --- a/modules/bar/components/StatusIcons.qml +++ b/modules/bar/components/StatusIcons.qml @@ -10,51 +10,29 @@ import Quickshell.Services.UPower import QtQuick import QtQuick.Layouts -Item { +StyledRect { id: root property color colour: Colours.palette.m3secondary + readonly property alias items: iconColumn - readonly property list<var> hoverAreas: [ - { - name: "audio", - item: audioIcon, - enabled: Config.bar.status.showAudio - }, - { - name: "network", - item: networkIcon, - enabled: Config.bar.status.showNetwork - }, - { - name: "bluetooth", - item: bluetoothGroup, - enabled: Config.bar.status.showBluetooth - }, - { - name: "battery", - item: batteryIcon, - enabled: Config.bar.status.showBattery - } - ] + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.full clip: true - implicitWidth: iconColumn.implicitWidth - implicitHeight: iconColumn.implicitHeight + implicitWidth: iconColumn.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: iconColumn.implicitHeight + Appearance.padding.normal * 2 ColumnLayout { id: iconColumn - anchors.horizontalCenter: parent.horizontalCenter + anchors.centerIn: parent spacing: Appearance.spacing.smaller / 2 // Audio icon - Loader { - id: audioIcon - - asynchronous: true + WrappedLoader { + name: "audio" active: Config.bar.status.showAudio - visible: active sourceComponent: MaterialIcon { animate: true @@ -65,8 +43,6 @@ Item { // Keyboard layout icon Loader { - id: kbLayout - Layout.alignment: Qt.AlignHCenter asynchronous: true active: Config.bar.status.showKbLayout @@ -81,12 +57,9 @@ Item { } // Network icon - Loader { - id: networkIcon - - asynchronous: true + WrappedLoader { + name: "network" active: Config.bar.status.showNetwork - visible: active sourceComponent: MaterialIcon { animate: true @@ -95,13 +68,10 @@ Item { } } - // Bluetooth section (grouped for hover area) - Loader { - id: bluetoothGroup - - asynchronous: true + // Bluetooth section + WrappedLoader { + name: "bluetooth" active: Config.bar.status.showBluetooth - visible: active sourceComponent: ColumnLayout { spacing: Appearance.spacing.smaller / 2 @@ -157,12 +127,9 @@ Item { } // Battery icon - Loader { - id: batteryIcon - - asynchronous: true + WrappedLoader { + name: "battery" active: Config.bar.status.showBattery - visible: active sourceComponent: MaterialIcon { animate: true @@ -194,6 +161,14 @@ Item { Anim {} } + component WrappedLoader: Loader { + required property string name + + Layout.alignment: Qt.AlignHCenter + asynchronous: true + visible: active + } + component Anim: NumberAnimation { duration: Appearance.anim.durations.large easing.type: Easing.BezierSpline diff --git a/modules/bar/components/workspaces/Workspaces.qml b/modules/bar/components/workspaces/Workspaces.qml index 0a806f7..fb6201d 100644 --- a/modules/bar/components/workspaces/Workspaces.qml +++ b/modules/bar/components/workspaces/Workspaces.qml @@ -2,10 +2,11 @@ pragma ComponentBehavior: Bound import qs.services import qs.config +import qs.components import QtQuick import QtQuick.Layouts -Item { +StyledRect { id: root readonly property list<Workspace> workspaces: layout.children.filter(c => c.isWorkspace).sort((w1, w2) => w1.ws - w2.ws) @@ -15,60 +16,69 @@ Item { }, {}) readonly property int groupOffset: Math.floor((Hyprland.activeWsId - 1) / Config.bar.workspaces.shown) * Config.bar.workspaces.shown - implicitWidth: layout.implicitWidth - implicitHeight: layout.implicitHeight + implicitWidth: layout.implicitWidth + Appearance.padding.small * 2 + implicitHeight: layout.implicitHeight + Appearance.padding.small * 2 + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.full - ColumnLayout { - id: layout + Item { + id: inner - spacing: 0 - layer.enabled: true - layer.smooth: true + anchors.fill: parent + anchors.margins: Appearance.padding.small - Repeater { - model: Config.bar.workspaces.shown + ColumnLayout { + id: layout - Workspace { - occupied: root.occupied - groupOffset: root.groupOffset + spacing: 0 + layer.enabled: true + layer.smooth: true + + Repeater { + model: Config.bar.workspaces.shown + + Workspace { + occupied: root.occupied + groupOffset: root.groupOffset + } } } - } - Loader { - active: Config.bar.workspaces.occupiedBg - asynchronous: true + Loader { + active: Config.bar.workspaces.occupiedBg + asynchronous: true - z: -1 - anchors.fill: parent + z: -1 + anchors.fill: parent - sourceComponent: OccupiedBg { - workspaces: root.workspaces - occupied: root.occupied - groupOffset: root.groupOffset + sourceComponent: OccupiedBg { + workspaces: root.workspaces + occupied: root.occupied + groupOffset: root.groupOffset + } } - } - Loader { - active: Config.bar.workspaces.activeIndicator - asynchronous: true + Loader { + active: Config.bar.workspaces.activeIndicator + asynchronous: true - sourceComponent: ActiveIndicator { - workspaces: root.workspaces - mask: layout - maskWidth: root.width - maskHeight: root.height - groupOffset: root.groupOffset + sourceComponent: ActiveIndicator { + workspaces: root.workspaces + mask: layout + maskWidth: inner.width + maskHeight: inner.height + groupOffset: root.groupOffset + } } - } - MouseArea { - anchors.fill: parent + MouseArea { + anchors.fill: parent - onPressed: event => { - const ws = layout.childAt(event.x, event.y).index + root.groupOffset + 1; - if (Hyprland.activeWsId !== ws) - Hyprland.dispatch(`workspace ${ws}`); + onPressed: event => { + const ws = layout.childAt(event.x, event.y).index + root.groupOffset + 1; + if (Hyprland.activeWsId !== ws) + Hyprland.dispatch(`workspace ${ws}`); + } } } } diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml index 26f177c..06183dd 100644 --- a/modules/drawers/Drawers.qml +++ b/modules/drawers/Drawers.qml @@ -133,17 +133,17 @@ Variants { visibilities: visibilities bar: bar } - } - BarWrapper { - id: bar + BarWrapper { + id: bar - anchors.top: parent.top - anchors.bottom: parent.bottom + anchors.top: parent.top + anchors.bottom: parent.bottom - screen: scope.modelData - visibilities: visibilities - popouts: panels.popouts + screen: scope.modelData + visibilities: visibilities + popouts: panels.popouts + } } } } diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 6b276a0..94d27cc 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -45,18 +45,7 @@ CustomMouseArea { function onWheel(event: WheelEvent): void { if (event.x < bar.implicitWidth) { - if (event.y < screen.height / 2) { - if (event.angleDelta.y > 0) - Audio.incrementVolume(); - else if (event.angleDelta.y < 0) - Audio.decrementVolume(); - } else { - const monitor = Brightness.getMonitorForScreen(screen); - if (event.angleDelta.y > 0) - monitor.setBrightness(monitor.brightness + 0.1); - else if (event.angleDelta.y < 0) - monitor.setBrightness(monitor.brightness - 0.1); - } + bar.handleWheel(event.y, event.angleDelta); } } diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml index ae3a9f2..d27dca0 100644 --- a/modules/drawers/Panels.qml +++ b/modules/drawers/Panels.qml @@ -92,7 +92,7 @@ Item { const diff = root.height - Math.floor(off + nonAnimHeight); if (diff < 0) return off + diff; - return off; + return Math.max(off, 0); } } |