updated pi, quickshell, and ollama
This commit is contained in:
685
home/hypr/quickshell/components/SystemHUD.qml
Normal file
685
home/hypr/quickshell/components/SystemHUD.qml
Normal file
@@ -0,0 +1,685 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
import "../theme"
|
||||
import ".."
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
}
|
||||
|
||||
margins {
|
||||
left: Theme.sidebarWidth
|
||||
top: Theme.topStripHeight
|
||||
}
|
||||
|
||||
implicitWidth: Theme.hudWidth
|
||||
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "quickshell-systemhud"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
visible: GlobalState.systemHudVisible
|
||||
|
||||
color: "transparent"
|
||||
|
||||
property real slideOffset: GlobalState.systemHudVisible ? 0 : -Theme.hudWidth
|
||||
Behavior on slideOffset {
|
||||
NumberAnimation {
|
||||
duration: Theme.animSmooth
|
||||
easing.type: Easing.OutQuint
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Escape"
|
||||
onActivated: GlobalState.systemHudVisible = false
|
||||
}
|
||||
|
||||
component SectionHeader: Item {
|
||||
property string sectionNum: "00"
|
||||
property string title: "Section"
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 24
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
Text {
|
||||
text: sectionNum + " //"
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 12
|
||||
color: Theme.accentVolt
|
||||
}
|
||||
Text {
|
||||
text: title
|
||||
font.family: Theme.fontSerif
|
||||
font.italic: true
|
||||
font.pixelSize: 16
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ToggleCard: Rectangle {
|
||||
property string title: ""
|
||||
property string icon: ""
|
||||
property bool active: false
|
||||
signal clicked()
|
||||
|
||||
Layout.preferredWidth: 160
|
||||
Layout.preferredHeight: 56
|
||||
color: Theme.bgPrimary
|
||||
border.color: active ? Qt.rgba(Theme.accentVolt.r, Theme.accentVolt.g, Theme.accentVolt.b, 0.3) : Theme.glassBorder
|
||||
border.width: 1
|
||||
radius: 4
|
||||
|
||||
Behavior on border.color { ColorAnimation { duration: Theme.animFast } }
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: clicked()
|
||||
onEntered: if (!active) border.color = Theme.glassBorderHover
|
||||
onExited: if (!active) border.color = Theme.glassBorder
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 12
|
||||
spacing: 12
|
||||
Image {
|
||||
source: icon ? Qt.resolvedUrl("../" + icon) : ""
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
sourceSize: Qt.size(24, 24)
|
||||
visible: parent.icon !== ""
|
||||
}
|
||||
Text {
|
||||
text: title
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component CustomSlider: Item {
|
||||
property real value: 0
|
||||
signal valueChangedUser(real newVal)
|
||||
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: 24
|
||||
|
||||
Rectangle {
|
||||
id: track
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - 40
|
||||
height: 6
|
||||
color: Theme.bgSecondary
|
||||
radius: 3
|
||||
|
||||
Rectangle {
|
||||
id: fill
|
||||
width: track.width * (parent.parent.value / 100)
|
||||
height: track.height
|
||||
color: Theme.accentVolt
|
||||
radius: 3
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 14
|
||||
height: 14
|
||||
radius: 7
|
||||
color: Theme.textPrimary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: Math.max(0, fill.width - 7)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -10
|
||||
function updateValue(mouseX) {
|
||||
let p = Math.max(0, Math.min(1, mouseX / track.width))
|
||||
parent.parent.valueChangedUser(p * 100)
|
||||
}
|
||||
onPressed: (mouse) => updateValue(mouse.x)
|
||||
onPositionChanged: (mouse) => updateValue(mouse.x)
|
||||
}
|
||||
}
|
||||
Text {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: Math.round(parent.value) + "%"
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
component ProgressBarItem: ColumnLayout {
|
||||
property string label: ""
|
||||
property real percent: 0
|
||||
property string valueText: ""
|
||||
property color barColor: Theme.accentVolt
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: parent.parent.label
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 12
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Text {
|
||||
text: parent.parent.valueText
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 8
|
||||
color: Theme.bgSecondary
|
||||
radius: 4
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * (parent.parent.percent / 100)
|
||||
height: parent.height
|
||||
color: parent.parent.barColor
|
||||
radius: 4
|
||||
Behavior on width { NumberAnimation { duration: Theme.animFast; easing.type: Easing.OutCubic } }
|
||||
Behavior on color { ColorAnimation { duration: Theme.animFast } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: panelContent
|
||||
anchors.fill: parent
|
||||
color: Theme.glassBg
|
||||
opacity: GlobalState.systemHudVisible ? 1 : 0
|
||||
|
||||
transform: Translate { x: slideOffset }
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.animSmooth
|
||||
easing.type: Easing.OutQuint
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: 1
|
||||
color: Theme.glassBorder
|
||||
}
|
||||
|
||||
Item {
|
||||
id: header
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 60
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.glassBorder
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 20
|
||||
anchors.rightMargin: 20
|
||||
|
||||
Text {
|
||||
text: "System HUD"
|
||||
font.family: Theme.fontSerif
|
||||
font.italic: true
|
||||
font.pixelSize: 19
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "✕"
|
||||
color: Theme.textMuted
|
||||
font.pixelSize: 16
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -10
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.textPrimary
|
||||
onExited: parent.color = Theme.textMuted
|
||||
onClicked: GlobalState.systemHudVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.top: header.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
contentWidth: availableWidth
|
||||
clip: true
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
width: parent.width - 40
|
||||
x: 20
|
||||
spacing: 32
|
||||
|
||||
Item { Layout.preferredHeight: 16 } // Top padding
|
||||
|
||||
// Section 1: Toggles
|
||||
ColumnLayout {
|
||||
id: sectionToggles
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "01"; title: "Toggles" }
|
||||
GridLayout {
|
||||
columns: 2
|
||||
columnSpacing: 12
|
||||
rowSpacing: 12
|
||||
Layout.fillWidth: true
|
||||
|
||||
ToggleCard {
|
||||
title: GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "Wi-Fi"
|
||||
icon: "assets/wifi.svg"
|
||||
active: GlobalState.wifiConnected
|
||||
onClicked: {
|
||||
let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"])
|
||||
proc.start()
|
||||
}
|
||||
}
|
||||
|
||||
ToggleCard {
|
||||
title: "Bluetooth"
|
||||
icon: "assets/bluetooth.svg"
|
||||
active: false // Adjust if there's a bluetooth state
|
||||
onClicked: {
|
||||
let proc = Quickshell.createProcess("sh", ["-c", "foot -e bluetoothctl"])
|
||||
proc.start()
|
||||
}
|
||||
}
|
||||
|
||||
ToggleCard {
|
||||
property bool dndActive: false
|
||||
title: "Do Not Disturb"
|
||||
icon: "" // Optional DND icon
|
||||
active: dndActive
|
||||
onClicked: dndActive = !dndActive
|
||||
}
|
||||
|
||||
ToggleCard {
|
||||
title: GlobalState.volumeMuted ? "Muted" : "Sound"
|
||||
icon: "" // Optional sound icon
|
||||
active: !GlobalState.volumeMuted
|
||||
onClicked: GlobalState.toggleVolumeMute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section 2: Volume
|
||||
ColumnLayout {
|
||||
id: sectionVolume
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "02"; title: "Volume" }
|
||||
|
||||
CustomSlider {
|
||||
Layout.fillWidth: true
|
||||
value: GlobalState.volume
|
||||
onValueChangedUser: (val) => GlobalState.volume = val // Adjust to actual volume setter if needed
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: GlobalState.volumeMuted ? "Unmute" : "Mute"
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: GlobalState.toggleVolumeMute()
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
Text {
|
||||
text: "Sound Preferences..."
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.textPrimary
|
||||
onExited: parent.color = Theme.textMuted
|
||||
onClicked: {
|
||||
let proc = Quickshell.createProcess("pavucontrol", [])
|
||||
proc.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section 3: Brightness
|
||||
ColumnLayout {
|
||||
id: sectionBrightness
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "03"; title: "Brightness" }
|
||||
|
||||
CustomSlider {
|
||||
Layout.fillWidth: true
|
||||
value: GlobalState.brightness
|
||||
onValueChangedUser: (val) => GlobalState.setBrightness(val)
|
||||
}
|
||||
}
|
||||
|
||||
// Section 4: Performance
|
||||
ColumnLayout {
|
||||
id: sectionPerformance
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "04"; title: "Performance" }
|
||||
|
||||
ProgressBarItem {
|
||||
label: "CPU"
|
||||
percent: GlobalState.cpuPercent
|
||||
valueText: GlobalState.cpuPercent + "%"
|
||||
barColor: GlobalState.cpuPercent > 80 ? Theme.accentCoral : Theme.accentVolt
|
||||
}
|
||||
|
||||
ProgressBarItem {
|
||||
label: "RAM"
|
||||
percent: (GlobalState.memUsedGb / Math.max(1, GlobalState.memTotalGb)) * 100
|
||||
valueText: GlobalState.memUsedGb + " / " + GlobalState.memTotalGb + " GB"
|
||||
}
|
||||
|
||||
ProgressBarItem {
|
||||
label: "VRAM"
|
||||
percent: (GlobalState.gpuVramUsed / Math.max(1, GlobalState.gpuVramTotal)) * 100
|
||||
valueText: GlobalState.gpuVramUsed + " / " + GlobalState.gpuVramTotal + " GB"
|
||||
}
|
||||
}
|
||||
|
||||
// Section 5: Network
|
||||
ColumnLayout {
|
||||
id: sectionNetwork
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "05"; title: "Network" }
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Rectangle {
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
color: GlobalState.wifiConnected ? Theme.accentVolt : Theme.accentCoral
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.wifiConnected ? "Connected" : "Disconnected"
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 14
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "SSID: " + (GlobalState.wifiSsid !== "" ? GlobalState.wifiSsid : "None")
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "IP: " + GlobalState.ipAddress
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 13
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Network Preferences..."
|
||||
color: Theme.textMuted
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.textPrimary
|
||||
onExited: parent.color = Theme.textMuted
|
||||
onClicked: {
|
||||
let proc = Quickshell.createProcess("sh", ["-c", "nm-connection-editor || foot -e nmtui"])
|
||||
proc.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section 6: Battery
|
||||
ColumnLayout {
|
||||
id: sectionBattery
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
visible: GlobalState.batteryPresent
|
||||
SectionHeader { sectionNum: "06"; title: "Battery" }
|
||||
|
||||
RowLayout {
|
||||
spacing: 16
|
||||
Text {
|
||||
text: GlobalState.batteryPercent + "%"
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 24
|
||||
color: GlobalState.batteryStatus === "Charging" ? Theme.accentVolt :
|
||||
(GlobalState.batteryPercent < 20 ? Theme.accentCoral : Theme.textPrimary)
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.batteryStatus
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section 7: Now Playing
|
||||
ColumnLayout {
|
||||
id: sectionNowPlaying
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
visible: GlobalState.mediaTitle !== ""
|
||||
SectionHeader { sectionNum: "07"; title: "Now Playing" }
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Text {
|
||||
text: GlobalState.mediaTitle
|
||||
color: Theme.textPrimary
|
||||
font.family: Theme.fontSans
|
||||
font.bold: true
|
||||
font.pixelSize: 14
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.mediaArtist
|
||||
color: Theme.textSecondary
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 12
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 16
|
||||
|
||||
Text {
|
||||
text: "⏮"
|
||||
font.pixelSize: 20
|
||||
color: Theme.textSecondary
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -10
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.accentVolt
|
||||
onExited: parent.color = Theme.textSecondary
|
||||
onClicked: GlobalState.mediaPrevious()
|
||||
}
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.mediaStatus === "Playing" ? "⏸" : "▶"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textSecondary
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -10
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.accentVolt
|
||||
onExited: parent.color = Theme.textSecondary
|
||||
onClicked: GlobalState.mediaPlayPause()
|
||||
}
|
||||
}
|
||||
Text {
|
||||
text: "⏭"
|
||||
font.pixelSize: 20
|
||||
color: Theme.textSecondary
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.margins: -10
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = Theme.accentVolt
|
||||
onExited: parent.color = Theme.textSecondary
|
||||
onClicked: GlobalState.mediaNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Section 8: Calendar
|
||||
ColumnLayout {
|
||||
id: sectionCalendar
|
||||
Layout.fillWidth: true
|
||||
spacing: 16
|
||||
SectionHeader { sectionNum: "08"; title: "Calendar" }
|
||||
|
||||
RowLayout {
|
||||
spacing: 12
|
||||
Text {
|
||||
text: GlobalState.timeString
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 22
|
||||
color: Theme.accentVolt
|
||||
}
|
||||
Text {
|
||||
text: GlobalState.fullDateString
|
||||
font.family: Theme.fontSans
|
||||
font.pixelSize: 13
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
columns: 7
|
||||
columnSpacing: 4
|
||||
rowSpacing: 4
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
Repeater {
|
||||
model: ["S", "M", "T", "W", "T", "F", "S"]
|
||||
Text {
|
||||
text: modelData
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 11
|
||||
color: Theme.textMuted
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.preferredWidth: 32
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: 31
|
||||
Rectangle {
|
||||
property bool isToday: (index + 1) === new Date().getDate()
|
||||
Layout.preferredWidth: 32
|
||||
Layout.preferredHeight: 32
|
||||
color: isToday ? Theme.accentVolt : "transparent"
|
||||
radius: isToday ? 0 : 4
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: index + 1
|
||||
color: parent.isToday ? "#ffffff" : Theme.textSecondary
|
||||
font.family: Theme.fontMono
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: if (!parent.isToday) parent.color = Qt.rgba(1, 1, 1, 0.04)
|
||||
onExited: if (!parent.isToday) parent.color = "transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 20 } // Bottom padding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-scroll to active section when changed
|
||||
Connections {
|
||||
target: GlobalState
|
||||
function onSystemHudActiveSectionChanged() {
|
||||
if (!GlobalState.systemHudVisible) return;
|
||||
|
||||
let targetY = 0;
|
||||
if (GlobalState.systemHudActiveSection === "volume") targetY = sectionVolume.y;
|
||||
else if (GlobalState.systemHudActiveSection === "brightness") targetY = sectionBrightness.y;
|
||||
else if (GlobalState.systemHudActiveSection === "performance") targetY = sectionPerformance.y;
|
||||
else if (GlobalState.systemHudActiveSection === "network") targetY = sectionNetwork.y;
|
||||
else if (GlobalState.systemHudActiveSection === "battery") targetY = sectionBattery.y;
|
||||
else if (GlobalState.systemHudActiveSection === "nowplaying") targetY = sectionNowPlaying.y;
|
||||
else if (GlobalState.systemHudActiveSection === "calendar") targetY = sectionCalendar.y;
|
||||
|
||||
if (targetY > 0) {
|
||||
// Ensure targetY doesn't scroll past max possible scroll
|
||||
let maxY = Math.max(0, contentLayout.height - scrollView.height);
|
||||
scrollView.ScrollBar.vertical.position = Math.min(targetY, maxY) / contentLayout.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user