left sidebar: put in loader

This commit is contained in:
end-4
2025-05-17 23:45:53 +02:00
parent 2b98b8dada
commit 8533310f29
@@ -17,191 +17,199 @@ Scope { // Scope
property int sidebarPadding: 15 property int sidebarPadding: 15
property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}] property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}]
PanelWindow { // Window Loader {
id: sidebarRoot id: sidebarLoader
visible: false active: false
focusable: true onActiveChanged: {
property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab GlobalStates.sidebarLeftOpenCount += active ? 1 : -1
property bool extend: false
property bool pin: false
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
onVisibleChanged: {
GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1
} }
PanelWindow { // Window
id: sidebarRoot
visible: sidebarLoader.active
focusable: true
property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab
property bool extend: false
property bool pin: false
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
exclusiveZone: pin ? sidebarWidth : 0 function hide() {
implicitWidth: Appearance.sizes.sidebarWidthExtended sidebarLoader.active = false
WlrLayershell.namespace: "quickshell:sidebarLeft"
// Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
top: true
left: true
bottom: true
}
mask: Region {
item: sidebarLeftBackground
}
HyprlandFocusGrab { // Click outside to close
id: grab
windows: [ sidebarRoot ]
active: false
onCleared: () => {
if (!active) sidebarRoot.visible = false
} }
}
Connections { exclusiveZone: pin ? sidebarWidth : 0
target: sidebarRoot implicitWidth: Appearance.sizes.sidebarWidthExtended
function onVisibleChanged() { WlrLayershell.namespace: "quickshell:sidebarLeft"
delayedGrabTimer.start() // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close
swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
top: true
left: true
bottom: true
} }
function onPinChanged() {
grab.active = !sidebarRoot.pin mask: Region {
item: sidebarLeftBackground
} }
}
Timer { HyprlandFocusGrab { // Click outside to close
id: delayedGrabTimer id: grab
interval: ConfigOptions.hacks.arbitraryRaceConditionDelay windows: [ sidebarRoot ]
repeat: false active: false
onTriggered: { onCleared: () => {
grab.active = sidebarRoot.visible && !sidebarRoot.pin if (!active) sidebarRoot.hide()
}
}
// Background
Rectangle {
id: sidebarLeftBackground
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
color: Appearance.colors.colLayer0
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
focus: sidebarRoot.visible
Behavior on width {
NumberAnimation {
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
} }
} }
Keys.onPressed: (event) => { Connections {
// console.log("Key pressed: " + event.key) target: sidebarRoot
if (event.key === Qt.Key_Escape) { function onVisibleChanged() {
sidebarRoot.visible = false; delayedGrabTimer.start()
swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus()
} }
if (event.modifiers === Qt.ControlModifier) { function onPinChanged() {
if (event.key === Qt.Key_PageDown) { grab.active = !sidebarRoot.pin
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1))
}
else if (event.key === Qt.Key_PageUp) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0))
}
else if (event.key === Qt.Key_Tab) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length);
}
else if (event.key === Qt.Key_Backtab) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length);
}
else if (event.key === Qt.Key_O) {
sidebarRoot.extend = !sidebarRoot.extend;
}
else if (event.key === Qt.Key_P) {
sidebarRoot.pin = !sidebarRoot.pin;
}
event.accepted = true;
} }
} }
ColumnLayout { Timer {
anchors.fill: parent id: delayedGrabTimer
anchors.margins: sidebarPadding interval: ConfigOptions.hacks.arbitraryRaceConditionDelay
repeat: false
spacing: sidebarPadding onTriggered: {
grab.active = sidebarRoot.visible && !sidebarRoot.pin
}
}
PrimaryTabBar { // Tab strip // Background
id: tabBar Rectangle {
tabButtonList: root.tabButtonList id: sidebarLeftBackground
externalTrackedTab: sidebarRoot.selectedTab
function onCurrentIndexChanged(currentIndex) { anchors.top: parent.top
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) anchors.left: parent.left
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
color: Appearance.colors.colLayer0
radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1
focus: sidebarRoot.visible
Behavior on width {
NumberAnimation {
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
} }
} }
SwipeView { // Content pages Keys.onPressed: (event) => {
id: swipeView // console.log("Key pressed: " + event.key)
Layout.topMargin: 5 if (event.key === Qt.Key_Escape) {
Layout.fillWidth: true sidebarRoot.hide();
Layout.fillHeight: true
spacing: 10
currentIndex: sidebarRoot.selectedTab
onCurrentIndexChanged: {
tabBar.enableIndicatorAnimation = true
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
} }
if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_PageDown) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1))
}
else if (event.key === Qt.Key_PageUp) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0))
}
else if (event.key === Qt.Key_Tab) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length);
}
else if (event.key === Qt.Key_Backtab) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length);
}
else if (event.key === Qt.Key_O) {
sidebarRoot.extend = !sidebarRoot.extend;
}
else if (event.key === Qt.Key_P) {
sidebarRoot.pin = !sidebarRoot.pin;
}
event.accepted = true;
}
}
clip: true ColumnLayout {
layer.enabled: true anchors.fill: parent
layer.effect: OpacityMask { anchors.margins: sidebarPadding
maskSource: Rectangle {
width: swipeView.width spacing: sidebarPadding
height: swipeView.height
radius: Appearance.rounding.small PrimaryTabBar { // Tab strip
id: tabBar
tabButtonList: root.tabButtonList
externalTrackedTab: sidebarRoot.selectedTab
function onCurrentIndexChanged(currentIndex) {
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
} }
} }
AiChat { SwipeView { // Content pages
panelWindow: sidebarRoot id: swipeView
} Layout.topMargin: 5
Anime { Layout.fillWidth: true
panelWindow: sidebarRoot Layout.fillHeight: true
spacing: 10
currentIndex: sidebarRoot.selectedTab
onCurrentIndexChanged: {
tabBar.enableIndicatorAnimation = true
PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex)
}
clip: true
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: swipeView.width
height: swipeView.height
radius: Appearance.rounding.small
}
}
AiChat {
panelWindow: sidebarRoot
}
Anime {
panelWindow: sidebarRoot
}
} }
} }
} }
}
// Shadow // Shadow
DropShadow { DropShadow {
anchors.fill: sidebarLeftBackground anchors.fill: sidebarLeftBackground
horizontalOffset: 0 horizontalOffset: 0
verticalOffset: 2 verticalOffset: 2
radius: Appearance.sizes.elevationMargin radius: Appearance.sizes.elevationMargin
samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs
color: Appearance.colors.colShadow color: Appearance.colors.colShadow
source: sidebarLeftBackground source: sidebarLeftBackground
} }
}
} }
IpcHandler { IpcHandler {
target: "sidebarLeft" target: "sidebarLeft"
function toggle(): void { function toggle(): void {
sidebarRoot.visible = !sidebarRoot.visible; sidebarLoader.active = !sidebarLoader.active
if(sidebarRoot.visible) Notifications.timeoutAll(); if(sidebarLoader.active) Notifications.timeoutAll();
} }
function close(): void { function close(): void {
sidebarRoot.visible = false; sidebarLoader.active = false
} }
function open(): void { function open(): void {
sidebarRoot.visible = true; sidebarLoader.active = true
if(sidebarRoot.visible) Notifications.timeoutAll(); if(sidebarLoader.active) Notifications.timeoutAll();
} }
} }
@@ -210,8 +218,8 @@ Scope { // Scope
description: "Toggles left sidebar on press" description: "Toggles left sidebar on press"
onPressed: { onPressed: {
sidebarRoot.visible = !sidebarRoot.visible; sidebarLoader.active = !sidebarLoader.active;
if(sidebarRoot.visible) Notifications.timeoutAll(); if(sidebarLoader.active) Notifications.timeoutAll();
} }
} }
@@ -220,8 +228,8 @@ Scope { // Scope
description: "Opens left sidebar on press" description: "Opens left sidebar on press"
onPressed: { onPressed: {
sidebarRoot.visible = true; sidebarLoader.active = true;
if(sidebarRoot.visible) Notifications.timeoutAll(); if(sidebarLoader.active) Notifications.timeoutAll();
} }
} }
@@ -230,7 +238,7 @@ Scope { // Scope
description: "Closes left sidebar on press" description: "Closes left sidebar on press"
onPressed: { onPressed: {
sidebarRoot.visible = false; sidebarLoader.active = false;
} }
} }