init: working version

This commit is contained in:
Kiana Sheibani 2025-10-07 19:43:46 -04:00
commit 7d8d7dacae
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
109 changed files with 15066 additions and 0 deletions

View file

@ -0,0 +1,496 @@
import qs.modules.bar.popouts as BarPopouts
import qs.config
import qs.custom
import qs.services
import qs.util
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
Item {
id: root
required property PersistentProperties uiState
required property BarPopouts.Wrapper popouts
width: Config.dashboard.workspaceWidth + 32
height: 750
property HyprlandWorkspace dragSourceWorkspace: null
property HyprlandWorkspace dragTargetWorkspace: null
// Controls scrolling at edge of panel while dragging
property real dragScrollVelocity: 0
CustomListView {
id: list
anchors.fill: parent
anchors.margins: 12
anchors.rightMargin: 0
spacing: 12
clip: true
CustomScrollBar.vertical: CustomScrollBar {
flickable: list
}
model: Hypr.monitors
delegate: Item {
id: monitor
required property int index
required property HyprlandMonitor modelData
readonly property ListModel workspaces: States.screens.get(modelData).workspaces
width: list.width - 20
height: childrenRect.height
Item {
id: monitorHeader
anchors.left: parent.left
anchors.right: parent.right
height: monIcon.height
MaterialIcon {
id: monIcon
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
text: "desktop_windows"
color: Config.colors.tertiary
}
CustomText {
id: monText
anchors.verticalCenter: parent.verticalCenter
anchors.left: monIcon.right
anchors.leftMargin: 4
text: monitor.modelData.name
color: Config.colors.tertiary
font.family: Config.font.family.mono
font.pointSize: Config.font.size.smaller
}
CustomRect {
anchors.verticalCenter: parent.verticalCenter
anchors.left: monText.right
anchors.right: parent.right
anchors.leftMargin: 8
anchors.rightMargin: 18
height: 1
color: Config.colors.inactive
}
}
ListView {
id: workspaceList
anchors.top: monitorHeader.bottom
anchors.topMargin: 8
width: list.width - 20
spacing: 22
height: contentHeight
acceptedButtons: Qt.NoButton
boundsBehavior: Flickable.StopAtBounds
model: monitor.workspaces
delegate: Column {
id: entry
required property int index
required property HyprlandWorkspace workspace
width: list.width - 20
spacing: 5
property bool hoveredWhileDragging: false
z: root.dragSourceWorkspace === workspace ? 10 : 0
Item {
id: header
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: labelState.height
property color nonAnimColor: entry.hoveredWhileDragging ? Config.colors.workspaceMove :
entry.workspace?.active ? Config.colors.workspaces : Config.colors.primary
property color color: nonAnimColor
Behavior on color {
CAnim {}
}
StateLayer {
id: labelState
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
implicitWidth: label.width + 20
implicitHeight: label.height + 8
color: Config.colors.secondary
function onClicked(): void {
Hypr.dispatch(`workspace ${entry.workspace.id}`)
}
Row {
id: label
anchors.centerIn: parent
spacing: 7
MaterialIcon {
text: Icons.getWorkspaceIcon(entry.workspace)
color: header.color
font.pointSize: Config.font.size.larger
animate: true
animateDuration: Config.anim.durations.small
}
CustomText {
anchors.top: parent.top
anchors.topMargin: 1
text: qsTr("Workspace %1").arg(index + 1)
font.family: Config.font.family.mono
font.pointSize: Config.font.size.normal
color: header.color
}
}
}
StateLayer {
anchors.verticalCenter: parent.verticalCenter
anchors.right: downArrow.left
anchors.rightMargin: 2
implicitWidth: implicitHeight
implicitHeight: upIcon.height + 6
disabled: entry.index === 0 && monitor.index === 0
function onClicked(): void {
if (entry.index !== 0) {
monitor.workspaces.move(entry.index, entry.index - 1, 1)
} else {
const workspace = entry.workspace;
monitor.workspaces.remove(entry.index, 1);
const otherWorkspaces = list.itemAtIndex(monitor.index - 1).workspaces;
otherWorkspaces.insert(0, {"workspace": workspace});
}
}
MaterialIcon {
id: upIcon
anchors.centerIn: parent
text: "keyboard_arrow_up"
color: parent.disabled ? Config.colors.inactive : header.nonAnimColor
font.pointSize: Config.font.size.larger
Behavior on color {
CAnim {}
}
}
}
StateLayer {
id: downArrow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
implicitWidth: implicitHeight
implicitHeight: downIcon.height + 6
disabled: entry.index === monitor.workspaces.count - 1 && monitor.index === list.count - 1
function onClicked(): void {
if (entry.index !== monitor.workspaces.count - 1) {
root.uiState.workspaces.move(entry.index, entry.index + 1, 1)
} else {
const workspace = entry.workspace;
monitor.workspaces.remove(entry.index, 1);
const otherWorkspaces = list.itemAtIndex(monitor.index + 1).workspaces;
otherWorkspaces.append({"workspace": workspace});
}
}
MaterialIcon {
id: downIcon
anchors.centerIn: parent
text: "keyboard_arrow_down"
color: parent.disabled ? Config.colors.inactive : header.nonAnimColor
font.pointSize: Config.font.size.larger
Behavior on color {
CAnim {}
}
}
}
}
CustomRect {
id: preview
anchors.left: parent.left
anchors.right: parent.right
readonly property HyprlandMonitor mon: entry.workspace.monitor
// Exclude UI border and apply monitor scale
readonly property real monX: Config.border.thickness
readonly property real monY: Config.bar.height
readonly property real monWidth: (mon.width / mon.scale) - 2 * Config.border.thickness
readonly property real monHeight: (mon.height / mon.scale) - Config.bar.height - Config.border.thickness
readonly property real aspectRatio: monHeight / monWidth
readonly property real sizeRatio: Config.dashboard.workspaceWidth / monWidth
width: Config.dashboard.workspaceWidth
height: aspectRatio * width
radius: 7
color: entry.hoveredWhileDragging ? Config.colors.workspaceMove :
entry.workspace?.active ? Color.mute(Config.colors.workspaces, 1.5, 1.1) : Config.colors.container
Behavior on color {
CAnim {}
}
DropArea {
anchors.fill: parent
onEntered: {
root.dragTargetWorkspace = entry.workspace;
entry.hoveredWhileDragging = true;
}
onExited: {
if (root.draggingTargetWorkspace === entry.workspace)
root.draggingTargetWorkspace = null;
entry.hoveredWhileDragging = false;
}
}
Repeater {
anchors.fill: parent
model: entry.workspace?.toplevels
delegate: Item {
id: window
required property HyprlandToplevel modelData
property var ipc: modelData.lastIpcObject
opacity: ipc && ipc.at && !ipc.hidden ? 1 : 0
property real nonAnimX: ipc?.at ? (ipc.at[0] - preview.monX) * preview.sizeRatio : 0
property real nonAnimY: ipc?.at ? (ipc.at[1] - preview.monY) * preview.sizeRatio : 0
property real nonAnimWidth: ipc?.size ? ipc.size[0] * preview.sizeRatio : 0
property real nonAnimHeight: ipc?.size ? ipc.size[1] * preview.sizeRatio : 0
x: nonAnimX
y: nonAnimY
width: nonAnimWidth
height: nonAnimHeight
z: nonAnimX !== x || nonAnimY !== y ? 10 : 0
Behavior on x {
enabled: window.x !== 0
Anim {}
}
Behavior on y {
enabled: window.y !== 0
Anim {}
}
Behavior on width {
enabled: window.width !== 0
Anim {}
}
Behavior on height {
enabled: window.height !== 0
Anim {}
}
Behavior on opacity { Anim {} }
ScreencopyView {
id: view
visible: false
anchors.left: parent.left
anchors.top: parent.top
layer.enabled: true
// NOTE: Simulates cropping fill (which ScreencopyView does not natively support)
readonly property real aspectRatio: sourceSize.height / sourceSize.width
width: Math.max(window.nonAnimWidth, window.nonAnimHeight / aspectRatio)
height: Math.max(window.nonAnimHeight, window.nonAnimWidth * aspectRatio)
captureSource: window.modelData.wayland
live: true
CustomRect {
anchors.fill: parent
color: mouse.drag.active ?
Qt.tint(Qt.alpha(Config.colors.overlay, 0.7), Qt.alpha(Config.colors.workspaceMove, 0.2)) :
mouse.pressed ?
Qt.tint(Qt.alpha(Config.colors.overlay, 0.7), Qt.alpha(Config.colors.workspaces, 0.2)) :
mouse.containsMouse ?
Qt.tint(Qt.alpha(Config.colors.overlay, 0.5), Qt.alpha(Config.colors.workspaces, 0.1)) :
Qt.alpha(Config.colors.overlay, 0.5)
Behavior on color {
CAnim { duration: Config.anim.durations.small }
}
}
}
Item {
id: mask
visible: false
anchors.fill: view
layer.enabled: true
Rectangle {
width: window.width
height: window.height
radius: 8
}
}
MultiEffect {
anchors.fill: view
source: view
maskEnabled: true
maskSource: mask
}
IconImage {
id: icon
anchors.centerIn: parent
implicitSize: Math.min(48, window.width * 0.5, window.height * 0.5)
source: Icons.getAppIcon(window.ipc?.class ?? "", "")
}
// Interactions
Drag.active: mouse.drag.active
Drag.source: window
Drag.hotSpot.x: nonAnimWidth / 2
Drag.hotSpot.y: nonAnimHeight / 2
MouseArea {
id: mouse
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.AllButtons
drag.target: parent
onPressed: event => {
root.dragSourceWorkspace = entry.workspace;
window.Drag.hotSpot.x = event.x;
window.Drag.hotSpot.y = event.y;
}
onReleased: event => {
const targetWorkspace = root.dragTargetWorkspace;
root.dragSourceWorkspace = null;
root.dragScrollVelocity = 0;
if (targetWorkspace !== null && targetWorkspace !== entry.workspace) {
Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace.id}, address:0x${window.modelData.address}`)
} else {
window.x = Qt.binding(() => window.nonAnimX);
window.y = Qt.binding(() => window.nonAnimY);
}
}
onClicked: event => {
if (event.button === Qt.LeftButton) {
root.uiState.dashboard = false;
Hypr.dispatch(`focuswindow address:0x${window.modelData.address}`);
} else if (event.button === Qt.MiddleButton) {
Hypr.dispatch(`closewindow address:0x${window.modelData.address}`);
} else if (event.button === Qt.RightButton) {
root.uiState.dashboard = false;
popouts.currentName = "activewindow";
popouts.currentCenter = QsWindow.window.width / 2;
popouts.window = window.modelData;
popouts.hasCurrent = true;
}
}
onPositionChanged: event => {
if (!drag.active) return;
const y = root.mapFromItem(this, 0, event.y).y;
if (y < 100) {
root.dragScrollVelocity = (y - 100) / 25;
} else if (y > root.height - 100) {
root.dragScrollVelocity = (y - root.height + 100) / 25;
} else {
root.dragScrollVelocity = 0;
}
}
}
}
}
}
}
add: Transition {
Anim {
property: "opacity"
from: 0
to: 1
}
}
remove: Transition {
Anim {
property: "opacity"
from: 1
to: 0
}
}
move: Transition {
Anim {
property: "y"
}
Anim {
properties: "opacity"
to: 1
}
}
displaced: Transition {
Anim {
property: "y"
}
Anim {
properties: "opacity"
to: 1
}
}
}
}
}
Timer {
running: root.dragScrollVelocity !== 0
repeat: true
interval: 10
onTriggered: {
list.contentY += root.dragScrollVelocity;
list.returnToBounds();
}
}
}