Compare commits

6 Commits

10 changed files with 165 additions and 153 deletions

View File

@@ -13,7 +13,7 @@ PanelWindow {
screen: modelData
implicitHeight: Theme.barSize
color: Qt.rgba(0.68, 0.75, 0.88, 0)
color: "transparent"
anchors {
top: true

View File

@@ -1,4 +1,5 @@
// CalendarComponent.qml (For one month)
pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Common.Styled
@@ -74,6 +75,8 @@ Item {
Repeater {
model: root.getLocalizedDayHeaders()
StyledText {
required property var modelData
text: modelData
horizontalAlignment: Text.AlignHCenter
width: 18
@@ -84,6 +87,7 @@ Item {
Repeater {
model: root.layoutData.days
StyledText {
required property var modelData
text: modelData
horizontalAlignment: Text.AlignHCenter
width: 18

View File

@@ -15,7 +15,6 @@ BackgroundRectangle {
property var notification: null
property bool startTimer: false
property int timerDuration: 2000
property bool firstTime: true
property string image: {
if (hasImage) {
@@ -26,60 +25,67 @@ BackgroundRectangle {
}
return Quickshell.iconPath(notification?.appIcon);
}
property real targetHeight: notifLayout.implicitHeight + 20
property bool collapsed: true
implicitWidth: 400
implicitHeight: 0
// implicitHeight: Math.max(notifLayout.implicitHeight, 100)
readonly property real contentHeight: Math.max(notifLayout.implicitHeight + 20, 100)
height: collapsed ? 0 : contentHeight
Component.onCompleted: {
root.implicitHeight = targetHeight;
root.collapsed = false;
}
clip: true
Behavior on implicitHeight {
Behavior on height {
SequentialAnimation {
NumberAnimation {
duration: 150
duration: 200
easing.type: Easing.OutCubic
}
ScriptAction {
script: {
if (clicked)
if (root.clicked)
root.dismissed();
if (root.implicitHeight === 0)
if (root.height === 0)
root.timedout();
}
}
}
}
clip: true
MouseArea {
anchors.fill: parent
onClicked: {
root.clicked = true;
root.implicitHeight = 0;
root.collapsed = true;
root.dismissed();
}
}
Timer {
id: notifTimer
interval: timerDuration
interval: root.timerDuration
running: root.startTimer
repeat: false
onTriggered: {
root.implicitHeight = 0;
root.collapsed = true;
}
}
RowLayout {
id: notifLayout
implicitHeight: Math.max(iconImage.implicitHeight, gridRoot.implicitHeight)
Layout.preferredHeight: Math.max(iconImage.implicitHeight, gridRoot.implicitHeight)
anchors {
fill: parent
top: parent.top
left: parent.left
right: parent.right
margins: 10
}
IconImage {
@@ -89,7 +95,7 @@ BackgroundRectangle {
Layout.leftMargin: 10
implicitSize: 80
visible: root.image ? true : false
source: root.image
source: root.image ? root.image : ""
}
ColumnLayout {
@@ -115,9 +121,8 @@ BackgroundRectangle {
StyledText {
id: summaryText
anchors.fill: parent
wrapMode: Text.Wrap
text: root.notification ? root.notification.summary : ""
}
}
@@ -137,9 +142,8 @@ BackgroundRectangle {
}
StyledText {
id: bodyText
anchors.fill: parent
wrapMode: Text.Wrap
text: root.notification ? root.notification.body : ""
}
}

View File

@@ -1,12 +1,9 @@
pragma Singleton
import Quickshell
Singleton {
id:root
id: root
readonly property string time: "oi"
}

View File

@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Widgets

View File

@@ -3,125 +3,106 @@ pragma Singleton
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import Quickshell.Io
import qs.Common
Singleton {
id: root
readonly property string hyprlandSignature: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")
readonly property var focusedMon: Hyprland.focusedMonitor
property var hasFullscreen: false
property var isScreencasting: false
property var sortedTopLevels: {
if (!ToplevelManager.toplevels || !ToplevelManager.toplevels.values) {
return [];
}
const topLevels = Array.from(Hyprland.toplevels.values);
const sortedHyprland = topLevels.sort((a, b) => {
if (a.monitor && b.monitor) {
const monitorCompare = a.monitor.name.localeCompare(b.monitor.name);
if (monitorCompare !== 0) {
return monitorCompare;
}
}
if (a.workspace && b.workspace) {
const workspaceCompare = a.workspace.id - b.workspace.id;
if (workspaceCompare !== 0) {
return workspaceCompare;
}
}
if (a.lastIpcObject && b.lastIpcObject && a.lastIpcObject.at && b.lastIpcObject.at) {
const aX = a.lastIpcObject.at[0];
const bX = b.lastIpcObject.at[0];
const aY = a.lastIpcObject.at[1];
const bY = b.lastIpcObject.at[1];
const xCompare = aX - bX;
if (Math.abs(xCompare) > 10) {
return xCompare;
}
return aY - bY;
}
if (a.lastIpcObject && !b.lastIpcObject) {
return -1;
}
if (!a.lastIpcObject && b.lastIpcObject) {
return 1;
}
if (a.title && b.title) {
return a.title.localeCompare(b.title);
}
return 0;
});
return sortedHyprland.filter(tl => tl.wayland !== null);
}
property var topLevelWorkspaces: {
return sortedTopLevels.map(topLevel => topLevel.workspace);
}
property bool hasFullscreen: false
property bool isScreencasting: false
property ListModel sortedDesktopApplicationsModel: ListModel {}
onSortedTopLevelsChanged: updateSortedDesktopApplications()
// Derived property of sorted toplevels
property var sortedTopLevels: {
const topLevels = Array.from(Hyprland.toplevels?.values ?? []);
const sorted = topLevels.sort((a, b) => {
if (a.monitor && b.monitor) {
const monitorCompare = a.monitor.name.localeCompare(b.monitor.name);
if (monitorCompare !== 0)
return monitorCompare;
}
if (a.workspace && b.workspace) {
const workspaceCompare = a.workspace.id - b.workspace.id;
if (workspaceCompare !== 0)
return workspaceCompare;
}
if (a.lastIpcObject?.at && b.lastIpcObject?.at) {
const xCompare = a.lastIpcObject.at[0] - b.lastIpcObject.at[0];
if (Math.abs(xCompare) > 10)
return xCompare;
return a.lastIpcObject.at[1] - b.lastIpcObject.at[1];
}
if (a.title && b.title)
return a.title.localeCompare(b.title);
return 0;
});
return sorted.filter(tl => tl.wayland !== null);
}
function updateSortedDesktopApplications() {
sortedDesktopApplicationsModel.clear();
property var topLevelWorkspaces: {
return sortedTopLevels.map(toplevel => toplevel.workspace);
}
for (const topLevel of sortedTopLevels) {
const entry = DesktopEntries.heuristicLookup(topLevel.wayland.appId);
sortedDesktopApplicationsModel.append({
topLevel: topLevel,
desktopEntry: entry
});
onSortedTopLevelsChanged: refreshSortedDesktopApplications()
Timer {
id: retryTimer
interval: 300
repeat: false
onTriggered: root.refreshSortedDesktopApplications()
}
function refreshSortedDesktopApplications() {
if (!Hyprland.toplevels)
return;
try {
sortedDesktopApplicationsModel.clear();
for (const topLevel of sortedTopLevels) {
const entry = DesktopEntries.heuristicLookup(topLevel.wayland.appId);
if (!entry) {
retryTimer.restart();
return;
}
sortedDesktopApplicationsModel.append({
topLevel: topLevel,
desktopEntry: entry
});
}
} catch (err) {
retryTimer.restart();
}
}
function workspaceApps(workspaceIndexAlign) {
const list = [];
const model = sortedDesktopApplicationsModel;
for (let i = 0; i < model.count; i++) {
const item = model.get(i);
for (let i = 0; i < sortedDesktopApplicationsModel.count; i++) {
const item = sortedDesktopApplicationsModel.get(i);
if (item.topLevel.workspace.id === workspaceIndexAlign)
list.push(item.desktopEntry);
}
return list;
}
// Hyprland socket listener
Socket {
path: `${Quickshell.env("XDG_RUNTIME_DIR")}/hypr/${Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE")}/.socket2.sock`
path: `${Quickshell.env("XDG_RUNTIME_DIR")}/hypr/${root.hyprlandSignature}/.socket2.sock`
connected: true
parser: SplitParser {
property var fullscreenRegex: new RegExp("fullscreen>>.")
property var screencastRegex: new RegExp("screencast>>.*")
property var fullscreenRegex: /fullscreen>>./
property var screencastRegex: /screencast>>.*/
onRead: msg => {
let match = fullscreenRegex.exec(msg);
if (match != null) {
if (msg.split(">>")[1] === "1") {
root.hasFullscreen = true;
} else {
root.hasFullscreen = false;
}
if (fullscreenRegex.test(msg)) {
root.hasFullscreen = msg.split(">>")[1] === "1";
}
match = screencastRegex.exec(msg);
if (match != null) {
if (msg.split(">>")[1].split(',')[0] === "1") {
root.isScreencasting = true;
} else {
root.isScreencasting = false;
}
if (screencastRegex.test(msg)) {
root.isScreencasting = msg.split(">>")[1].split(',')[0] === "1";
}
}
}

View File

@@ -12,7 +12,7 @@ Singleton {
readonly property var notificationServer: notificationServer
readonly property var notificationsNumber: notificationServer.trackedNotifications.values.length
readonly property var maxShown: 3
readonly property int maxShown: 3
property bool receivingLock: false
property bool manualNotificationsMuted: false
@@ -31,7 +31,7 @@ Singleton {
return;
notif.tracked = true;
root.addNotification(trackedNotifications, notif);
root.addNotification(root.trackedNotifications, notif);
if (notif.lastGeneration)
return;
@@ -150,7 +150,6 @@ Singleton {
Quickshell.screens.filter(screen => screen.name == HyprlandService.focusedMon.name)[0];
}
anchors.top: true
margins.top: screen.height / 100
exclusiveZone: 0
implicitWidth: 400
implicitHeight: screen.height

View File

@@ -93,6 +93,7 @@ PanelWindow {
required property var modelData
notification: modelData
implicitWidth: listView.width - 20
collapsed: false
onDismissed: {
NotificationService.notificationDismiss(notification);
}

View File

@@ -5,9 +5,8 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import qs.Services
import qs.Common.Styled
import qs.Common
import Quickshell.Wayland
import QtQuick.Effects
PanelWindow {
id: root
@@ -26,8 +25,26 @@ PanelWindow {
visible: true
signal clear
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
exclusionMode: ExclusionMode.Ignore
ScreencopyView {
id: raveel
anchors.fill: parent
captureSource: screen
}
MultiEffect {
source: raveel
anchors.fill: raveel
blurEnabled: true
blurMax: 32
blur: 1.0
}
MouseArea {
anchors.fill: parent
onClicked: root.clear()
@@ -36,7 +53,7 @@ PanelWindow {
Rectangle {
id: notifWindow
anchors.centerIn:parent
anchors.centerIn: parent
implicitHeight: 300
implicitWidth: listview.contentWidth
@@ -51,32 +68,36 @@ PanelWindow {
spacing: 5
orientation: ListView.Horizontal
focus:true
highlight: Rectangle { color: "black"; radius: 5 }
focus: true
highlight: Rectangle {
color: "black"
radius: 5
}
highlightFollowsCurrentItem: true
keyNavigationEnabled: false
highlightMoveDuration: 100
Keys.onPressed: (event) => {
Keys.onPressed: event => {
switch (event.key) {
case Qt.Key_L: // move down
case Qt.Key_D:
if (currentIndex < count - 1)
currentIndex++
event.accepted = true
break
case Qt.Key_H: // move up
case Qt.Key_A:
if (currentIndex > 0)
currentIndex--
event.accepted = true
break
case Qt.Key_Return:
currentItem.activate()
event.accepted = true
break
case Qt.Key_Escape:
root.clear()
break
case Qt.Key_L: // move down
case Qt.Key_D:
if (currentIndex < count - 1)
currentIndex++;
event.accepted = true;
break;
case Qt.Key_H: // move up
case Qt.Key_A:
if (currentIndex > 0)
currentIndex--;
event.accepted = true;
break;
case Qt.Key_Return:
currentItem.activate();
event.accepted = true;
break;
case Qt.Key_Escape:
root.clear();
break;
}
}
@@ -91,30 +112,34 @@ PanelWindow {
color: "transparent"
function activate() {
modelData.topLevel.wayland.activate()
root.clear()
modelData.topLevel.wayland.activate();
root.clear();
}
MouseArea {
anchors.fill: parent
onClicked: activate()
onClicked: parent.activate()
}
ScreencopyView {
anchors.fill: parent
live: true
captureSource: modelData.topLevel.wayland
captureSource: parent.modelData.topLevel.wayland
}
IconImage {
anchors.bottom: parent.bottom
property int workspaceId: modelData.topLevel.workspace.id
property var desktopEntry: modelData.desktopEntry
anchors {
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
}
property int workspaceId: parent.modelData.topLevel.workspace.id
property var desktopEntry: parent.modelData.desktopEntry ? parent.modelData.desktopEntry : null
width: 30
height: 30
source: (modelData && desktopEntry.icon) ? Quickshell.iconPath(desktopEntry.icon, 1) : "aaa"
source: (parent.modelData && desktopEntry.icon) ? Quickshell.iconPath(desktopEntry.icon, 1) : "aaa"
}
}
}

View File

@@ -127,7 +127,6 @@ WrapperMouseArea {
if (workspacesRectangle.workspace.id === Hyprland.focusedWorkspace.id) {
return;
}
;
Hyprland.dispatch("workspace " + workspacesRectangle.workspace.id);
PopUpHover.exit();
}