diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml
index 694c3af8e..b496f4d17 100644
--- a/.config/quickshell/modules/common/Appearance.qml
+++ b/.config/quickshell/modules/common/Appearance.qml
@@ -107,7 +107,7 @@ Singleton {
property color colLayer1: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerLow, m3colors.m3background, 0.8), root.contentTransparency);
property color colOnLayer1: m3colors.m3onSurfaceVariant;
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
- property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.7), root.contentTransparency)
+ property color colLayer2: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.1), root.contentTransparency)
property color colOnLayer2: m3colors.m3onSurface;
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
property color colLayer3: ColorUtils.transparentize(ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96), root.contentTransparency)
diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml
index 92d56b72c..c268f1d95 100644
--- a/.config/quickshell/modules/common/ConfigOptions.qml
+++ b/.config/quickshell/modules/common/ConfigOptions.qml
@@ -155,6 +155,10 @@ Singleton {
property string dateFormat: "dddd, dd/MM"
}
+ property QtObject windows: QtObject {
+ property bool showTitlebar: true // Client-side decoration for shell apps
+ }
+
property QtObject hacks: QtObject {
property int arbitraryRaceConditionDelay: 20 // milliseconds
}
diff --git a/.config/quickshell/modules/common/widgets/GroupButton.qml b/.config/quickshell/modules/common/widgets/GroupButton.qml
index bfa887b68..362ae39e9 100644
--- a/.config/quickshell/modules/common/widgets/GroupButton.qml
+++ b/.config/quickshell/modules/common/widgets/GroupButton.qml
@@ -16,8 +16,8 @@ Button {
id: root
property bool toggled
property string buttonText
- property real buttonRadius: Appearance?.rounding?.small ?? 4
- property real buttonRadiusPressed: buttonRadius
+ property real buttonRadius: Appearance?.rounding?.small ?? 8
+ property real buttonRadiusPressed: Appearance?.rounding?.small ?? 6
property var downAction // When left clicking (down)
property var releaseAction // When left clicking (release)
property var altAction // When right clicking
@@ -34,18 +34,6 @@ Button {
Layout.fillHeight: (clickIndex - 1 <= parentGroup.children.indexOf(root) && parentGroup.children.indexOf(root) <= clickIndex + 1)
implicitWidth: (root.down && bounce) ? clickedWidth : baseWidth
implicitHeight: (root.down && bounce) ? clickedHeight : baseHeight
-
- Behavior on implicitWidth {
- animation: Appearance.animation.clickBounce.numberAnimation.createObject(this)
- }
-
- Behavior on implicitHeight {
- animation: Appearance.animation.clickBounce.numberAnimation.createObject(this)
- }
-
- Behavior on radius {
- animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
- }
property color colBackground: ColorUtils.transparentize(Appearance?.colors.colLayer1Hover, 1) || "transparent"
property color colBackgroundHover: Appearance?.colors.colLayer1Hover ?? "#E5DFED"
@@ -55,6 +43,8 @@ Button {
property color colBackgroundToggledActive: Appearance?.colors.colPrimaryActive ?? "#D6CEE2"
property real radius: root.down ? root.buttonRadiusPressed : root.buttonRadius
+ property real leftRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius
+ property real rightRadius: root.down ? root.buttonRadiusPressed : root.buttonRadius
property color color: root.enabled ? (root.toggled ?
(root.down ? colBackgroundToggledActive :
root.hovered ? colBackgroundToggledHover :
@@ -71,6 +61,21 @@ Button {
}
}
+ Behavior on implicitWidth {
+ animation: Appearance.animation.clickBounce.numberAnimation.createObject(this)
+ }
+
+ Behavior on implicitHeight {
+ animation: Appearance.animation.clickBounce.numberAnimation.createObject(this)
+ }
+
+ Behavior on leftRadius {
+ animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ }
+ Behavior on rightRadius {
+ animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
+ }
+
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
@@ -100,7 +105,10 @@ Button {
background: Rectangle {
id: buttonBackground
- radius: root.radius
+ topLeftRadius: root.leftRadius
+ topRightRadius: root.rightRadius
+ bottomLeftRadius: root.leftRadius
+ bottomRightRadius: root.rightRadius
implicitHeight: 50
color: root.color
diff --git a/.config/quickshell/scripts/colors/random_konachan_wall.sh b/.config/quickshell/scripts/colors/random_konachan_wall.sh
new file mode 100755
index 000000000..52853d090
--- /dev/null
+++ b/.config/quickshell/scripts/colors/random_konachan_wall.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+
+mkdir -p ~/Pictures/Wallpapers
+page=$((1 + RANDOM % 1000));
+response=$(curl "https://konachan.com/post.json?tags=rating%3Asafe&limit=1&page=$page")
+link=$(echo "$response" | jq '.[0].file_url' -r);
+ext=$(echo "$link" | awk -F. '{print $NF}')
+downloadPath="$HOME/Pictures/Wallpapers/konachan_random_image.$ext"
+curl "$link" -o "$downloadPath"
+~/.config/quickshell/scripts/colors/switchwall.sh --image "$downloadPath"
diff --git a/.config/quickshell/scripts/colors/switchwall.sh b/.config/quickshell/scripts/colors/switchwall.sh
index 4136772f2..0a633e242 100755
--- a/.config/quickshell/scripts/colors/switchwall.sh
+++ b/.config/quickshell/scripts/colors/switchwall.sh
@@ -291,6 +291,10 @@ main() {
shift
fi
;;
+ --image)
+ imgpath="$2"
+ shift 2
+ ;;
--noswitch)
noswitch_flag="1"
imgpath=$(swww query | awk -F 'image: ' '{print $2}')
diff --git a/.config/quickshell/services/FirstRunExperience.qml b/.config/quickshell/services/FirstRunExperience.qml
index e28d4b49b..86ccb98b8 100644
--- a/.config/quickshell/services/FirstRunExperience.qml
+++ b/.config/quickshell/services/FirstRunExperience.qml
@@ -27,7 +27,7 @@ Singleton {
}
function handleFirstRun() {
- Hyprland.dispatch(`exec '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`)
+ Hyprland.dispatch(`exec swww query | grep 'image' || '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`)
Hyprland.dispatch(`exec qs -p '${root.welcomeQmlPath}'`)
}
diff --git a/.config/quickshell/services/Notifications.qml b/.config/quickshell/services/Notifications.qml
index 75033292c..dc2d2206d 100644
--- a/.config/quickshell/services/Notifications.qml
+++ b/.config/quickshell/services/Notifications.qml
@@ -217,8 +217,10 @@ Singleton {
const action = notifServerNotif.actions.find((action) => action.identifier === notifIdentifier);
action.invoke()
}
- // else console.log("Notification not found in server: " + id)
- // root.discard(id);
+ else {
+ console.log("Notification not found in server: " + id)
+ root.discardNotification(id);
+ }
}
function triggerListChange() {
diff --git a/.config/quickshell/welcome.qml b/.config/quickshell/welcome.qml
index dc8559dfd..cbc5b4a32 100644
--- a/.config/quickshell/welcome.qml
+++ b/.config/quickshell/welcome.qml
@@ -11,6 +11,7 @@ import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import Quickshell
+import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
@@ -25,7 +26,6 @@ ApplicationWindow {
property string firstRunFileContent: "This file is just here to confirm you've been greeted :>"
property real contentPadding: 5
property bool showNextTime: false
-
visible: true
onClosing: Qt.quit()
title: "illogical-impulse Welcome"
@@ -38,16 +38,44 @@ ApplicationWindow {
minimumWidth: 600
minimumHeight: 400
width: 800
- height: 600
+ height: 650
color: Appearance.m3colors.m3background
+ Process {
+ id: konachanWallProc
+ property string status: ""
+ command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/random_konachan_wall.sh`)]
+ stdout: SplitParser {
+ onRead: data => {
+ console.log(`Konachan wall proc output: ${data}`);
+ konachanWallProc.status = data.trim();
+ }
+ }
+ }
+
+ component SelectionConnectedButton: GroupButton {
+ id: selectionConnectedButtonRoot
+ horizontalPadding: 12
+ verticalPadding: 8
+ bounce: false
+ property bool leftmost: false
+ property bool rightmost: false
+ leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
+ rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
+ colBackground: Appearance.colors.colSecondaryContainer
+ contentItem: StyledText {
+ color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
+ text: selectionConnectedButtonRoot.buttonText
+ }
+ }
+
component Section: ColumnLayout {
id: sectionRoot
property string title
default property alias data: sectionContent.data
Layout.fillWidth: true
- spacing: 10
+ spacing: 8
StyledText {
text: sectionRoot.title
font.pixelSize: Appearance.font.pixelSize.larger
@@ -58,6 +86,56 @@ ApplicationWindow {
}
}
+ component ButtonWithIcon: RippleButton {
+ id: buttonWithIconRoot
+ property string nerdIcon
+ property string iconText
+ property string mainText: "Button text"
+ property Component mainContentComponent: Component {
+ StyledText {
+ text: buttonWithIconRoot.mainText
+ font.pixelSize: Appearance.font.pixelSize.small
+ color: Appearance.colors.colOnSecondaryContainer
+ }
+ }
+ implicitHeight: 35
+ horizontalPadding: 15
+ buttonRadius: Appearance.rounding.small
+ colBackground: Appearance.colors.colLayer2
+
+ contentItem: RowLayout {
+ Item {
+ implicitWidth: Math.max(materialIconLoader.implicitWidth, nerdIconLoader.implicitWidth)
+ Loader {
+ id: materialIconLoader
+ anchors.centerIn: parent
+ active: !nerdIcon
+ sourceComponent: MaterialSymbol {
+ text: buttonWithIconRoot.iconText
+ iconSize: Appearance.font.pixelSize.larger
+ color: Appearance.colors.colOnSecondaryContainer
+ fill: 1
+ }
+ }
+ Loader {
+ id: nerdIconLoader
+ anchors.centerIn: parent
+ active: nerdIcon
+ sourceComponent: StyledText {
+ text: buttonWithIconRoot.nerdIcon
+ font.pixelSize: Appearance.font.pixelSize.larger
+ font.family: Appearance.font.family.iconNerd
+ color: Appearance.colors.colOnSecondaryContainer
+ }
+ }
+ }
+ Loader {
+ sourceComponent: buttonWithIconRoot.mainContentComponent
+ Layout.alignment: Qt.AlignVCenter
+ }
+ }
+ }
+
component LightDarkPrefButton: GroupButton {
id: lightDarkButtonRoot
required property bool dark
@@ -178,14 +256,16 @@ ApplicationWindow {
fill: parent
margins: contentPadding
}
+
Item {
+ visible: ConfigOptions?.windows.showTitlebar
Layout.fillWidth: true
implicitHeight: Math.max(welcomeText.implicitHeight, windowControlsRow.implicitHeight)
StyledText {
id: welcomeText
anchors.centerIn: parent
color: Appearance.colors.colOnLayer0
- text: "Welcome"
+ text: "Yooooo hi there"
font.pixelSize: Appearance.font.pixelSize.hugeass
font.family: Appearance.font.family.title
}
@@ -232,69 +312,256 @@ ApplicationWindow {
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.windowRounding - root.contentPadding
- ColumnLayout {
- id: contentColumn
- anchors {
- top: parent.top
- bottom: parent.bottom
- horizontalCenter: parent.horizontalCenter
- margins: 10
- }
- spacing: 20
+ Flickable {
+ clip: true
+ anchors.fill: parent
+ contentHeight: contentColumn.implicitHeight
+ implicitWidth: contentColumn.implicitWidth
+ ColumnLayout {
+ id: contentColumn
+ anchors {
+ top: parent.top
+ bottom: parent.bottom
+ horizontalCenter: parent.horizontalCenter
+ margins: 10
+ }
+ spacing: 20
- Section {
- title: "Customize"
+ Section {
+ title: "Style & wallpaper"
- ButtonGroup {
+ ButtonGroup {
+ Layout.fillWidth: true
+ LightDarkPrefButton {
+ dark: false
+ }
+ LightDarkPrefButton {
+ dark: true
+ }
+ }
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ ButtonWithIcon {
+ id: rndWallBtn
+ Layout.alignment: Qt.AlignHCenter
+ buttonRadius: Appearance.rounding.small
+ iconText: "wallpaper"
+ mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
+ onClicked: {
+ console.log(konachanWallProc.command.join(" "))
+ konachanWallProc.running = true;
+ }
+ }
+ ButtonWithIcon {
+ iconText: "wallpaper"
+ onClicked: {
+ Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath}`)
+ }
+ mainContentComponent: Component {
+ RowLayout {
+ spacing: 10
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ text: "Choose file"
+ color: Appearance.colors.colOnSecondaryContainer
+ }
+ RowLayout {
+ spacing: 3
+ KeyboardKey {
+ key: "Ctrl"
+ }
+ KeyboardKey {
+ key: ""
+ }
+ StyledText {
+ Layout.alignment: Qt.AlignVCenter
+ text: "+"
+ }
+ KeyboardKey {
+ key: "T"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ StyledText {
+ Layout.alignment: Qt.AlignHCenter
+ text: "Change any time later with /dark, /light, /img in the launcher"
+ font.pixelSize: Appearance.font.pixelSize.smaller
+ color: Appearance.colors.colSubtext
+ }
+ }
+
+ Section {
+ title: "Policies"
+
+ RowLayout {
+ Layout.alignment: Qt.AlignHCenter
+ spacing: 15
+ ColumnLayout { // Weeb policy
+ StyledText {
+ text: "Weeb"
+ color: Appearance.colors.colSubtext
+ }
+ ButtonGroup {
+ id: weebPolicyBtnGroup
+ property int selectedPolicy: ConfigOptions.policies.weeb
+ spacing: 2
+ SelectionConnectedButton {
+ property int value: 0
+ leftmost: true
+ buttonText: "No"
+ toggled: (weebPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.weeb", value);
+ }
+ }
+ SelectionConnectedButton {
+ property int value: 1
+ buttonText: "Yes"
+ toggled: (weebPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.weeb", value);
+ }
+ }
+ SelectionConnectedButton {
+ property int value: 2
+ rightmost: true
+ buttonText: "Closet"
+ toggled: (weebPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.weeb", value);
+ }
+ }
+ }
+ }
+ ColumnLayout { // AI policy
+ StyledText {
+ text: "AI"
+ color: Appearance.colors.colSubtext
+ }
+ ButtonGroup {
+ id: aiPolicyBtnGroup
+ property int selectedPolicy: ConfigOptions.policies.ai
+ spacing: 2
+ SelectionConnectedButton {
+ property int value: 0
+ leftmost: true
+ buttonText: "No"
+ toggled: (aiPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.ai", value);
+ }
+ }
+ SelectionConnectedButton {
+ property int value: 1
+ buttonText: "Yes"
+ toggled: (aiPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.ai", value);
+ }
+ }
+ SelectionConnectedButton {
+ property int value: 2
+ rightmost: true
+ buttonText: "Local only"
+ toggled: (aiPolicyBtnGroup.selectedPolicy === value)
+ onClicked: {
+ ConfigLoader.setConfigValueAndSave("policies.ai", value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Section {
+ title: "Info"
+
+ Flow {
+ Layout.fillWidth: true
+ spacing: 10
+
+ ButtonWithIcon {
+ iconText: "keyboard_alt"
+ onClicked: {
+ Hyprland.dispatch("global quickshell:cheatsheetOpen")
+ }
+ mainContentComponent: Component {
+ RowLayout {
+ spacing: 10
+ StyledText {
+ font.pixelSize: Appearance.font.pixelSize.small
+ text: "Keybinds"
+ color: Appearance.colors.colOnSecondaryContainer
+ }
+ RowLayout {
+ spacing: 3
+ KeyboardKey {
+ key: ""
+ }
+ StyledText {
+ Layout.alignment: Qt.AlignVCenter
+ text: "+"
+ }
+ KeyboardKey {
+ key: "/"
+ }
+ }
+ }
+ }
+ }
+
+ ButtonWithIcon {
+ iconText: "help"
+ mainText: "Usage"
+ onClicked: {
+ Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
+ }
+ }
+ ButtonWithIcon {
+ iconText: "construction"
+ mainText: "Configuration"
+ onClicked: {
+ Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/")
+ }
+ }
+ }
+ }
+
+ Section {
+ title: "Useless buttons"
+
+ Flow {
+ Layout.fillWidth: true
+ spacing: 10
+
+ ButtonWithIcon {
+ nerdIcon: ""
+ mainText: "GitHub"
+ onClicked: {
+ Qt.openUrlExternally("https://github.com/end-4/dots-hyprland")
+ }
+ }
+ ButtonWithIcon {
+ iconText: "favorite"
+ mainText: "Funny number"
+ onClicked: {
+ Qt.openUrlExternally("https://github.com/sponsors/end-4")
+ }
+ }
+ }
+ }
+
+ Item {
Layout.fillWidth: true
- LightDarkPrefButton {
- dark: false
- }
- LightDarkPrefButton {
- dark: true
- }
+ Layout.fillHeight: true
}
+
}
-
- Section {
- title: "Info"
-
- RippleButton {
- implicitHeight: 35
- horizontalPadding: 10
- // buttonRadius: Appearance.rounding.full
- colBackground: Appearance.colors.colSecondaryContainer
- colBackgroundHover: Appearance.colors.colSecondaryContainerHover
- colRipple: Appearance.colors.colSecondaryContainerActive
-
- onClicked: {
- Hyprland.dispatch("global quickshell:cheatsheetOpen")
- }
-
- contentItem: RowLayout {
- KeyboardKey {
- key: ""
- }
- StyledText {
- Layout.alignment: Qt.AlignVCenter
- text: "+"
- }
- KeyboardKey {
- key: "/"
- }
- StyledText {
- text: "Open keybind cheatsheet"
- color: Appearance.colors.colOnSecondaryContainer
- }
- }
- }
- }
-
- Item {
- Layout.fillWidth: true
- Layout.fillHeight: true
- }
-
}
}
}
diff --git a/README.md b/README.md
index d65c5ce9d..e381b1251 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,13 @@
- For a more comprehensive list of dependencies, see [scriptdata/dependencies.conf](https://github.com/end-4/dots-hyprland/blob/main/scriptdata/dependencies.conf)
+Logo ideas welcome
+
+ - See [#1436](https://github.com/end-4/dots-hyprland/issues/1436)
+
+