forked from Shinonome/dots-hyprland
waffles: taskview: show windows
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname=".svg"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="7.75"
|
||||||
|
inkscape:cx="12"
|
||||||
|
inkscape:cy="12"
|
||||||
|
inkscape:window-width="1173"
|
||||||
|
inkscape:window-height="790"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1007 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m8.5 16.586-3.793-3.793a1 1 0 0 0-1.414 1.414l4.5 4.5a1 1 0 0 0 1.414 0l11-11a1 1 0 0 0-1.414-1.414L8.5 16.586Z" fill="#212121"/></svg>
|
||||||
|
After Width: | Height: | Size: 239 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4.53 12.97a.75.75 0 0 0-1.06 1.06l4.5 4.5a.75.75 0 0 0 1.06 0l11-11a.75.75 0 0 0-1.06-1.06L8.5 16.94l-3.97-3.97Z" fill="#212121"/></svg>
|
||||||
|
After Width: | Height: | Size: 241 B |
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
sodipodi:docname=".svg"
|
||||||
|
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#000000"
|
||||||
|
borderopacity="0.25"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1"
|
||||||
|
inkscape:zoom="7.75"
|
||||||
|
inkscape:cx="12"
|
||||||
|
inkscape:cy="12"
|
||||||
|
inkscape:window-width="1173"
|
||||||
|
inkscape:window-height="790"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1007 B |
@@ -0,0 +1,6 @@
|
|||||||
|
import Quickshell
|
||||||
|
|
||||||
|
ScriptModel {
|
||||||
|
required property int count
|
||||||
|
values: Array(count).map((_, i) => i)
|
||||||
|
}
|
||||||
@@ -177,7 +177,7 @@ Singleton {
|
|||||||
|
|
||||||
property Component color: Component {
|
property Component color: Component {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
duration: 70
|
duration: 80
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,7 @@ import QtQuick.Controls
|
|||||||
ListView {
|
ListView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
boundsBehavior: Flickable.DragOverBounds
|
||||||
|
|
||||||
ScrollBar.vertical: WScrollBar {}
|
ScrollBar.vertical: WScrollBar {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
import qs
|
import qs
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.models
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
import qs.modules.waffle.looks
|
import qs.modules.waffle.looks
|
||||||
|
import "window-layout.js" as WindowLayout
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
@@ -36,6 +41,90 @@ Rectangle {
|
|||||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
property real maxWindowHeight: 290
|
||||||
|
property real maxWindowWidth: 738
|
||||||
|
property real padding: 52
|
||||||
|
property real spacing: 25
|
||||||
|
readonly property list<var> toplevels: ToplevelManager.toplevels.values.filter(t => {
|
||||||
|
const client = HyprlandData.clientForToplevel(t);
|
||||||
|
return client && client.workspace.id === HyprlandData.activeWorkspace?.id;
|
||||||
|
})
|
||||||
|
readonly property list<var> arrangedToplevels: {
|
||||||
|
const maxRowWidth = width - padding * 2;
|
||||||
|
const count = toplevels.length;
|
||||||
|
const resultLayout = [];
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
var row = [];
|
||||||
|
var rowWidth = 0;
|
||||||
|
var j = i;
|
||||||
|
|
||||||
|
while (j < count) {
|
||||||
|
const toplevel = toplevels[j];
|
||||||
|
const client = HyprlandData.clientForToplevel(toplevel);
|
||||||
|
const scaledSize = WindowLayout.scaleWindow(client, maxWindowWidth, maxWindowHeight);
|
||||||
|
|
||||||
|
if (rowWidth + scaledSize.width <= maxRowWidth || row.length === 0) {
|
||||||
|
row.push(toplevel);
|
||||||
|
rowWidth += scaledSize.width;
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultLayout.push(row);
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
return resultLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Windows
|
||||||
|
WListView {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: (root.height - (wsBorder.height + 16) - height) / 2
|
||||||
|
}
|
||||||
|
spacing: root.spacing
|
||||||
|
topMargin: root.padding
|
||||||
|
bottomMargin: root.padding
|
||||||
|
leftMargin: root.padding
|
||||||
|
rightMargin: root.padding
|
||||||
|
height: Math.min(contentHeight + topMargin + bottomMargin, root.height - (wsBorder.height + 16))
|
||||||
|
|
||||||
|
interactive: height < contentHeight
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
model: IndexModel {
|
||||||
|
count: arrangedToplevels.length
|
||||||
|
}
|
||||||
|
delegate: RowLayout {
|
||||||
|
id: clientRow
|
||||||
|
required property int index
|
||||||
|
spacing: root.spacing
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: IndexModel {
|
||||||
|
count: root.arrangedToplevels[clientRow.index].length
|
||||||
|
}
|
||||||
|
delegate: TaskViewWindow {
|
||||||
|
id: client
|
||||||
|
required property int index
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
maxHeight: root.maxWindowHeight
|
||||||
|
maxWidth: root.maxWindowWidth
|
||||||
|
toplevel: root.arrangedToplevels[clientRow.index][index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Workspaces
|
// Workspaces
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: wsBorder
|
id: wsBorder
|
||||||
@@ -65,7 +154,7 @@ Rectangle {
|
|||||||
|
|
||||||
implicitHeight: 174
|
implicitHeight: 174
|
||||||
|
|
||||||
ListView {
|
WListView {
|
||||||
id: workspaceListView
|
id: workspaceListView
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
@@ -83,15 +172,27 @@ Rectangle {
|
|||||||
clip: true
|
clip: true
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
|
||||||
model: ScriptModel {
|
function reposition() {
|
||||||
values: {
|
positionViewAtIndex(HyprlandData.activeWorkspace.id - 1, ListView.Contain);
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: HyprlandData
|
||||||
|
function onActiveWorkspaceChanged() {
|
||||||
|
workspaceListView.reposition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model: IndexModel {
|
||||||
|
id: workspaceIndexModel
|
||||||
|
count: {
|
||||||
const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id));
|
const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id));
|
||||||
return Array(Math.max(maxWorkspaceId, 1));
|
return Math.max(maxWorkspaceId, 1) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate: TaskViewWorkspace {
|
delegate: TaskViewWorkspace {
|
||||||
required property int index
|
required property int index
|
||||||
workspace: index + 1
|
workspace: index + 1
|
||||||
|
newWorkspace: index == workspaceIndexModel.count - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.waffle.looks
|
||||||
|
import "window-layout.js" as WindowLayout
|
||||||
|
|
||||||
|
WMouseAreaButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var toplevel
|
||||||
|
required property int maxHeight
|
||||||
|
required property int maxWidth
|
||||||
|
|
||||||
|
property var hyprlandClient: HyprlandData.clientForToplevel(root.toplevel)
|
||||||
|
|
||||||
|
property string iconName: AppSearch.guessIcon(hyprlandClient?.class)
|
||||||
|
|
||||||
|
color: containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase
|
||||||
|
borderColor: Looks.colors.bg2Border
|
||||||
|
radius: Looks.radius.xLarge
|
||||||
|
|
||||||
|
property size size: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight)
|
||||||
|
implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138)
|
||||||
|
implicitHeight: Math.round(contentItem.implicitHeight)
|
||||||
|
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: root.background
|
||||||
|
}
|
||||||
|
scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
id: scaleAnim
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutExpo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWindow() {
|
||||||
|
Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||||
|
onClicked: (event) => {
|
||||||
|
if (event.button === Qt.LeftButton) {
|
||||||
|
GlobalStates.overviewOpen = false
|
||||||
|
Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`)
|
||||||
|
GlobalStates.overviewOpen = false;
|
||||||
|
} else if (event.button === Qt.MiddleButton) {
|
||||||
|
root.closeWindow();
|
||||||
|
event.accepted = true;
|
||||||
|
} else if (event.button === Qt.RightButton) {
|
||||||
|
if (!windowMenu.visible) windowMenu.popup();
|
||||||
|
else windowMenu.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: contentItem
|
||||||
|
z: 2
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 1
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 8
|
||||||
|
WAppIcon {
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
iconName: root.iconName
|
||||||
|
implicitSize: 16
|
||||||
|
tryCustomIcon: false
|
||||||
|
}
|
||||||
|
WText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: root.hyprlandClient?.title ?? ""
|
||||||
|
}
|
||||||
|
CloseButton {
|
||||||
|
implicitWidth: 38
|
||||||
|
implicitHeight: 38
|
||||||
|
padding: 8
|
||||||
|
onClicked: root.closeWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreencopyView {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
implicitWidth: Math.round(root.size.width)
|
||||||
|
implicitHeight: Math.round(root.size.height)
|
||||||
|
|
||||||
|
captureSource: root.toplevel ?? null
|
||||||
|
live: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WMenu {
|
||||||
|
id: windowMenu
|
||||||
|
downDirection: true
|
||||||
|
|
||||||
|
Action {
|
||||||
|
enabled: root.hyprlandClient?.floating
|
||||||
|
property bool isPinned: root.hyprlandClient?.pinned
|
||||||
|
icon.name: isPinned ? "checkmark" : "empty"
|
||||||
|
text: Translation.tr("Show this window on all desktops")
|
||||||
|
onTriggered: {
|
||||||
|
Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Action {
|
||||||
|
icon.name: "empty"
|
||||||
|
text: Translation.tr("Close")
|
||||||
|
onTriggered: root.closeWindow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,13 +15,15 @@ WMouseAreaButton {
|
|||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property int workspace
|
required property int workspace
|
||||||
|
property bool newWorkspace: false
|
||||||
|
|
||||||
readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace
|
readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace
|
||||||
readonly property real screenWidth: QsWindow.window.width
|
readonly property real screenWidth: QsWindow.window.width
|
||||||
readonly property real screenHeight: QsWindow.window.height
|
readonly property real screenHeight: QsWindow.window.height
|
||||||
readonly property real screenAspectRatio: screenWidth / screenHeight
|
readonly property real screenAspectRatio: screenWidth / screenHeight
|
||||||
readonly property real screenScale: QsWindow.window.devicePixelRatio
|
readonly property real windowScale: wallpaperHeight / screenHeight
|
||||||
readonly property real scale: 0.1148148148
|
|
||||||
|
property real wallpaperHeight: 124
|
||||||
|
|
||||||
height: ListView.view.height
|
height: ListView.view.height
|
||||||
implicitWidth: 244 // for now
|
implicitWidth: 244 // for now
|
||||||
@@ -36,8 +38,18 @@ WMouseAreaButton {
|
|||||||
animation: Looks.transition.color.createObject(this)
|
animation: Looks.transition.color.createObject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scale: root.containsPress ? 0.95 : 1
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
id: scaleAnim
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutExpo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: contentItem
|
||||||
anchors {
|
anchors {
|
||||||
fill: parent
|
fill: parent
|
||||||
leftMargin: 12
|
leftMargin: 12
|
||||||
@@ -52,15 +64,15 @@ WMouseAreaButton {
|
|||||||
Layout.fillHeight: false
|
Layout.fillHeight: false
|
||||||
horizontalAlignment: Text.AlignLeft
|
horizontalAlignment: Text.AlignLeft
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: Translation.tr("Desktop %1").arg(root.workspace)
|
text: root.newWorkspace ? Translation.tr("New desktop") : Translation.tr("Desktop %1").arg(root.workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: wsBg
|
id: wsBg
|
||||||
height: 124
|
height: root.wallpaperHeight
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: Looks.colors.bg1Base
|
color: Looks.colors.bg1
|
||||||
|
|
||||||
layer.enabled: true
|
layer.enabled: true
|
||||||
layer.effect: OpacityMask {
|
layer.effect: OpacityMask {
|
||||||
@@ -71,34 +83,42 @@ WMouseAreaButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledImage {
|
// Workspace content
|
||||||
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cache: true
|
active: !root.newWorkspace
|
||||||
sourceSize: Qt.size(root.screenAspectRatio * 124, 124)
|
sourceComponent: StyledImage {
|
||||||
source: Config.options.background.wallpaperPath
|
cache: true
|
||||||
fillMode: Image.PreserveAspectCrop
|
sourceSize: Qt.size(root.screenAspectRatio * root.wallpaperHeight, root.wallpaperHeight)
|
||||||
|
source: Config.options.background.wallpaperPath
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: ToplevelManager.toplevels.values.filter(toplevel => {
|
values: HyprlandData.toplevelsForWorkspace(root.workspace)
|
||||||
const address = `0x${toplevel.HyprlandToplevel?.address}`;
|
}
|
||||||
var win = HyprlandData.windowByAddress[address];
|
delegate: ScreencopyView {
|
||||||
const inWorkspace = win?.workspace?.id === root.workspace;
|
required property var modelData
|
||||||
return inWorkspace;
|
readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`]
|
||||||
})
|
captureSource: modelData
|
||||||
}
|
live: true
|
||||||
delegate: ScreencopyView {
|
width: hyprlandWindowData?.size[0] * root.windowScale
|
||||||
required property var modelData
|
height: hyprlandWindowData?.size[1] * root.windowScale
|
||||||
readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`]
|
x: hyprlandWindowData?.at[0] * root.windowScale
|
||||||
captureSource: modelData
|
y: hyprlandWindowData?.at[1] * root.windowScale
|
||||||
live: true
|
}
|
||||||
width: hyprlandWindowData?.size[0] * root.scale
|
|
||||||
height: hyprlandWindowData?.size[1] * root.scale
|
|
||||||
x: hyprlandWindowData?.at[0] * root.scale
|
|
||||||
y: hyprlandWindowData?.at[1] * root.scale
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New plus icon
|
||||||
|
Loader {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
active: root.newWorkspace
|
||||||
|
sourceComponent: FluentIcon {
|
||||||
|
icon: "add"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +129,7 @@ WMouseAreaButton {
|
|||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
}
|
}
|
||||||
shown: root.isActiveWorkspace
|
shown: root.isActiveWorkspace
|
||||||
|
|
||||||
sourceComponent: Rectangle {
|
sourceComponent: Rectangle {
|
||||||
id: activeIndicator
|
id: activeIndicator
|
||||||
implicitWidth: 32
|
implicitWidth: 32
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
function scaleWindow(hyprlandClient, maxWindowWidth, maxWindowHeight) {
|
||||||
|
const [width, height] = hyprlandClient.size;
|
||||||
|
const [xScale, yScale] = [maxWindowWidth / width, maxWindowHeight / height];
|
||||||
|
const scale = Math.min(xScale, yScale);
|
||||||
|
return Qt.size(width * scale, height * scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrangedClients(hyprlandClients, maxRowWidth, maxWindowWidth, maxWindowHeight) {
|
||||||
|
const count = hyprlandClients.length;
|
||||||
|
const resultLayout = [];
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
var row = [];
|
||||||
|
var rowWidth = 0;
|
||||||
|
var j = i;
|
||||||
|
|
||||||
|
while (j < count) {
|
||||||
|
const client = hyprlandClients[j];
|
||||||
|
const scaledSize = scaleWindow(client, maxWindowWidth, maxWindowHeight);
|
||||||
|
|
||||||
|
if (rowWidth + scaledSize.width <= maxRowWidth || row.length === 0) {
|
||||||
|
row.push(client);
|
||||||
|
rowWidth += scaledSize.width;
|
||||||
|
j++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resultLayout.push(row);
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultLayout;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,6 +22,30 @@ Singleton {
|
|||||||
property var monitors: []
|
property var monitors: []
|
||||||
property var layers: ({})
|
property var layers: ({})
|
||||||
|
|
||||||
|
// Convenient stuff
|
||||||
|
|
||||||
|
function toplevelsForWorkspace(workspace) {
|
||||||
|
return ToplevelManager.toplevels.values.filter(toplevel => {
|
||||||
|
const address = `0x${toplevel.HyprlandToplevel?.address}`;
|
||||||
|
var win = HyprlandData.windowByAddress[address];
|
||||||
|
return win?.workspace?.id === workspace;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function hyprlandClientsForWorkspace(workspace) {
|
||||||
|
return root.windowList.filter(win => win.workspace.id === workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clientForToplevel(toplevel) {
|
||||||
|
if (!toplevel || !toplevel.HyprlandToplevel) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const address = `0x${toplevel?.HyprlandToplevel?.address}`;
|
||||||
|
return root.windowByAddress[address];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internals
|
||||||
|
|
||||||
function updateWindowList() {
|
function updateWindowList() {
|
||||||
getClients.running = true;
|
getClients.running = true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user