diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf
index 6032ab65a..0cca263f1 100644
--- a/dots/.config/hypr/hyprland/keybinds.conf
+++ b/dots/.config/hypr/hyprland/keybinds.conf
@@ -53,7 +53,7 @@ bindd = Ctrl+Super, T, Toggle wallpaper selector, global, quickshell:wallpaperSe
bindd = Ctrl+Super+Alt, T, Select random wallpaper, global, quickshell:wallpaperSelectorRandom # Random wallpaper
bindd = Ctrl+Super, T, Change wallpaper, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # [hidden] Change wallpaper (fallback)
bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets
-bind = Super+Alt, W, global, quickshell:panelFamilyCycle # Cycle panel family
+bind = Ctrl+Super, P, global, quickshell:panelFamilyCycle # Cycle panel family
##! Utilities
# Screenshot, Record, OCR, Color picker, Clipboard history
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/.svg b/dots/.config/quickshell/ii/assets/icons/fluent/.svg
new file mode 100644
index 000000000..c047f0c03
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg
new file mode 100644
index 000000000..966863066
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg
new file mode 100644
index 000000000..07380988b
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg b/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg
new file mode 100644
index 000000000..c047f0c03
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg
@@ -0,0 +1,35 @@
+
+
diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml
index aebb91b66..341df9045 100644
--- a/dots/.config/quickshell/ii/modules/common/Config.qml
+++ b/dots/.config/quickshell/ii/modules/common/Config.qml
@@ -418,6 +418,8 @@ Singleton {
property real scale: 0.18 // Relative to screen size
property real rows: 2
property real columns: 5
+ property bool orderRightLeft: false
+ property bool orderBottomUp: false
property bool centerIcons: true
}
diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml
index 9afbed44b..78b0a9edb 100644
--- a/dots/.config/quickshell/ii/modules/common/Directories.qml
+++ b/dots/.config/quickshell/ii/modules/common/Directories.qml
@@ -44,6 +44,7 @@ Singleton {
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`)
property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts")
property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`)
+ property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`)
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
@@ -59,6 +60,7 @@ Singleton {
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
+ Quickshell.execDetached(["mkdir", "-p", `${userActions}`])
Quickshell.execDetached(["rm", "-rf", `${tempImages}`])
}
}
diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml
index 478cadd10..247884028 100644
--- a/dots/.config/quickshell/ii/modules/common/Persistent.qml
+++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml
@@ -63,6 +63,10 @@ Singleton {
property real temperature: 0.5
}
+ property JsonObject cheatsheet: JsonObject {
+ property int tabIndex: 0
+ }
+
property JsonObject sidebar: JsonObject {
property JsonObject bottomGroup: JsonObject {
property bool collapsed: false
diff --git a/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml b/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml
new file mode 100644
index 000000000..0cf30cdea
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml
@@ -0,0 +1,6 @@
+import Quickshell
+
+ScriptModel {
+ required property int count
+ values: Array(count).map((_, i) => i)
+}
diff --git a/dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml
index e6454b476..60d5ac949 100644
--- a/dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml
+++ b/dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml
@@ -72,7 +72,7 @@ Scope {
}
function stopFingerPam() {
- if (fingerPam.running) {
+ if (fingerPam.active) {
fingerPam.abort();
}
}
diff --git a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml
index 9e4b9bd94..5a1b24d7e 100644
--- a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml
+++ b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml
@@ -92,7 +92,6 @@ Scope {
WlSessionLock {
id: lock
locked: GlobalStates.screenLocked
-
surface: root.sessionLockSurface
}
diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml
index 26518cfb1..4267e7bcd 100644
--- a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml
+++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml
@@ -100,7 +100,7 @@ ListView {
to: 1,
}),
] : []
- }
+ }
move: Transition {
animations: root.animateMovement ? [
diff --git a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml
index d133fa654..f7fb68f68 100644
--- a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml
+++ b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml
@@ -4,6 +4,7 @@ import qs.modules.common.widgets
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
+import Qt.labs.synchronizer
import Qt5Compat.GraphicalEffects
import Quickshell.Io
import Quickshell
@@ -135,7 +136,10 @@ Scope { // Scope
ToolbarTabBar {
id: tabBar
tabButtonList: root.tabButtonList
- currentIndex: swipeView.currentIndex
+
+ Synchronizer on currentIndex {
+ property alias source: swipeView.currentIndex
+ }
}
}
@@ -144,8 +148,11 @@ Scope { // Scope
Layout.topMargin: 5
Layout.fillWidth: true
Layout.fillHeight: true
- currentIndex: tabBar.currentIndex
spacing: 10
+ currentIndex: Persistent.states.cheatsheet.tabIndex
+ onCurrentIndexChanged: {
+ Persistent.states.cheatsheet.tabIndex = currentIndex;
+ }
implicitWidth: Math.max.apply(null, contentChildren.map(child => child.implicitWidth || 0))
implicitHeight: Math.max.apply(null, contentChildren.map(child => child.implicitHeight || 0))
diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml
index db424e0e6..8b791f44a 100644
--- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml
+++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml
@@ -50,6 +50,21 @@ Item {
property Component windowComponent: OverviewWindow {}
property list windowWidgets: []
+
+ function getWsRow(ws) {
+ // 1-indexed workspace, 0-indexed row
+ var normalRow = Math.floor((ws - 1) / Config.options.overview.columns) % Config.options.overview.rows;
+ return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - normalRow - 1 : normalRow);
+ }
+ function getWsColumn(ws) {
+ // 1-indexed workspace, 0-indexed column
+ var normalCol = (ws - 1) % Config.options.overview.columns;
+ return (Config.options.overview.orderRightLeft ? Config.options.overview.columns - normalCol - 1 : normalCol);
+ }
+ function getWsInCell(ri, ci) {
+ // 1-indexed workspace, 0-indexed row and column index
+ return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - ri - 1 : ri) * Config.options.overview.columns + (Config.options.overview.orderRightLeft ? Config.options.overview.columns - ci - 1 : ci) + 1
+ }
StyledRectangularShadow {
target: overviewBackground
@@ -85,7 +100,7 @@ Item {
id: workspace
required property int index
property int colIndex: index
- property int workspaceValue: root.workspaceGroup * root.workspacesShown + row.index * Config.options.overview.columns + colIndex + 1
+ property int workspaceValue: root.workspaceGroup * root.workspacesShown + getWsInCell(row.index, colIndex)
property color defaultWorkspaceColor: ColorUtils.mix(Appearance.colors.colBackgroundSurfaceContainer, Appearance.colors.colSurfaceContainerHigh, 0.8)
property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
@@ -182,8 +197,8 @@ Item {
property bool atInitPosition: (initX == x && initY == y)
// Offset on the canvas
- property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.columns
- property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.columns)
+ property int workspaceColIndex: getWsColumn(windowData?.workspace.id)
+ property int workspaceRowIndex: getWsRow(windowData?.workspace.id)
xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex
yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex
property real xWithinWorkspaceWidget: Math.max((windowData?.at[0] - (monitor?.x ?? 0) - monitorData?.reserved[0]) * root.scale, 0)
@@ -286,9 +301,8 @@ Item {
Rectangle { // Focused workspace indicator
id: focusedWorkspaceIndicator
- property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown)
- property int rowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.columns)
- property int colIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.columns
+ property int rowIndex: getWsRow(monitor.activeWorkspace?.id)
+ property int colIndex: getWsColumn(monitor.activeWorkspace?.id)
x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex
y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex
z: root.windowZ
diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml
index b99e291b0..f94f1e811 100644
--- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml
+++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml
@@ -739,6 +739,45 @@ ContentPage {
}
}
}
+ ConfigRow {
+ uniform: true
+ ConfigSelectionArray {
+ currentValue: Config.options.overview.orderRightLeft
+ onSelected: newValue => {
+ Config.options.overview.orderRightLeft = newValue
+ }
+ options: [
+ {
+ displayName: Translation.tr("Left to right"),
+ icon: "arrow_forward",
+ value: 0
+ },
+ {
+ displayName: Translation.tr("Right to left"),
+ icon: "arrow_back",
+ value: 1
+ }
+ ]
+ }
+ ConfigSelectionArray {
+ currentValue: Config.options.overview.orderBottomUp
+ onSelected: newValue => {
+ Config.options.overview.orderBottomUp = newValue
+ }
+ options: [
+ {
+ displayName: Translation.tr("Top-down"),
+ icon: "arrow_downward",
+ value: 0
+ },
+ {
+ displayName: Translation.tr("Bottom-up"),
+ icon: "arrow_upward",
+ value: 1
+ }
+ ]
+ }
+ }
}
ContentSection {
diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml
index f33c6e12a..a2670118e 100644
--- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml
@@ -9,8 +9,8 @@ MouseArea {
id: root
Layout.fillHeight: true
- implicitHeight: row.implicitHeight
- implicitWidth: row.implicitWidth
+ implicitHeight: appRow.implicitHeight
+ implicitWidth: appRow.implicitWidth
hoverEnabled: true
function showPreviewPopup(appEntry, button) {
@@ -21,31 +21,31 @@ MouseArea {
animation: Looks.transition.move.createObject(this)
}
- // Apps row
- RowLayout {
- id: row
+ WListView {
+ id: appRow
anchors {
top: parent.top
bottom: parent.bottom
}
+ orientation: Qt.Horizontal
spacing: 0
+ implicitWidth: contentWidth
+ clip: true
+ interactive: false
+ // TODO: Include only apps (and windows) in current workspace only | wait, does that even make sense in a Hyprland workflow?
+ model: ScriptModel {
+ objectProp: "appId"
+ values: TaskbarApps.apps.filter(app => app.appId !== "SEPARATOR")
+ }
+ delegate: TaskAppButton {
+ required property var modelData
+ appEntry: modelData
- Repeater {
- // TODO: Include only apps (and windows) in current workspace only | wait, does that even make sense in a Hyprland workflow?
- model: ScriptModel {
- objectProp: "appId"
- values: TaskbarApps.apps.filter(app => app.appId !== "SEPARATOR")
+ onHoverPreviewRequested: {
+ root.showPreviewPopup(appEntry, this);
}
- delegate: TaskAppButton {
- required property var modelData
- appEntry: modelData
-
- onHoverPreviewRequested: {
- root.showPreviewPopup(appEntry, this)
- }
- onHoverPreviewDismissed: {
- previewPopup.close()
- }
+ onHoverPreviewDismissed: {
+ previewPopup.close();
}
}
}
@@ -56,5 +56,4 @@ MouseArea {
tasksHovered: root.containsMouse
anchor.window: root.QsWindow.window
}
-
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml
index 581ce9152..b1ff6f353 100644
--- a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml
@@ -36,15 +36,34 @@ LockScreen {
Image {
id: bg
z: 0
- anchors.fill: parent
+ width: parent.width
+ height: parent.height
+ onStatusChanged: {
+ if (status === Image.Ready) {
+ print("Lock wallpaper loaded");
+ print(lockSurfaceItem.height);
+ y = -lockSurfaceItem.height;
+ openAnim.restart();
+ }
+ }
sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height)
source: Config.options.background.wallpaperPath
fillMode: Image.PreserveAspectCrop
+
+ PropertyAnimation {
+ id: openAnim
+ target: bg
+ property: "y"
+ to: 0
+ duration: 350
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
+ }
}
GaussianBlur {
z: 1
- anchors.fill: parent
+ anchors.fill: bg
source: bg
radius: 100
samples: radius * 2 + 1
@@ -67,7 +86,7 @@ LockScreen {
Interactables {
id: interactables
z: 2
- anchors.fill: parent
+ anchors.fill: bg
}
}
@@ -83,12 +102,31 @@ LockScreen {
// }
function switchToFocusedView() {
- root.passwordView = true;
+ switchToPasswordViewAnim.restart();
+ }
+
+ SequentialAnimation {
+ id: switchToPasswordViewAnim
+ PropertyAnimation {
+ target: unfocusedContent
+ property: "y"
+ from: 0
+ to: -height * 1.1
+ duration: 250
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
+ }
+ ScriptAction {
+ script: {
+ root.passwordView = true;
+ }
+ }
}
Item {
id: unfocusedContent
- anchors.fill: parent
+ width: parent.width
+ height: parent.height
visible: !root.passwordView
ClockTextGroup {
anchors {
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
index 51e7fe40c..8120aa85e 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
@@ -177,7 +177,7 @@ Singleton {
property Component color: Component {
ColorAnimation {
- duration: 120
+ duration: 80
easing.type: Easing.BezierSpline
easing.bezierCurve: transition.easing.bezierCurve.easeIn
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml
index bc567762a..3e506ef99 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml
@@ -6,5 +6,14 @@ import QtQuick.Controls
ListView {
id: root
+ boundsBehavior: Flickable.DragOverBounds
+
ScrollBar.vertical: WScrollBar {}
+
+ displaced: Transition {
+ animations: [Looks.transition.enter.createObject(this, {
+ property: "y"
+ })]
+ }
+
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml
index 368829865..5e9582bf1 100644
--- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml
@@ -53,10 +53,9 @@ BodyRectangle {
}
}
- StyledListView {
+ WListView {
Layout.fillWidth: true
Layout.fillHeight: true
- animateAppearance: false
clip: true
model: Notifications.appNameList
diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml
new file mode 100644
index 000000000..8d3972c40
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml
@@ -0,0 +1,32 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import qs.modules.common
+import qs.modules.common.widgets
+import qs.modules.common.functions
+import qs.modules.waffle.looks
+
+SequentialAnimation {
+ id: root
+
+ required property var target
+
+ PropertyAction {
+ target: root.target
+ property: "ListView.delayRemove"
+ value: true
+ }
+ NumberAnimation {
+ target: root.target
+ property: "x"
+ to: root.target.width
+ duration: 250
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
+ }
+ PropertyAction {
+ target: root.target
+ property: "ListView.delayRemove"
+ value: false
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml
index 658bc03ae..88c44136e 100644
--- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml
@@ -1,3 +1,4 @@
+pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
@@ -18,10 +19,44 @@ MouseArea {
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
+ function dismissAll() {
+ root.notifications.forEach(notif => {
+ Qt.callLater(() => {
+ Notifications.discardNotification(notif.notificationId);
+ });
+ });
+ removeAnimation.start();
+ }
+
+ WNotificationDismissAnim {
+ id: removeAnimation
+ target: root
+ }
+
+ property real dragDismissThreshold: 100
+ drag {
+ axis: Drag.XAxis
+ target: contentLayout
+ minimumX: 0
+ onActiveChanged: {
+ if (drag.active)
+ return;
+ if (contentLayout.x > root.dragDismissThreshold) {
+ root.dismissAll();
+ } else {
+ contentLayout.x = 0;
+ }
+ }
+ }
+
ColumnLayout {
id: contentLayout
- anchors.fill: parent
spacing: 4
+ width: root.width
+
+ Behavior on x {
+ animation: Looks.transition.enter.createObject(this)
+ }
GroupHeader {
id: notifHeader
@@ -29,7 +64,9 @@ MouseArea {
Layout.margins: 11
}
- ListView {
+ WListView {
+ Layout.leftMargin: -Math.min(35, contentLayout.x)
+ Layout.rightMargin: -Layout.leftMargin
Layout.fillWidth: true
implicitWidth: notifHeader.implicitWidth
implicitHeight: contentHeight
@@ -40,14 +77,20 @@ MouseArea {
objectProp: "notificationId"
}
delegate: WSingleNotification {
+ id: singleNotif
required property int index
required property var modelData
+
width: ListView.view.width
notification: modelData
+
groupExpandControlMessage: {
- if (root.notifications.length <= 1) return "";
- if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1);
- if (index === root.notifications.length - 1) return Translation.tr("See fewer");
+ if (root.notifications.length <= 1)
+ return "";
+ if (!root.expanded)
+ return Translation.tr("+%1 notifications").arg(root.notifications.length - 1);
+ if (index === root.notifications.length - 1)
+ return Translation.tr("See fewer");
return "";
}
onGroupExpandToggle: {
@@ -94,11 +137,7 @@ MouseArea {
Layout.rightMargin: 3
icon.name: "dismiss"
onClicked: {
- root.notifications.forEach(notif => {
- Qt.callLater(() => {
- Notifications.discardNotification(notif.notificationId);
- });
- });
+ root.dismissAll();
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml
index 895ab6892..350e7b0ab 100644
--- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml
@@ -18,6 +18,18 @@ MouseArea {
signal groupExpandToggle
hoverEnabled: true
+ function dismiss() {
+ Qt.callLater(() => {
+ Notifications.discardNotification(root.notification?.notificationId);
+ });
+ removeAnimation.start();
+ }
+
+ WNotificationDismissAnim {
+ id: removeAnimation
+ target: root
+ }
+
implicitHeight: contentItem.implicitHeight
implicitWidth: contentItem.implicitWidth
@@ -25,9 +37,25 @@ MouseArea {
animation: Looks.transition.enter.createObject(this)
}
+ property real dragDismissThreshold: 100
+ drag {
+ axis: Drag.XAxis
+ target: contentItem
+ minimumX: 0
+ onActiveChanged: {
+ if (drag.active)
+ return;
+ if (contentItem.x > root.dragDismissThreshold) {
+ root.dismiss();
+ } else {
+ contentItem.x = 0;
+ }
+ }
+ }
+
Rectangle {
id: contentItem
- anchors.fill: parent
+ width: parent.width
color: Looks.colors.bgPanelBody
radius: Looks.radius.medium
property real padding: 12
@@ -36,6 +64,10 @@ MouseArea {
border.width: 1
border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
+ Behavior on x {
+ animation: Looks.transition.enter.createObject(this)
+ }
+
ColumnLayout {
id: notificationContent
anchors.fill: parent
@@ -128,11 +160,7 @@ MouseArea {
opacity: root.containsMouse ? 1 : 0
icon.name: "dismiss"
implicitSize: 12
- onClicked: {
- Qt.callLater(() => {
- Notifications.discardNotification(root.notification?.notificationId);
- });
- }
+ onClicked: root.dismiss()
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml
index fb506dd49..64ad321c6 100644
--- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml
@@ -86,7 +86,7 @@ WBarAttachedPanelContent {
id: searchBar
Layout.fillWidth: true
implicitWidth: 832 // TODO: Make sizes naturally inferred
- horizontalPadding: root.searching ? 24 : 32
+ horizontalPadding: 32
// verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel
Synchronizer on searching {
property alias target: root.searching
diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml
index 7c7153fb9..4dc6f5bf7 100644
--- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml
@@ -1,44 +1,214 @@
import QtQuick
+import QtQuick.Layouts
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.models
import qs.modules.common.widgets
import qs.modules.waffle.looks
+import "window-layout.js" as WindowLayout
Rectangle {
id: root
color: ColorUtils.transparentize(Looks.colors.bg1Base, 0.5)
+ property bool draggingWindow: false
property real openProgress: 0
+ property Item hoveredWorkspace: null
+ signal closed
Component.onCompleted: {
openAnim.start();
}
+ function close() {
+ closeAnim.start();
+ }
PropertyAnimation {
id: openAnim
target: root
property: "openProgress"
to: 1
- duration: 200
+ duration: 250
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
}
- PropertyAnimation {
+ SequentialAnimation {
id: closeAnim
- target: root
- property: "openProgress"
- to: 0
- duration: 200
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
+
+ PropertyAnimation {
+ target: root
+ property: "openProgress"
+ to: 0
+ duration: 250
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
+ }
+ ScriptAction {
+ script: {
+ root.closed();
+ }
+ }
+ }
+
+ // Windows
+ property real maxWindowHeight: 290
+ property real maxWindowWidth: 738
+ property real padding: 52
+ property real spacing: 25
+ readonly property list toplevels: ToplevelManager.toplevels.values.filter(t => {
+ const client = HyprlandData.clientForToplevel(t);
+ return client && client.workspace.id === HyprlandData.activeWorkspace?.id;
+ })
+ readonly property list 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;
+ }
+
+ MouseArea {
+ z: 0
+ anchors.fill: parent
+ onClicked: {
+ GlobalStates.overviewOpen = false;
+ }
+ }
+
+ // Windows
+ WListView {
+ id: windowListView
+ z: root.openProgress == 1 ? 2 : 1
+ 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) && !root.draggingWindow
+ clip: root.openProgress > 0.99 && !root.draggingWindow
+
+ model: ScriptModel {
+ values: root.arrangedToplevels
+ }
+ delegate: RowLayout {
+ id: clientRow
+ required property var modelData
+ spacing: root.spacing
+ anchors.horizontalCenter: parent?.horizontalCenter ?? undefined
+
+ Repeater {
+ model: ScriptModel {
+ values: clientRow.modelData
+ }
+ delegate: Item {
+ id: clientGridArea
+ required property int index
+ required property var modelData
+ implicitWidth: windowItem.openedSize.width
+ implicitHeight: windowItem.openedSize.height + windowItem.titleBarImplicitHeight
+
+ TaskViewWindow {
+ id: windowItem
+ z: Drag.active ? 2 : 1
+ opacity: openAnim.running ? root.openProgress : 1
+
+ property int mappedX: {
+ // print("AAAWAWAAWAWWA: ", -(clientRow.x + clientGridArea.x + root.padding));
+ var rootPosToThis = -(clientRow.x + clientGridArea.x + root.padding);
+ return rootPosToThis + hyprlandClient.at[0];
+ }
+ property int mappedY: {
+ // print("AAAWAWAAWAWWA YYYY YUIUSDFOIU: ", clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight)
+ var rootPosToThis = -(clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight);
+ return rootPosToThis + hyprlandClient.at[1];
+ }
+ property int openedX: 0
+ property int openedY: 0
+ // property int openedX: Drag.active ? (dragHandler.xAxis.activeValue) : 0
+ // property int openedY: Drag.active ? (dragHandler.yAxis.activeValue) : 0
+ scaleSize: (root.openProgress > 0 && !closeAnim.running)
+ x: mappedX + (openedX - mappedX) * root.openProgress
+ y: mappedY + (openedY - mappedY) * root.openProgress
+
+ droppable: root.hoveredWorkspace !== null
+ Drag.active: dragHandler.active
+ Drag.hotSpot.x: mouseX
+ Drag.hotSpot.y: mouseY
+
+ DragHandler {
+ id: dragHandler
+ target: null
+ xAxis.onActiveValueChanged: {
+ windowItem.openedX = dragHandler.xAxis.activeValue;
+ }
+ yAxis.onActiveValueChanged: {
+ windowItem.openedY = dragHandler.yAxis.activeValue;
+ }
+ onActiveChanged: {
+ if (active) {
+ root.draggingWindow = true;
+ } else {
+ root.draggingWindow = false;
+ if (root.hoveredWorkspace !== null && root.hoveredWorkspace.workspace !== windowItem.hyprlandClient.workspace.id) {
+ Hyprland.dispatch(`movetoworkspacesilent ${root.hoveredWorkspace.workspace}, address:${windowItem.hyprlandClient.address}`);
+ } else {
+ windowItem.openedX = 0;
+ windowItem.openedY = 0;
+ }
+ }
+ }
+ }
+
+ Layout.alignment: Qt.AlignTop
+ maxHeight: root.maxWindowHeight
+ maxWidth: root.maxWindowWidth
+ toplevel: clientGridArea.modelData
+ }
+ }
+ }
+ }
}
// Workspaces
Rectangle {
id: wsBorder
+ z: root.openProgress == 1 ? 1 : 2
property real sourceEdgeMargin: -(height + 8) + root.openProgress * (height + 16)
anchors {
left: parent.left
@@ -64,8 +234,9 @@ Rectangle {
color: Looks.colors.bgPanelFooterBase
implicitHeight: 174
-
- ListView {
+
+ WListView {
+ id: workspaceListView
anchors {
top: parent.top
bottom: parent.bottom
@@ -73,22 +244,56 @@ Rectangle {
topMargin: 5
bottomMargin: 5
}
+ flickableDirection: Flickable.HorizontalFlick
+ orientation: ListView.Horizontal
+ interactive: width == parent.width
width: Math.min(contentWidth + leftMargin + rightMargin, parent.width)
leftMargin: 5
rightMargin: 5
clip: true
- orientation: ListView.Horizontal
spacing: 4
- model: ScriptModel {
- values: {
- const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id))
- return Array(maxWorkspaceId)
+ function reposition() {
+ 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));
+ return Math.max(maxWorkspaceId, 1) + 1;
}
}
delegate: TaskViewWorkspace {
+ id: workspaceItem
required property int index
workspace: index + 1
+ newWorkspace: index == workspaceIndexModel.count - 1
+
+ droppable: root.hoveredWorkspace === workspaceItem
+ DropArea {
+ anchors.fill: parent
+ onEntered: drag => {
+ root.hoveredWorkspace = workspaceItem;
+ }
+ onExited: {
+ if (root.hoveredWorkspace === workspaceItem) {
+ root.hoveredWorkspace = null;
+ }
+ }
+ }
+
+ onClicked: {
+ GlobalStates.overviewOpen = false;
+ root.closed(); // Close immediately to avoid weird animations
+ Hyprland.dispatch(`workspace ${workspaceItem.workspace}`);
+ }
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml
new file mode 100644
index 000000000..0cab91fe5
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml
@@ -0,0 +1,155 @@
+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 address: hyprlandClient?.address
+
+ property string iconName: AppSearch.guessIcon(hyprlandClient?.class)
+
+ color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase)
+ borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0)
+ radius: Looks.radius.xLarge
+
+ property real titleBarImplicitHeight: titleBar.implicitHeight
+ property bool scaleSize: true
+ property size openedSize: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight);
+ property size fullSize: Qt.size(hyprlandClient?.size[0] ?? maxWidth, hyprlandClient?.size[1] ?? maxHeight)
+ property size size: scaleSize ? openedSize : fullSize
+ implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138)
+ implicitHeight: Math.round(contentItem.implicitHeight)
+
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Item {
+ width: root.background.width
+ height: root.background.height
+ Rectangle {
+ radius: root.background.radius
+ anchors {
+ fill: parent
+ topMargin: root.drag.active ? root.titleBarImplicitHeight : 0
+ }
+ }
+ }
+ }
+ property bool droppable: false
+ scale: (root.pressedButtons & Qt.LeftButton || root.Drag.active) ? (droppable ? 0.4 : 0.95) : 1
+ Behavior on scale {
+ NumberAnimation {
+ id: scaleAnim
+ duration: 200
+ 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 {
+ id: titleBar
+ opacity: root.drag.active ? 0 : 1
+ 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)
+ constraintSize: Qt.size(Math.round(root.size.width), Math.round(root.size.height))
+
+ Behavior on implicitWidth {
+ animation: Looks.transition.enter.createObject(this)
+ }
+ Behavior on implicitHeight {
+ animation: Looks.transition.enter.createObject(this)
+ }
+
+ 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()
+ }
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml
index 987511f05..8fe3f35bd 100644
--- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml
@@ -15,22 +15,37 @@ WMouseAreaButton {
id: root
required property int workspace
+ property bool newWorkspace: false
+ property bool droppable: false
- readonly property real screenWidth: QsWindow.window.width
- readonly property real screenHeight: QsWindow.window.height
+ readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace
+ readonly property real screenWidth: QsWindow.window?.width ?? 0
+ readonly property real screenHeight: QsWindow.window?.height ?? 0
readonly property real screenAspectRatio: screenWidth / screenHeight
- readonly property real screenScale: QsWindow.window.devicePixelRatio
- readonly property real scale: 0.1148148148
+ readonly property real windowScale: wallpaperHeight / screenHeight
- height: ListView.view.height
+ property real wallpaperHeight: 124
+
+ height: ListView.view?.height ?? 100
implicitWidth: 244 // for now
- onClicked: {
- GlobalStates.overviewOpen = false;
- Hyprland.dispatch(`workspace ${root.workspace}`);
+ colBackground: ColorUtils.transparentize(Looks.colors.bg2, (isActiveWorkspace || droppable) ? 0 : 1)
+ Behavior on color {
+ 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
ColumnLayout {
+ id: contentItem
anchors {
fill: parent
leftMargin: 12
@@ -45,15 +60,15 @@ WMouseAreaButton {
Layout.fillHeight: false
horizontalAlignment: Text.AlignLeft
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 {
id: wsBg
- height: 124
+ height: root.wallpaperHeight
Layout.fillHeight: true
Layout.fillWidth: true
- color: Looks.colors.bg1Base
+ color: Looks.colors.bg1
layer.enabled: true
layer.effect: OpacityMask {
@@ -64,34 +79,67 @@ WMouseAreaButton {
}
}
- StyledImage {
+ // Workspace content
+ Loader {
anchors.fill: parent
- cache: true
- sourceSize: Qt.size(root.screenAspectRatio * 124, 124)
- source: Config.options.background.wallpaperPath
- fillMode: Image.PreserveAspectCrop
+ active: !root.newWorkspace
+ sourceComponent: StyledImage {
+ cache: true
+ sourceSize: Qt.size(root.screenAspectRatio * root.wallpaperHeight, root.wallpaperHeight)
+ source: Config.options.background.wallpaperPath
+ fillMode: Image.PreserveAspectCrop
- Repeater {
- model: ScriptModel {
- values: ToplevelManager.toplevels.values.filter(toplevel => {
- const address = `0x${toplevel.HyprlandToplevel?.address}`;
- var win = HyprlandData.windowByAddress[address];
- const inWorkspace = win?.workspace?.id === root.workspace;
- return inWorkspace;
- })
- }
- delegate: ScreencopyView {
- required property var modelData
- readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`]
- captureSource: modelData
- 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
+ Repeater {
+ model: ScriptModel {
+ values: HyprlandData.toplevelsForWorkspace(root.workspace)
+ }
+ delegate: ScreencopyView {
+ required property var modelData
+ readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`]
+ captureSource: modelData
+ live: true
+ width: hyprlandWindowData?.size[0] * root.windowScale
+ height: hyprlandWindowData?.size[1] * root.windowScale
+ x: hyprlandWindowData?.at[0] * root.windowScale
+ y: hyprlandWindowData?.at[1] * root.windowScale
+ }
}
}
}
+
+ // New plus icon
+ Loader {
+ anchors.centerIn: parent
+ active: root.newWorkspace
+ sourceComponent: FluentIcon {
+ icon: "add"
+ }
+ }
+
+ Rectangle {
+ z: 2
+ visible: root.droppable && !root.newWorkspace
+ anchors.fill: parent
+ color: Looks.colors.accent
+ opacity: 0.2
+ }
+ }
+ }
+
+ // Active indicator
+ WFadeLoader {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ bottom: parent.bottom
+ }
+ shown: root.isActiveWorkspace
+
+ sourceComponent: Rectangle {
+ id: activeIndicator
+ implicitWidth: 32
+ implicitHeight: 3
+ color: Looks.colors.accent
+ radius: height / 2
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml
index 75e71ccb1..05a2ccf40 100644
--- a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml
@@ -23,7 +23,14 @@ Scope {
Loader {
id: panelLoader
required property var modelData
- active: GlobalStates.overviewOpen
+ active: false
+ Connections {
+ target: GlobalStates
+ function onOverviewOpenChanged() {
+ if (GlobalStates.overviewOpen)
+ panelLoader.active = true;
+ }
+ }
sourceComponent: PanelWindow {
id: root
property string searchingText: ""
@@ -33,7 +40,7 @@ Scope {
WlrLayershell.namespace: "quickshell:wTaskView"
WlrLayershell.layer: WlrLayer.Overlay
- // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
+ WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
@@ -44,7 +51,26 @@ Scope {
}
TaskViewContent {
+ id: taskViewContent
anchors.fill: parent
+
+ Component.onCompleted: {
+ taskViewContent.forceActiveFocus();
+ }
+ Keys.onPressed: event => {
+ if (event.key === Qt.Key_Escape) {
+ GlobalStates.overviewOpen = false;
+ }
+ }
+
+ Connections {
+ target: GlobalStates
+ function onOverviewOpenChanged() {
+ if (!GlobalStates.overviewOpen)
+ taskViewContent.close();
+ }
+ }
+ onClosed: panelLoader.active = false
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js b/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js
new file mode 100644
index 000000000..d5548bdb5
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js
@@ -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;
+}
diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh
index fc7d8729e..5b24f16f9 100755
--- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh
+++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh
@@ -2,6 +2,6 @@
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate
-"$SCRIPT_DIR/thumbgen.py" "$@"
+GIO_USE_VFS=local "$SCRIPT_DIR/thumbgen.py" "$@"
deactivate
diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py
index 69f630721..92dd126f6 100755
--- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py
+++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py
@@ -15,7 +15,7 @@ import gi
from loguru import logger
from tqdm import tqdm
-gi.require_version("GnomeDesktop", "3.0")
+gi.require_version("GnomeDesktop", "4.0")
from gi.repository import Gio, GnomeDesktop # isort:skip
thumbnail_size_map = {
diff --git a/dots/.config/quickshell/ii/services/HyprlandData.qml b/dots/.config/quickshell/ii/services/HyprlandData.qml
index abbaaf577..5ec7bd68d 100644
--- a/dots/.config/quickshell/ii/services/HyprlandData.qml
+++ b/dots/.config/quickshell/ii/services/HyprlandData.qml
@@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
+import Quickshell.Wayland
import Quickshell.Hyprland
/**
@@ -21,6 +22,30 @@ Singleton {
property var monitors: []
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() {
getClients.running = true;
}
@@ -63,6 +88,7 @@ Singleton {
function onRawEvent(event) {
// console.log("Hyprland raw event:", event.name);
+ if (["openlayer", "closelayer", "screencast"].includes(event.name)) return;
updateAll()
}
}
diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml
index f5a18bec9..52e783e28 100644
--- a/dots/.config/quickshell/ii/services/LauncherSearch.qml
+++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml
@@ -4,6 +4,7 @@ import qs.modules.common
import qs.modules.common.models
import qs.modules.common.functions
import QtQuick
+import Qt.labs.folderlistmodel
import Quickshell
import Quickshell.Io
@@ -31,6 +32,34 @@ Singleton {
return acc;
}, []).sort()
+ // Load user action scripts from ~/.config/illogical-impulse/actions/
+ // Uses FolderListModel to auto-reload when scripts are added/removed
+ property var userActionScripts: {
+ const actions = [];
+ for (let i = 0; i < userActionsFolder.count; i++) {
+ const fileName = userActionsFolder.get(i, "fileName");
+ const filePath = userActionsFolder.get(i, "filePath");
+ if (fileName && filePath) {
+ const actionName = fileName.replace(/\.[^/.]+$/, ""); // strip extension
+ actions.push({
+ action: actionName,
+ execute: ((path) => (args) => {
+ Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]);
+ })(FileUtils.trimFileProtocol(filePath.toString()))
+ });
+ }
+ }
+ return actions;
+ }
+
+ FolderListModel {
+ id: userActionsFolder
+ folder: Qt.resolvedUrl(Directories.userActions)
+ showDirs: false
+ showHidden: false
+ sortField: FolderListModel.Name
+ }
+
property var searchActions: [
{
action: "accentcolor",
@@ -90,6 +119,9 @@ Singleton {
},
]
+ // Combined built-in and user actions
+ property var allActions: searchActions.concat(userActionScripts)
+
property string mathResult: ""
property bool clipboardWorkSafetyActive: {
const enabled = Config.options.workSafety.enable.clipboard;
@@ -273,7 +305,7 @@ Singleton {
Qt.openUrlExternally(url);
}
});
- const launcherActionObjects = root.searchActions.map(action => {
+ const launcherActionObjects = root.allActions.map(action => {
const actionString = `${Config.options.search.prefix.action}${action.action}`;
if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) {
return resultComp.createObject(null, {
diff --git a/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml
index b610c3d44..9f04f6364 100644
--- a/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml
+++ b/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml
@@ -111,6 +111,14 @@ ApiStrategy {
return ({})
}
+ // Error response handling
+ if (dataJson.error) {
+ const errorMsg = `**Error ${dataJson.error.code}**: ${dataJson.error.message}`;
+ message.rawContent += errorMsg;
+ message.content += errorMsg;
+ return { finished: true };
+ }
+
// No candidates?
if (!dataJson.candidates) return {};
diff --git a/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml
index 14cd1a17a..9a37df096 100644
--- a/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml
+++ b/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml
@@ -58,8 +58,17 @@ ApiStrategy {
// Real stuff
try {
const dataJson = JSON.parse(cleanData);
+
+ // Error response handling
+ if (dataJson.error) {
+ const errorMsg = `**Error**: ${dataJson.error.message || JSON.stringify(dataJson.error)}`;
+ message.rawContent += errorMsg;
+ message.content += errorMsg;
+ return { finished: true };
+ }
+
let newContent = "";
-
+
const responseContent = dataJson.choices[0]?.delta?.content || dataJson.message?.content;
const responseReasoning = dataJson.choices[0]?.delta?.reasoning || dataJson.choices[0]?.delta?.reasoning_content;
diff --git a/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml
index 1178c837f..a049abf30 100644
--- a/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml
+++ b/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml
@@ -49,8 +49,17 @@ ApiStrategy {
// Real stuff
try {
const dataJson = JSON.parse(cleanData);
+
+ // Error response handling
+ if (dataJson.error) {
+ const errorMsg = `**Error**: ${dataJson.error.message || JSON.stringify(dataJson.error)}`;
+ message.rawContent += errorMsg;
+ message.content += errorMsg;
+ return { finished: true };
+ }
+
let newContent = "";
-
+
const responseContent = dataJson.choices[0]?.delta?.content || dataJson.message?.content;
const responseReasoning = dataJson.choices[0]?.delta?.reasoning || dataJson.choices[0]?.delta?.reasoning_content;
diff --git a/sdata/dist-fedora/SPECS/quickshell-git.spec b/sdata/dist-fedora/SPECS/quickshell-git.spec
index e90bdebe2..5167a1ae3 100644
--- a/sdata/dist-fedora/SPECS/quickshell-git.spec
+++ b/sdata/dist-fedora/SPECS/quickshell-git.spec
@@ -63,8 +63,6 @@ Wayland and X11.
%endif
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_BUILD_TYPE=Release \
- -DDISTRIBUTOR="Fedora COPR (errornointernet/quickshell)" \
- -DDISTRIBUTOR_DEBUGINFO_AVAILABLE=YES \
-DGIT_REVISION=%{commit} \
-DINSTALL_QML_PREFIX=%{_lib}/qt6/qml
%cmake_build
diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml
index 367626ced..def14e066 100644
--- a/sdata/dist-fedora/feddeps.toml
+++ b/sdata/dist-fedora/feddeps.toml
@@ -1,3 +1,13 @@
+# COPR repositories list
+[copr]
+repos = [
+ "ririko66z/dots-hyprland",
+ "solopasha/hyprland",
+ "deltacopy/darkly",
+ "alternateved/eza",
+ "atim/starship"
+]
+
# Fedora dependencies list
# Audio
@@ -17,6 +27,7 @@ packages = [
"brightnessctl",
"ddcutil"
]
+install_opts = ["--setopt=install_weak_deps=False"]
# Basic
[groups.basic]
diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh
index 20e1d1328..18da73e06 100644
--- a/sdata/dist-fedora/install-deps.sh
+++ b/sdata/dist-fedora/install-deps.sh
@@ -1,9 +1,15 @@
# This script is meant to be sourced.
# It's not for directly running.
+# -------------------------
+# CONFIG
+# -------------------------
+user_config="${REPO_ROOT}/sdata/dist-fedora/user_data.yaml"
+rpmbuildroot="${REPO_ROOT}/cache/rpmbuild"
+deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml"
-
-# Initialize the user configuration file
-user_config=${REPO_ROOT}/sdata/dist-fedora/user_data.yaml
+# -------------------------
+# FUNCTIONS
+# -------------------------
# Recording DNF Transaction ID
function r() {
@@ -16,6 +22,42 @@ function r() {
[ "$original_id" == "$last_id" ] || yq -i ".dnf.transaction_ids += [ $last_id ]" "$user_config" || :
}
+# Start building and install the missing RPM package locally.
+function install_RPMS() {
+ local local_specs local_rpms
+ rpmbuildroot="${rpmbuildroot:-${REPO_ROOT}/cache/rpmbuild}"
+
+ x mkdir -p "$rpmbuildroot"/{BUILD,RPMS,SOURCES}
+ x cp -r "${REPO_ROOT}/sdata/dist-fedora/SPECS" "$rpmbuildroot/"
+
+ x cd $rpmbuildroot/SPECS
+
+ mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0)
+ for spec_file in ${local_specs[@]}; do
+ # Download sources
+ x spectool -g -C "$rpmbuildroot/SOURCES" "$spec_file"
+ # Install build dependencies
+ r x sudo dnf builddep -y "$spec_file"
+ # Build the RPM package locally. If it fails, download it from COPR.
+ if ! rpmbuild -bb --define "_topdir $rpmbuildroot" --define "debug_package %{nil}" "$spec_file"; then
+ printf "${STY_RED}Local build encountered an issue. Downloading $(basename "$spec_file" .spec) from COPR. Report the issue to Discussions pls.${STY_RST}\n"
+ sudo dnf install -y $(basename "$spec_file" .spec)
+ nolock_qs=true
+ fi
+ done
+
+ mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0)
+ if [[ ${#local_rpms[@]} -ge 1 ]]; then
+ echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y"
+ r x sudo dnf install "${local_rpms[@]}" -y
+ fi
+ x cd ${REPO_ROOT}
+}
+
+# -------------------------
+# MAIN
+# -------------------------
+
if ! command -v dnf >/dev/null 2>&1; then
printf "${STY_RED}[$0]: dnf not found, it seems that the system is not Fedora 42 or later distros. Aborting...${STY_RST}\n"
exit 1
@@ -33,61 +75,38 @@ v sudo dnf versionlock delete quickshell-git 2>/dev/null
# Install yq for parsing config files
v sudo dnf install yq -y
-# Development-tools
-r v sudo dnf install @development-tools fedora-packager rpmdevtools fonts-rpm-macros qt6-rpm-macros -y
+# Install development tools
+r v sudo dnf install @development-tools fedora-packager -y
-# COPR repositories
-v sudo dnf copr enable ririko66z/dots-hyprland -y
-v sudo dnf copr enable solopasha/hyprland -y
-v sudo dnf copr enable deltacopy/darkly -y
-v sudo dnf copr enable alternateved/eza -y
-v sudo dnf copr enable atim/starship -y
-
-# Start building and install the missing RPM package locally.
-install_RPMS() {
- rpmbuildroot=${REPO_ROOT}/cache/rpmbuild
- x mkdir -p $rpmbuildroot/{BUILD,RPMS,SOURCES}
- x cp -r ${REPO_ROOT}/sdata/dist-fedora/SPECS $rpmbuildroot/
- x cd $rpmbuildroot/SPECS
- mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0)
- for spec_file in ${local_specs[@]}; do
- x spectool -g -C "$rpmbuildroot/SOURCES" $spec_file
- r x sudo dnf builddep -y $spec_file
- x rpmbuild -bb --define "_topdir $rpmbuildroot" $spec_file
- done
- mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0)
- echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y"
- r x sudo dnf install "${local_rpms[@]}" -y
- x cd ${REPO_ROOT}
-}
+# Install COPR repositories
+copr_repos_json=$(yq -o=j '.copr.repos // []' "$deps_data_file")
+eval "$(jq -r '@sh "copr_repos_array+=(\(.[]))"' <<<"$copr_repos_json")" # Fedora distro contains jq
+for copr in ${copr_repos_array[@]}; do
+ v sudo dnf copr enable "$copr" -y
+done
+# Build and install locally RPMS
showfun install_RPMS
v install_RPMS
-deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml"
+# Install packages from toml file
deps_data=$(yq -o=j '.' "$deps_data_file")
echo "Starting to install packages from $deps_data_file ..."
while IFS= read -r deps_list_key; do
-
echo "Installing package list: $deps_list_key"
+
install_opts=$(echo $deps_data | yq ".groups.\"$deps_list_key\" | select(has(\"install_opts\")) | .install_opts[]")
package_list=$(echo $deps_data | yq ".groups.\"$deps_list_key\".packages | unique | .[]")
r v sudo dnf install -y $install_opts $package_list 0)
-')
+done < <(echo "$deps_data" | yq '.groups | keys[]? | select(length > 0)')
# Add back versionlock at the end
-v sudo dnf versionlock add quickshell-git
+[ -n $nolock_qs ] || v sudo dnf versionlock add quickshell-git || true
echo -e "\n========================================"
-echo "All installations are complete."
+echo "All installations are completed."
echo "========================================"
-
diff --git a/sdata/dist-gentoo/README.md b/sdata/dist-gentoo/README.md
index 630a3a178..84153c779 100644
--- a/sdata/dist-gentoo/README.md
+++ b/sdata/dist-gentoo/README.md
@@ -52,3 +52,29 @@ end
- The Hyprland live ebuild sometimes has linkage issues, deleting _Hyprland_ and _hyprland_ from `/usr/bin/` and then re-emerging usually fixes this.
- When emerging Hyprland if you get an issue relating to `undefined reference to ``Hyprutils::Math::Vector2D::˜Vector2D()`` `
- Clear the cache folder (`rm -fr /var/tmp/portage/gui-wm/hyprland*`) then try again
+- If emerging ``hyprland-qtutils`` fails and gives you something like this...
+ ```cmake
+ CMake Error at utils/dialog/CMakeLists.txt:26 (target_link_libraries):
+ Target "hyprland-dialog" links to:
+ Qt6::WaylandClientPrivate
+ but the target was not found. Possible reasons include:
+ * There is a typo in the target name.
+ * A find_package call is missing for an IMPORTED target.
+ * An ALIAS target is missing.
+ CMake Error at utils/update-screen/CMakeLists.txt:34 (target_link_libraries):
+ Target "hyprland-update-screen" links to:
+ Qt6::WaylandClientPrivate
+ but the target was not found. Possible reasons include:
+ * There is a typo in the target name.
+ * A find_package call is missing for an IMPORTED target.
+ * An ALIAS target is missing.
+ CMake Error at utils/donate-screen/CMakeLists.txt:32 (target_link_libraries):
+ Target "hyprland-donate-screen" links to:
+ Qt6::WaylandClientPrivate
+ but the target was not found. Possible reasons include:
+ * There is a typo in the target name.
+ * A find_package call is missing for an IMPORTED target.
+ * An ALIAS target is missing.
+ ```
+ Try putting ``sdata/dist-gentoo/hyprland-qtutils-private.patch`` into ``/etc/portage/patches/gui-libs/hyprland-qtutils/``.
+ - Patch Credit: fedeliallalinea on https://forums.gentoo.org/viewtopic-p-8874098.html
diff --git a/sdata/dist-gentoo/hyprland-qtutils-private.patch b/sdata/dist-gentoo/hyprland-qtutils-private.patch
new file mode 100644
index 000000000..6ca55104f
--- /dev/null
+++ b/sdata/dist-gentoo/hyprland-qtutils-private.patch
@@ -0,0 +1,33 @@
+--- a/utils/dialog/CMakeLists.txt
++++ b/utils/dialog/CMakeLists.txt
+@@ -8,7 +8,7 @@
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient)
++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate)
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils)
+--- a/utils/donate-screen/CMakeLists.txt
++++ b/utils/donate-screen/CMakeLists.txt
+@@ -8,7 +8,7 @@
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient)
++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate)
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils)
+--- a/utils/update-screen/CMakeLists.txt
++++ b/utils/update-screen/CMakeLists.txt
+@@ -8,7 +8,7 @@
+ set(CMAKE_CXX_STANDARD 23)
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient)
++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate)
+ find_package(PkgConfig REQUIRED)
+
+ pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils)
diff --git a/sdata/dist-gentoo/install-deps.sh b/sdata/dist-gentoo/install-deps.sh
index 0aadbf81b..9d79c157a 100644
--- a/sdata/dist-gentoo/install-deps.sh
+++ b/sdata/dist-gentoo/install-deps.sh
@@ -1,23 +1,12 @@
printf "${STY_YELLOW}"
printf "============WARNING/NOTE (1)============\n"
-printf "GCC in use: $(which gcc)\n"
-printf "GCC version info: $(gcc --version | grep gcc)\n"
-printf "GCC version number: $(gcc --version | grep gcc | awk '{print $3}')\n"
-printf "GCC-15>= is required for Hyprland\n"
-printf "If you have GCC-15>= and it's currently set then you can safely ignore this\n"
-printf "If not, you must ensure you are using the correct GCC version and set it (gcc-config )\n"
-printf "It is heavily recommended to re-emerge @world with an empty tree after changing GCC version (emerge -e @world)\n\n"
-printf "${STY_RST}"
-pause
-
-printf "${STY_YELLOW}"
-printf "============WARNING/NOTE (2)============\n"
printf "Ensure you have a global use flag for elogind or systemd in your make.conf for simplicity\n"
printf "Or you can manually add the use flags for each package that requires it\n"
printf "${STY_RST}"
pause
printf "${STY_YELLOW}"
+printf "============WARNING/NOTE (2)============\n"
printf "https://github.com/end-4/dots-hyprland/blob/main/sdata/dist-gentoo/README.md\n"
printf "Checkout the above README for potential bug fixes or additional information\n\n"
printf "${STY_RST}"
diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh
index 0fe24f0ed..423f45b81 100644
--- a/sdata/lib/functions.sh
+++ b/sdata/lib/functions.sh
@@ -64,7 +64,7 @@ function showfun(){
function pause(){
if [ ! "$ask" == "false" ];then
printf "${STY_FAINT}${STY_SLANT}"
- local p; read -p "(Ctrl-C to abort, others to proceed)" p
+ local p; read -p "(Ctrl-C to abort, Enter to proceed)" p
printf "${STY_RST}"
fi
}
diff --git a/sdata/subcmd-install/3.files-exp.yaml b/sdata/subcmd-install/3.files-exp.yaml
index 5282e0594..63a0cad1f 100644
--- a/sdata/subcmd-install/3.files-exp.yaml
+++ b/sdata/subcmd-install/3.files-exp.yaml
@@ -20,6 +20,7 @@ patterns:
- from: "dots/.config/fish"
to: "$XDG_CONFIG_HOME/fish"
mode: "sync"
+ excludes: ["conf.d"]
condition:
type: "shell"
value: "fish"
diff --git a/sdata/subcmd-install/3.files-legacy.sh b/sdata/subcmd-install/3.files-legacy.sh
index 6ba8cefc2..0a3bef85f 100644
--- a/sdata/subcmd-install/3.files-legacy.sh
+++ b/sdata/subcmd-install/3.files-legacy.sh
@@ -30,7 +30,7 @@ esac
case "${SKIP_FISH}" in
true) sleep 0;;
*)
- install_dir__sync dots/.config/fish "$XDG_CONFIG_HOME"/fish
+ install_dir__sync_exclude dots/.config/fish "$XDG_CONFIG_HOME"/fish "conf.d"
;;
esac
diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh
index 41e9972d0..57c8568e1 100644
--- a/sdata/subcmd-install/3.files.sh
+++ b/sdata/subcmd-install/3.files.sh
@@ -67,6 +67,22 @@ rsync_dir__sync(){
x mkdir -p "$(dirname ${INSTALLED_LISTFILE})"
rsync -a --delete --out-format='%i %n' "$1"/ "$2"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}"
}
+rsync_dir__sync_exclude(){
+ # NOTE: This function is only for using in other functions
+ # Same as rsync_dir__sync but with exclude patterns support
+ # Usage: rsync_dir__sync_exclude [ ...]
+ local src="$1"
+ local dest_dir="$2"
+ shift 2
+ local excludes=()
+ for pattern in "$@"; do
+ excludes+=(--exclude "$pattern")
+ done
+ x mkdir -p "$dest_dir"
+ local dest="$(realpath -se $dest_dir)"
+ x mkdir -p "$(dirname ${INSTALLED_LISTFILE})"
+ rsync -a --delete "${excludes[@]}" --out-format='%i %n' "$src"/ "$dest_dir"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}"
+}
function install_file(){
# NOTE: Do not add prefix `v` or `x` when using this function
local s=$1
@@ -124,6 +140,18 @@ function install_dir__skip_existed(){
v rsync_dir $s $t
fi
}
+function install_dir__sync_exclude(){
+ # NOTE: Do not add prefix `v` or `x` when using this function
+ # Sync directory with exclude patterns
+ # Usage: install_dir__sync_exclude [ ...]
+ local s=$1
+ local t=$2
+ shift 2
+ if [ -d $t ];then
+ warning_overwrite
+ fi
+ rsync_dir__sync_exclude $s $t "$@"
+}
function install_google_sans_flex(){
local font_name="Google Sans Flex"
local src_name="google-sans-flex"
@@ -142,7 +170,7 @@ function install_google_sans_flex(){
x fc-cache -fv
x cd $REPO_ROOT
x mkdir -p "$(dirname ${INSTALLED_LISTFILE})"
- realpath -se "$2" >> "${INSTALLED_LISTFILE}"
+ realpath -se "$target_dir" >> "${INSTALLED_LISTFILE}"
}
#####################################################################################