From e567f06cefbadc3f2313669c4bba809033cba350 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 14:38:16 +0300 Subject: [PATCH 01/57] initial commit of record-location --- .../quickshell/ii/modules/common/Config.qml | 4 + .../ii/modules/settings/ServicesConfig.qml | 19 +++ .../quickshell/ii/scripts/videos/record.sh | 110 ++++++++++-------- 3 files changed, 85 insertions(+), 48 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 39c0ef131..042d10639 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -460,6 +460,10 @@ Singleton { } } + property JsonObject screenRecord: JsonObject { + property string savePath: Directories.videos + } + property JsonObject sounds: JsonObject { property bool battery: false property bool pomodoro: false diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index c9ff0c16b..778ff2b84 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -85,6 +85,25 @@ ContentPage { } + ContentSection { + icon: "screen_record" + title: Translation.tr("Screen Recording") + + ContentSubsection { + title: Translation.tr("Save path") + " (example: /home/user/Videos)" + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Path") + text: Config.options.screenRecord.savePath + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.screenRecord.savePath = text; + } + } + } + + } + ContentSection { icon: "search" title: Translation.tr("Search") diff --git a/dots/.config/quickshell/ii/scripts/videos/record.sh b/dots/.config/quickshell/ii/scripts/videos/record.sh index 794bcf1ba..f6410838c 100755 --- a/dots/.config/quickshell/ii/scripts/videos/record.sh +++ b/dots/.config/quickshell/ii/scripts/videos/record.sh @@ -1,21 +1,35 @@ #!/usr/bin/env bash +CONFIG_FILE="$HOME/.config/illogical-impulse/config.json" +JSON_PATH=".screenRecord.savePath" + +CUSTOM_PATH=$(jq -r "$JSON_PATH" "$CONFIG_FILE" 2>/dev/null) + +RECORDING_DIR="" + +if [[ -n "$CUSTOM_PATH" ]]; then + RECORDING_DIR="$CUSTOM_PATH" +else + RECORDING_DIR="$HOME/Videos" +fi + getdate() { - date '+%Y-%m-%d_%H.%M.%S' + date '+%Y-%m-%d_%H.%M.%S' } getaudiooutput() { - pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2 + pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2 } getactivemonitor() { - hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name' + hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name' } -xdgvideo="$(xdg-user-dir VIDEOS)" -if [[ $xdgvideo = "$HOME" ]]; then - unset xdgvideo -fi -mkdir -p "${xdgvideo:-$HOME/Videos}" -cd "${xdgvideo:-$HOME/Videos}" || exit +# ORİJİNAL XDG MANTIĞI SİLİNDİ, YERİNE SADECE YENİ DEĞİŞKEN KULLANILDI +# xdgvideo="$(xdg-user-dir VIDEOS)" +# if $xdgvideo = "$HOME" ]]; then +# unset xdgvideo +# fi +mkdir -p "$RECORDING_DIR" +cd "$RECORDING_DIR" || exit # parse --region without modifying $@ so other flags like --fullscreen still work ARGS=("$@") @@ -23,47 +37,47 @@ MANUAL_REGION="" SOUND_FLAG=0 FULLSCREEN_FLAG=0 for ((i=0;i<${#ARGS[@]};i++)); do - if [[ "${ARGS[i]}" == "--region" ]]; then - if (( i+1 < ${#ARGS[@]} )); then - MANUAL_REGION="${ARGS[i+1]}" - else - notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown - exit 1 - fi - elif [[ "${ARGS[i]}" == "--sound" ]]; then - SOUND_FLAG=1 - elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then - FULLSCREEN_FLAG=1 - fi + if [[ "${ARGS[i]}" == "--region" ]]; then + if (( i+1 < ${#ARGS[@]} )); then + MANUAL_REGION="${ARGS[i+1]}" + else + notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown + exit 1 + fi + elif [[ "${ARGS[i]}" == "--sound" ]]; then + SOUND_FLAG=1 + elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then + FULLSCREEN_FLAG=1 + fi done if pgrep wf-recorder > /dev/null; then - notify-send "Recording Stopped" "Stopped" -a 'Recorder' & - pkill wf-recorder & + notify-send "Recording Stopped" "Stopped" -a 'Recorder' & + pkill wf-recorder & else - if [[ $FULLSCREEN_FLAG -eq 1 ]]; then - notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown - if [[ $SOUND_FLAG -eq 1 ]]; then - wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" - else - wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t - fi - else - # If a manual region was provided via --region, use it; otherwise run slurp as before. - if [[ -n "$MANUAL_REGION" ]]; then - region="$MANUAL_REGION" - else - if ! region="$(slurp 2>&1)"; then - notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown - exit 1 - fi - fi + if [[ $FULLSCREEN_FLAG -eq 1 ]]; then + notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown + if [[ $SOUND_FLAG -eq 1 ]]; then + wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" + else + wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t + fi + else + # If a manual region was provided via --region, use it; otherwise run slurp as before. + if [[ -n "$MANUAL_REGION" ]]; then + region="$MANUAL_REGION" + else + if ! region="$(slurp 2>&1)"; then + notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown + exit 1 + fi + fi - notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown - if [[ $SOUND_FLAG -eq 1 ]]; then - wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)" - else - wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" - fi - fi -fi + notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown + if [[ $SOUND_FLAG -eq 1 ]]; then + wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)" + else + wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" + fi + fi +fi \ No newline at end of file From 0fabedb0c4a445bcab33fe4f693f4d6f9c954f94 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 14:41:29 +0300 Subject: [PATCH 02/57] tweaks in script --- .../quickshell/ii/scripts/videos/record.sh | 91 +++++++++---------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/dots/.config/quickshell/ii/scripts/videos/record.sh b/dots/.config/quickshell/ii/scripts/videos/record.sh index f6410838c..bf0ab504d 100755 --- a/dots/.config/quickshell/ii/scripts/videos/record.sh +++ b/dots/.config/quickshell/ii/scripts/videos/record.sh @@ -8,26 +8,21 @@ CUSTOM_PATH=$(jq -r "$JSON_PATH" "$CONFIG_FILE" 2>/dev/null) RECORDING_DIR="" if [[ -n "$CUSTOM_PATH" ]]; then - RECORDING_DIR="$CUSTOM_PATH" + RECORDING_DIR="$CUSTOM_PATH" else - RECORDING_DIR="$HOME/Videos" + RECORDING_DIR="$HOME/Videos" # Use default path fi getdate() { - date '+%Y-%m-%d_%H.%M.%S' + date '+%Y-%m-%d_%H.%M.%S' } getaudiooutput() { - pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2 + pactl list sources | grep 'Name' | grep 'monitor' | cut -d ' ' -f2 } getactivemonitor() { - hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name' + hyprctl monitors -j | jq -r '.[] | select(.focused == true) | .name' } -# ORİJİNAL XDG MANTIĞI SİLİNDİ, YERİNE SADECE YENİ DEĞİŞKEN KULLANILDI -# xdgvideo="$(xdg-user-dir VIDEOS)" -# if $xdgvideo = "$HOME" ]]; then -# unset xdgvideo -# fi mkdir -p "$RECORDING_DIR" cd "$RECORDING_DIR" || exit @@ -37,47 +32,47 @@ MANUAL_REGION="" SOUND_FLAG=0 FULLSCREEN_FLAG=0 for ((i=0;i<${#ARGS[@]};i++)); do - if [[ "${ARGS[i]}" == "--region" ]]; then - if (( i+1 < ${#ARGS[@]} )); then - MANUAL_REGION="${ARGS[i+1]}" - else - notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown - exit 1 - fi - elif [[ "${ARGS[i]}" == "--sound" ]]; then - SOUND_FLAG=1 - elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then - FULLSCREEN_FLAG=1 - fi + if [[ "${ARGS[i]}" == "--region" ]]; then + if (( i+1 < ${#ARGS[@]} )); then + MANUAL_REGION="${ARGS[i+1]}" + else + notify-send "Recording cancelled" "No region specified for --region" -a 'Recorder' & disown + exit 1 + fi + elif [[ "${ARGS[i]}" == "--sound" ]]; then + SOUND_FLAG=1 + elif [[ "${ARGS[i]}" == "--fullscreen" ]]; then + FULLSCREEN_FLAG=1 + fi done if pgrep wf-recorder > /dev/null; then - notify-send "Recording Stopped" "Stopped" -a 'Recorder' & - pkill wf-recorder & + notify-send "Recording Stopped" "Stopped" -a 'Recorder' & + pkill wf-recorder & else - if [[ $FULLSCREEN_FLAG -eq 1 ]]; then - notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown - if [[ $SOUND_FLAG -eq 1 ]]; then - wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" - else - wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t - fi - else - # If a manual region was provided via --region, use it; otherwise run slurp as before. - if [[ -n "$MANUAL_REGION" ]]; then - region="$MANUAL_REGION" - else - if ! region="$(slurp 2>&1)"; then - notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown - exit 1 - fi - fi + if [[ $FULLSCREEN_FLAG -eq 1 ]]; then + notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown + if [[ $SOUND_FLAG -eq 1 ]]; then + wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --audio="$(getaudiooutput)" + else + wf-recorder -o "$(getactivemonitor)" --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t + fi + else + # If a manual region was provided via --region, use it; otherwise run slurp as before. + if [[ -n "$MANUAL_REGION" ]]; then + region="$MANUAL_REGION" + else + if ! region="$(slurp 2>&1)"; then + notify-send "Recording cancelled" "Selection was cancelled" -a 'Recorder' & disown + exit 1 + fi + fi - notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown - if [[ $SOUND_FLAG -eq 1 ]]; then - wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)" - else - wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" - fi - fi + notify-send "Starting recording" 'recording_'"$(getdate)"'.mp4' -a 'Recorder' & disown + if [[ $SOUND_FLAG -eq 1 ]]; then + wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" --audio="$(getaudiooutput)" + else + wf-recorder --pixel-format yuv420p -f './recording_'"$(getdate)"'.mp4' -t --geometry "$region" + fi + fi fi \ No newline at end of file From ace8802480227850ab528d7e171288328e7786a9 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 15:55:46 +0300 Subject: [PATCH 03/57] add screenshot location --- .../quickshell/ii/modules/common/Config.qml | 4 ++++ .../regionSelector/RegionSelection.qml | 22 ++++++++++++++++++- .../ii/modules/settings/ServicesConfig.qml | 19 +++++++++++++--- .../quickshell/ii/services/DateTime.qml | 1 + 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 042d10639..db8171b45 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -464,6 +464,10 @@ Singleton { property string savePath: Directories.videos } + property JsonObject screenSnip: JsonObject { + property string savePath: "/home/vaguesyntax/Pictures" + } + property JsonObject sounds: JsonObject { property bool battery: false property bool pomodoro: false diff --git a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml index 5ecce47ba..53ccfaf34 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml @@ -33,6 +33,10 @@ PanelWindow { property var selectionMode: RegionSelection.SelectionMode.RectCorners signal dismiss() + property string permanentScreenshotDir: Config.options.screenSnip.savePath && Config.options.screenSnip.savePath !== "" + ? Config.options.screenSnip.savePath + : "" + property string screenshotDir: Directories.screenshotTemp property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl property string fileUploadApiEndpoint: "https://uguu.se/upload" @@ -258,8 +262,24 @@ PanelWindow { } switch (root.action) { case RegionSelection.SnipAction.Copy: - snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] + if (permanentScreenshotDir === "") { + // no permanent dir, just copy to clipboard + snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] + break; + } + const saveFileName = 'screenshot-' + DateTime.fileDateTime + '.png' + const savePath = `${root.permanentScreenshotDir}/${saveFileName}` + + snipProc.command = [ + "bash", "-c", + `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.permanentScreenshotDir)}' ` + + `&& ${cropToStdout} | tee >(wl-copy) > '${StringUtils.shellSingleQuoteEscape(savePath)}' ` + + `&& ${cleanup}` + ] break; + + //snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] + //break; case RegionSelection.SnipAction.Edit: snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`] break; diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index 778ff2b84..5511a6bad 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -86,11 +86,11 @@ ContentPage { } ContentSection { - icon: "screen_record" - title: Translation.tr("Screen Recording") + icon: "file_open" + title: Translation.tr("Save paths") ContentSubsection { - title: Translation.tr("Save path") + " (example: /home/user/Videos)" + title: Translation.tr("Video save path") MaterialTextArea { Layout.fillWidth: true placeholderText: Translation.tr("Path") @@ -101,6 +101,19 @@ ContentPage { } } } + + ContentSubsection { + title: Translation.tr("Screenshot save path") + " (leave empty to just copy)" + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Path") + text: Config.options.screenSnip.savePath + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.screenSnip.savePath = text; + } + } + } } diff --git a/dots/.config/quickshell/ii/services/DateTime.qml b/dots/.config/quickshell/ii/services/DateTime.qml index 62d296dbc..c6998f056 100644 --- a/dots/.config/quickshell/ii/services/DateTime.qml +++ b/dots/.config/quickshell/ii/services/DateTime.qml @@ -22,6 +22,7 @@ Singleton { property string shortDate: Qt.locale().toString(clock.date, Config.options?.time.shortDateFormat ?? "dd/MM") property string date: Qt.locale().toString(clock.date, Config.options?.time.dateFormat ?? "dddd, dd/MM") property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy") + property string fileDateTime: Qt.locale().toString(clock.date, "dd-MM-yyyy_HH.mm") property string uptime: "0h, 0m" Timer { From 0c587415eadd3081c968d061fd1b6f921d2ee888 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 16:00:32 +0300 Subject: [PATCH 04/57] tweaks in settings/config --- .../quickshell/ii/modules/common/Config.qml | 2 +- .../ii/modules/settings/ServicesConfig.qml | 39 ++++++++----------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index db8171b45..8a3b51c73 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -465,7 +465,7 @@ Singleton { } property JsonObject screenSnip: JsonObject { - property string savePath: "/home/vaguesyntax/Pictures" + property string savePath: "" // only copy to clipboard when empty } property JsonObject sounds: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index 5511a6bad..3f5f25f45 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -89,32 +89,25 @@ ContentPage { icon: "file_open" title: Translation.tr("Save paths") - ContentSubsection { - title: Translation.tr("Video save path") - MaterialTextArea { - Layout.fillWidth: true - placeholderText: Translation.tr("Path") - text: Config.options.screenRecord.savePath - wrapMode: TextEdit.Wrap - onTextChanged: { - Config.options.screenRecord.savePath = text; - } - } - } - - ContentSubsection { - title: Translation.tr("Screenshot save path") + " (leave empty to just copy)" - MaterialTextArea { - Layout.fillWidth: true - placeholderText: Translation.tr("Path") - text: Config.options.screenSnip.savePath - wrapMode: TextEdit.Wrap - onTextChanged: { - Config.options.screenSnip.savePath = text; - } + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Video Recording Path") + text: Config.options.screenRecord.savePath + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.screenRecord.savePath = text; } } + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Screenshot Path (leave empyt to just copy)") + text: Config.options.screenSnip.savePath + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.screenSnip.savePath = text; + } + } } ContentSection { From 6afa6d2142acc019ea921f0340bffbf0548165b0 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 16:04:04 +0300 Subject: [PATCH 05/57] remove reduntant code --- .../quickshell/ii/modules/regionSelector/RegionSelection.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml index 53ccfaf34..9d984d5ec 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml @@ -277,9 +277,6 @@ PanelWindow { `&& ${cleanup}` ] break; - - //snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] - //break; case RegionSelection.SnipAction.Edit: snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`] break; From a240329f22822a927a8ee757b19d9395bec7a4ca Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 1 Nov 2025 16:18:02 +0300 Subject: [PATCH 06/57] fix: initialize recording path setting properly --- dots/.config/quickshell/ii/modules/common/Config.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 8a3b51c73..10eaa0126 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -461,7 +461,7 @@ Singleton { } property JsonObject screenRecord: JsonObject { - property string savePath: Directories.videos + property string savePath: Directories.videos.replace("file://","") // strip "file://" } property JsonObject screenSnip: JsonObject { From 8e1a3d26b1de458ecee8d5040ffec0ca21baf9a7 Mon Sep 17 00:00:00 2001 From: Vague Syntax Date: Sat, 1 Nov 2025 17:56:38 +0300 Subject: [PATCH 07/57] Update dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml Co-authored-by: Madjid Taha <1833954+madjidtaha@users.noreply.github.com> --- dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index 3f5f25f45..f5931bb1b 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -101,7 +101,7 @@ ContentPage { MaterialTextArea { Layout.fillWidth: true - placeholderText: Translation.tr("Screenshot Path (leave empyt to just copy)") + placeholderText: Translation.tr("Screenshot Path (leave empty to just copy)") text: Config.options.screenSnip.savePath wrapMode: TextEdit.Wrap onTextChanged: { From b267b74e8be45ae6a3737cc0b9c04be5280a227f Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Sat, 1 Nov 2025 20:50:39 +0100 Subject: [PATCH 08/57] Added battery health and made it configurable --- .../ii/modules/bar/BatteryPopup.qml | 23 +++++++++++++++++++ .../quickshell/ii/modules/common/Config.qml | 1 + .../ii/scripts/battery/calculate_health.sh | 11 +++++++++ .../quickshell/ii/services/Battery.qml | 23 +++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 dots/.config/quickshell/ii/scripts/battery/calculate_health.sh diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 5bc07d18c..72523393d 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -63,6 +63,29 @@ StyledPopup { text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") color: Appearance.colors.colOnSurfaceVariant } + RowLayout { + spacing: 5 + visible: Config.options.battery.showHealth + Layout.fillWidth: true + + MaterialSymbol { + text: "healing" + color: Appearance.colors.colOnSurfaceVariant + iconSize: Appearance.font.pixelSize.large + } + + StyledText { + text: Translation.tr("Health:") + color: Appearance.colors.colOnSurfaceVariant + } + + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + color: Appearance.colors.colOnSurfaceVariant + text: `${(Battery.health).toFixed(1)}%` + } + } StyledText { Layout.fillWidth: true horizontalAlignment: Text.AlignRight diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 39c0ef131..854a059ab 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -257,6 +257,7 @@ Singleton { property int full: 101 property bool automaticSuspend: true property int suspend: 3 + property bool showHealth: true } property JsonObject conflictKiller: JsonObject { diff --git a/dots/.config/quickshell/ii/scripts/battery/calculate_health.sh b/dots/.config/quickshell/ii/scripts/battery/calculate_health.sh new file mode 100644 index 000000000..58a4b1d4d --- /dev/null +++ b/dots/.config/quickshell/ii/scripts/battery/calculate_health.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# This just guesses that the BAT0 is the actuall battery +full=$(cat /sys/class/power_supply/BAT0/charge_full) +design=$(cat /sys/class/power_supply/BAT0/charge_full_design) + +if [ "$design" -gt 0 ]; then # basically if design > 0 + awk "BEGIN { printf \"%.1f\n\", ($full/$design)*100 }" +else + echo "Error" +fi \ No newline at end of file diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index b07bd5305..8ff75da10 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -31,6 +31,29 @@ Singleton { property real timeToEmpty: UPower.displayDevice.timeToEmpty property real timeToFull: UPower.displayDevice.timeToFull + property real health: 0 + Process { + id: batteryProcess + running: Config.options.battery.showHealth + command: [ + "bash", + `${FileUtils.trimFileProtocol(Directories.scriptPath)}/battery/calculate-health.sh` + ] + + stdout: StdioCollector { + onStreamFinished: { + const output = text.trim() + const value = Number(output) + if (!isNaN(value)) { + root.health = value + console.log("Battery health:", value) + } else { + console.warn("Battery script output invalid:", output) + } + } + } + } + onIsLowAndNotChargingChanged: { if (!root.available || !isLowAndNotCharging) return; Quickshell.execDetached([ From ca7d6c8ae0ded1b5678c0baeb55f5503bcc69ef2 Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Sat, 1 Nov 2025 22:11:39 +0100 Subject: [PATCH 09/57] Added config toggle to settings --- .../quickshell/ii/modules/settings/GeneralConfig.qml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml index c2ea1f930..3be552b9c 100644 --- a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml @@ -127,6 +127,14 @@ ContentPage { } } } + ConfigSwitch { + buttonIcon: "healing" + text: Translation.tr('Show battery health in popup') + checked: Config.options.battery.showHealth + onCheckedChanged: { + Config.options.battery.showHealth = checked; + } + } } ContentSection { From 6676d5844bd5db542e491c1027ccebce30efc293 Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Sat, 1 Nov 2025 22:22:58 +0100 Subject: [PATCH 10/57] changed the script to the right name, added some info and removed unnecesary debug --- .../battery/{calculate_health.sh => calculate-health.sh} | 7 ++++++- dots/.config/quickshell/ii/services/Battery.qml | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) rename dots/.config/quickshell/ii/scripts/battery/{calculate_health.sh => calculate-health.sh} (53%) diff --git a/dots/.config/quickshell/ii/scripts/battery/calculate_health.sh b/dots/.config/quickshell/ii/scripts/battery/calculate-health.sh similarity index 53% rename from dots/.config/quickshell/ii/scripts/battery/calculate_health.sh rename to dots/.config/quickshell/ii/scripts/battery/calculate-health.sh index 58a4b1d4d..83b52a5a0 100644 --- a/dots/.config/quickshell/ii/scripts/battery/calculate_health.sh +++ b/dots/.config/quickshell/ii/scripts/battery/calculate-health.sh @@ -1,6 +1,7 @@ #!/bin/bash # This just guesses that the BAT0 is the actuall battery +# because BAT0 is the default for modern systems full=$(cat /sys/class/power_supply/BAT0/charge_full) design=$(cat /sys/class/power_supply/BAT0/charge_full_design) @@ -8,4 +9,8 @@ if [ "$design" -gt 0 ]; then # basically if design > 0 awk "BEGIN { printf \"%.1f\n\", ($full/$design)*100 }" else echo "Error" -fi \ No newline at end of file +fi + +# If you have any issues try to find your "real" battery. +# Run 'ls /sys/class/power_supply'. You may see BATM or BAT1 and no BAT0 +# You can check what is what my running 'cat /sys/class/power_supply/{whatever_you_got}/model_name' diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index 8ff75da10..caa0ab9fe 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -46,7 +46,6 @@ Singleton { const value = Number(output) if (!isNaN(value)) { root.health = value - console.log("Battery health:", value) } else { console.warn("Battery script output invalid:", output) } From 0e63e698f26a3c8afc22a8f0708a6c3895a46944 Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Sat, 1 Nov 2025 23:26:36 +0100 Subject: [PATCH 11/57] Wrong position and forgot an import --- .../ii/modules/bar/BatteryPopup.qml | 43 ++++++++++--------- .../quickshell/ii/services/Battery.qml | 1 + 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 72523393d..bba4c422c 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -63,7 +63,28 @@ StyledPopup { text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") color: Appearance.colors.colOnSurfaceVariant } - RowLayout { + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + color: Appearance.colors.colOnSurfaceVariant + text: { + function formatTime(seconds) { + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds % 3600) / 60); + if (h > 0) + return `${h}h, ${m}m`; + else + return `${m}m`; + } + if (Battery.isCharging) + return formatTime(Battery.timeToFull); + else + return formatTime(Battery.timeToEmpty); + } + } + } + + RowLayout { spacing: 5 visible: Config.options.battery.showHealth Layout.fillWidth: true @@ -86,26 +107,6 @@ StyledPopup { text: `${(Battery.health).toFixed(1)}%` } } - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: { - function formatTime(seconds) { - var h = Math.floor(seconds / 3600); - var m = Math.floor((seconds % 3600) / 60); - if (h > 0) - return `${h}h, ${m}m`; - else - return `${m}m`; - } - if (Battery.isCharging) - return formatTime(Battery.timeToFull); - else - return formatTime(Battery.timeToEmpty); - } - } - } RowLayout { spacing: 5 diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index caa0ab9fe..a3f45ac77 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -6,6 +6,7 @@ import Quickshell import Quickshell.Services.UPower import QtQuick import Quickshell.Io +import qs.modules.common.functions Singleton { id: root From 8a208242663868464e696ecfce0e0520adf7b86a Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Sat, 1 Nov 2025 23:32:08 +0100 Subject: [PATCH 12/57] Disabled by default --- dots/.config/quickshell/ii/modules/common/Config.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 854a059ab..7e82e87d0 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -257,7 +257,7 @@ Singleton { property int full: 101 property bool automaticSuspend: true property int suspend: 3 - property bool showHealth: true + property bool showHealth: false } property JsonObject conflictKiller: JsonObject { From a323e32a4290b2e861f1d1a65dc581f03c5a5ef0 Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Mon, 3 Nov 2025 20:53:40 +0100 Subject: [PATCH 13/57] Removed helper script and made workaround in qml --- .../ii/scripts/battery/calculate-health.sh | 16 --------- .../quickshell/ii/services/Battery.qml | 34 ++++++++++--------- 2 files changed, 18 insertions(+), 32 deletions(-) delete mode 100644 dots/.config/quickshell/ii/scripts/battery/calculate-health.sh diff --git a/dots/.config/quickshell/ii/scripts/battery/calculate-health.sh b/dots/.config/quickshell/ii/scripts/battery/calculate-health.sh deleted file mode 100644 index 83b52a5a0..000000000 --- a/dots/.config/quickshell/ii/scripts/battery/calculate-health.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# This just guesses that the BAT0 is the actuall battery -# because BAT0 is the default for modern systems -full=$(cat /sys/class/power_supply/BAT0/charge_full) -design=$(cat /sys/class/power_supply/BAT0/charge_full_design) - -if [ "$design" -gt 0 ]; then # basically if design > 0 - awk "BEGIN { printf \"%.1f\n\", ($full/$design)*100 }" -else - echo "Error" -fi - -# If you have any issues try to find your "real" battery. -# Run 'ls /sys/class/power_supply'. You may see BATM or BAT1 and no BAT0 -# You can check what is what my running 'cat /sys/class/power_supply/{whatever_you_got}/model_name' diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index a3f45ac77..0875bbe70 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -32,27 +32,29 @@ Singleton { property real timeToEmpty: UPower.displayDevice.timeToEmpty property real timeToFull: UPower.displayDevice.timeToFull - property real health: 0 - Process { - id: batteryProcess - running: Config.options.battery.showHealth - command: [ - "bash", - `${FileUtils.trimFileProtocol(Directories.scriptPath)}/battery/calculate-health.sh` - ] + property real health: (function() { + if (!Config.options.battery.showHealth) { + return 0; + } - stdout: StdioCollector { - onStreamFinished: { - const output = text.trim() - const value = Number(output) - if (!isNaN(value)) { - root.health = value + const devList = UPower.devices.values; + for (let i = 0; i < devList.length; ++i) { + const dev = devList[i]; + if (dev.isLaptopBattery && dev.healthSupported) { + const health = dev.healthPercentage; + if (health === 0) { + return 0; + } else if (health < 1) { + return health * 100; } else { - console.warn("Battery script output invalid:", output) + return health; } } } - } + return 0; + })() + + onIsLowAndNotChargingChanged: { if (!root.available || !isLowAndNotCharging) return; From c0d64c463037f59cbc95b06a4c5e8c38cc15534c Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Mon, 3 Nov 2025 20:55:17 +0100 Subject: [PATCH 14/57] Forgot to delete unnecessary import --- dots/.config/quickshell/ii/services/Battery.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index 0875bbe70..39aa720ab 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -6,7 +6,6 @@ import Quickshell import Quickshell.Services.UPower import QtQuick import Quickshell.Io -import qs.modules.common.functions Singleton { id: root From c1ff57c3d043d995a1f9a4b85451b9d7ee09e42f Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:20:23 +0100 Subject: [PATCH 15/57] Don't display when unsupported --- dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml | 2 +- dots/.config/quickshell/ii/services/Battery.qml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index bba4c422c..6e4bf1c9e 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -86,7 +86,7 @@ StyledPopup { RowLayout { spacing: 5 - visible: Config.options.battery.showHealth + visible: Config.options.battery.showHealth && Battery.health > 0 Layout.fillWidth: true MaterialSymbol { diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index 39aa720ab..849870cd2 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -35,14 +35,13 @@ Singleton { if (!Config.options.battery.showHealth) { return 0; } - const devList = UPower.devices.values; for (let i = 0; i < devList.length; ++i) { const dev = devList[i]; if (dev.isLaptopBattery && dev.healthSupported) { const health = dev.healthPercentage; if (health === 0) { - return 0; + return 0.01; } else if (health < 1) { return health * 100; } else { @@ -54,7 +53,6 @@ Singleton { })() - onIsLowAndNotChargingChanged: { if (!root.available || !isLowAndNotCharging) return; Quickshell.execDetached([ From 31f2184dc67984fd127041c02e0d32acb472ae86 Mon Sep 17 00:00:00 2001 From: EinBowser <146022965+EinBowser@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:58:01 +0100 Subject: [PATCH 16/57] Removed settings and configs for battery health --- dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml | 2 +- dots/.config/quickshell/ii/modules/common/Config.qml | 1 - .../quickshell/ii/modules/settings/GeneralConfig.qml | 8 -------- dots/.config/quickshell/ii/services/Battery.qml | 3 --- 4 files changed, 1 insertion(+), 13 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 6e4bf1c9e..47345ee1f 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -86,7 +86,7 @@ StyledPopup { RowLayout { spacing: 5 - visible: Config.options.battery.showHealth && Battery.health > 0 + visible: Battery.health > 0 Layout.fillWidth: true MaterialSymbol { diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 7e82e87d0..39c0ef131 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -257,7 +257,6 @@ Singleton { property int full: 101 property bool automaticSuspend: true property int suspend: 3 - property bool showHealth: false } property JsonObject conflictKiller: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml index 3be552b9c..c2ea1f930 100644 --- a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml @@ -127,14 +127,6 @@ ContentPage { } } } - ConfigSwitch { - buttonIcon: "healing" - text: Translation.tr('Show battery health in popup') - checked: Config.options.battery.showHealth - onCheckedChanged: { - Config.options.battery.showHealth = checked; - } - } } ContentSection { diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index 849870cd2..f368b8203 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -32,9 +32,6 @@ Singleton { property real timeToFull: UPower.displayDevice.timeToFull property real health: (function() { - if (!Config.options.battery.showHealth) { - return 0; - } const devList = UPower.devices.values; for (let i = 0; i < devList.length; ++i) { const dev = devList[i]; From 0e7422c335f54ab440ca02a294e32c930d5e3099 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:07:38 +0100 Subject: [PATCH 17/57] make bg clock draggable --- .../ii/modules/background/Background.qml | 292 +++--------------- .../widgets/AbstractBackgroundWidget.qml | 97 ++++++ .../background/widgets/clock/ClockWidget.qml | 195 ++++++++++++ .../clock}/CookieClock.qml | 54 ++-- .../clock}/CookieQuote.qml | 0 .../clock}/HourHand.qml | 0 .../clock}/HourMarks.qml | 0 .../clock}/MinuteHand.qml | 0 .../clock}/SecondHand.qml | 2 +- .../clock}/TimeColumn.qml | 4 +- .../clock}/dateIndicator/BubbleDate.qml | 0 .../clock}/dateIndicator/DateIndicator.qml | 2 +- .../clock}/dateIndicator/RectangleDate.qml | 2 +- .../clock}/dateIndicator/RotatingDate.qml | 6 +- .../clock}/minuteMarks/BigHourNumbers.qml | 0 .../clock}/minuteMarks/Dots.qml | 0 .../clock}/minuteMarks/Lines.qml | 0 .../clock}/minuteMarks/MinuteMarks.qml | 4 +- .../quickshell/ii/modules/common/Config.qml | 54 ++-- .../widgets/widgetCanvas/AbstractWidget.qml | 26 ++ .../widgets/widgetCanvas/WidgetCanvas.qml | 7 + .../ii/modules/settings/InterfaceConfig.qml | 113 ++++--- .../ii/scripts/images/least_busy_region.py | 16 +- 23 files changed, 521 insertions(+), 353 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml create mode 100644 dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/CookieClock.qml (73%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/CookieQuote.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/HourHand.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/HourMarks.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/MinuteHand.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/SecondHand.qml (94%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/TimeColumn.qml (86%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/dateIndicator/BubbleDate.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/dateIndicator/DateIndicator.qml (96%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/dateIndicator/RectangleDate.qml (92%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/dateIndicator/RotatingDate.qml (90%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/minuteMarks/BigHourNumbers.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/minuteMarks/Dots.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/minuteMarks/Lines.qml (100%) rename dots/.config/quickshell/ii/modules/background/{cookieClock => widgets/clock}/minuteMarks/MinuteMarks.qml (84%) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml diff --git a/dots/.config/quickshell/ii/modules/background/Background.qml b/dots/.config/quickshell/ii/modules/background/Background.qml index f36cf3590..583c085ea 100644 --- a/dots/.config/quickshell/ii/modules/background/Background.qml +++ b/dots/.config/quickshell/ii/modules/background/Background.qml @@ -4,6 +4,7 @@ import qs import qs.services import qs.modules.common import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas import qs.modules.common.functions as CF import QtQuick import QtQuick.Layouts @@ -13,18 +14,11 @@ import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland -import qs.modules.background.cookieClock +import qs.modules.background.widgets +import qs.modules.background.widgets.clock Variants { id: root - readonly property bool fixedClockPosition: Config.options.background.clock.fixedPosition - readonly property real fixedClockX: Config.options.background.clock.x - readonly property real fixedClockY: Config.options.background.clock.y - readonly property real clockSizePadding: 20 - readonly property real screenSizePadding: 50 - readonly property string clockStyle: Config.options.background.clock.style - readonly property bool showCookieQuote: Config.options.background.showQuote && Config.options.background.quote !== "" && !GlobalStates.screenLocked && Config.options.background.clock.style === "cookie" - readonly property real clockParallaxFactor: Config.options.background.parallax.clockFactor // 0 = full parallax, 1 = no parallax model: Quickshell.screens PanelWindow { @@ -46,9 +40,9 @@ Variants { property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath property bool wallpaperSafetyTriggered: { - const enabled = Config.options.workSafety.enable.wallpaper - const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords)) - const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + const enabled = Config.options.workSafety.enable.wallpaper; + const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords)); + const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)); return enabled && sensitiveWallpaper && sensitiveNetwork; } property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) @@ -59,18 +53,6 @@ Variants { property real movableXSpace: ((wallpaperWidth / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.width) / 2 property real movableYSpace: ((wallpaperHeight / wallpaperToScreenRatio * effectiveWallpaperScale) - screen.height) / 2 readonly property bool verticalParallax: (Config.options.background.parallax.autoVertical && wallpaperHeight > wallpaperWidth) || Config.options.background.parallax.vertical - // Position - property real clockX: (modelData.width / 2) - property real clockY: (modelData.height / 2) - property var textHorizontalAlignment: { - if ((Config.options.lock.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered) - return Text.AlignHCenter; - if (clockX < screen.width / 3) - return Text.AlignLeft; - if (clockX > screen.width * 2 / 3) - return Text.AlignRight; - return Text.AlignHCenter; - } // Colors property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable) property color dominantColor: Appearance.colors.colPrimary // Default, to be changed @@ -97,8 +79,9 @@ Variants { right: true } color: { - if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo) return "transparent"; - return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75) + if (!bgRoot.wallpaperSafetyTriggered || bgRoot.wallpaperIsVideo) + return "transparent"; + return CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75); } Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) @@ -134,53 +117,15 @@ Variants { // Oversized = can be zoomed for parallax, yay bgRoot.effectiveWallpaperScale = Math.min(bgRoot.preferredWallpaperScale, width / screenWidth, height / screenHeight); } - - bgRoot.updateClockPosition(); } } } - // Clock positioning - function updateClockPosition() { - // Somehow all this manual setting is needed to make the proc correctly use the new values - leastBusyRegionProc.path = bgRoot.wallpaperPath; - leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2; - leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2; - leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2; - leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2; - leastBusyRegionProc.running = false; - leastBusyRegionProc.running = true; - } - Process { - id: leastBusyRegionProc - property string path: bgRoot.wallpaperPath - property int contentWidth: 300 - property int contentHeight: 300 - property int horizontalPadding: bgRoot.movableXSpace - property int verticalPadding: bgRoot.movableYSpace - command: [Quickshell.shellPath("scripts/images/least-busy-region-venv.sh"), "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), "--width", contentWidth, "--height", contentHeight, "--horizontal-padding", horizontalPadding, "--vertical-padding", verticalPadding, path - // "--visual-output", - ,] - stdout: StdioCollector { - id: leastBusyRegionOutputCollector - onStreamFinished: { - const output = leastBusyRegionOutputCollector.text; - // console.log("[Background] Least busy region output:", output) - if (output.length === 0) - return; - const parsedContent = JSON.parse(output); - bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale; - bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale; - bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary; - } - } - } - - // Wallpaper Item { anchors.fill: parent clip: true + // Wallpaper StyledImage { id: wallpaper visible: opacity > 0 && !blurLoader.active @@ -261,25 +206,23 @@ Variants { } } - // The clock - Loader { - id: clockLoader - scale: Config.options.background.clock.scale - active: Config.options.background.clock.show + WidgetCanvas { + id: widgetCanvas anchors { left: wallpaper.left + right: wallpaper.right top: wallpaper.top - horizontalCenter: undefined - verticalCenter: undefined + bottom: wallpaper.bottom + readonly property real parallaxFactor: Config.options.background.parallax.widgetsFactor leftMargin: { - const clockXOnWallpaper = bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (root.clockParallaxFactor - 1); - return clockXOnWallpaper - extraMove; + const xOnWallpaper = bgRoot.movableXSpace; + const extraMove = (wallpaper.effectiveValueX * 2 * bgRoot.movableXSpace) * (parallaxFactor - 1); + return xOnWallpaper - extraMove; } topMargin: { - const clockYOnWallpaper = bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) - const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (root.clockParallaxFactor - 1); - return clockYOnWallpaper - extraMove; + const yOnWallpaper = bgRoot.movableYSpace; + const extraMove = (wallpaper.effectiveValueY * 2 * bgRoot.movableYSpace) * (parallaxFactor - 1); + return yOnWallpaper - extraMove; } Behavior on leftMargin { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -288,193 +231,54 @@ Variants { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } + width: wallpaper.width + height: wallpaper.height states: State { name: "centered" - when: (GlobalStates.screenLocked && Config.options.lock.centerClock) || bgRoot.wallpaperSafetyTriggered + when: GlobalStates.screenLocked || bgRoot.wallpaperSafetyTriggered + PropertyChanges { + target: widgetCanvas + width: parent.width + height: parent.height + } AnchorChanges { - target: clockLoader + target: widgetCanvas anchors { left: undefined right: undefined top: undefined - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter + bottom: undefined + // horizontalCenter: parent.horizontalCenter + // verticalCenter: parent.verticalCenter } } } transitions: Transition { + PropertyAnimation { + properties: "width,height" + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } AnchorAnimation { duration: Appearance.animation.elementMove.duration easing.type: Appearance.animation.elementMove.type easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } - sourceComponent: Column { - Loader { - id: digitalClockLoader - visible: root.clockStyle === "digital" - active: visible - sourceComponent: ColumnLayout { - id: clockColumn - spacing: 6 - ClockText { - font.pixelSize: 90 - text: DateTime.time - } - ClockText { - Layout.topMargin: -5 - text: DateTime.date - } - StyledText { - // Somehow gets fucked up if made a ClockText??? - visible: Config.options.background.showQuote && Config.options.background.quote.length > 0 - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - pixelSize: Appearance.font.pixelSize.normal - weight: 350 - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: Config.options.background.quote - } - } - } - - Loader { - id: cookieClockLoader - visible: root.clockStyle === "cookie" - active: visible - sourceComponent: CookieClock {} - } - - Loader { - id: cookieQuoteLoader - visible: root.showCookieQuote - active: visible - sourceComponent: CookieQuote {} - anchors.horizontalCenter: cookieClockLoader.horizontalCenter - } - - } - - Item { - anchors { - top: clockLoader.bottom - topMargin: 8 - horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined - left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined - right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined - leftMargin: -26 - rightMargin: -26 - } - implicitWidth: statusTextBg.implicitWidth - implicitHeight: statusTextBg.implicitHeight - - StyledRectangularShadow { - target: statusTextBg - visible: statusTextBg.visible && root.clockStyle === "cookie" - opacity: statusTextBg.opacity - } - - Rectangle { - id: statusTextBg - anchors.centerIn: parent - clip: true - opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 - visible: opacity > 0 - implicitHeight: statusTextRow.implicitHeight + 5 * 2 - implicitWidth: statusTextRow.implicitWidth + 5 * 2 - radius: Appearance.rounding.small - color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1) - - Behavior on implicitWidth { - animation: Appearance.animation.elementResize.numberAnimation.createObject(this) - } - Behavior on implicitHeight { - animation: Appearance.animation.elementResize.numberAnimation.createObject(this) - } - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - - RowLayout { - id: statusTextRow - anchors.centerIn: parent - spacing: 14 - Item { - Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft - implicitWidth: 1 - } - ClockStatusText { - id: safetyStatusText - shown: bgRoot.wallpaperSafetyTriggered - statusIcon: "hide_image" - statusText: Translation.tr("Wallpaper safety enforced") - } - ClockStatusText { - id: lockStatusText - shown: GlobalStates.screenLocked && Config.options.lock.showLockedText - statusIcon: "lock" - statusText: Translation.tr("Locked") - } - Item { - Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight - implicitWidth: 1 - } - } + FadeLoader { + shown: Config.options.background.widgets.clock.enable + sourceComponent: ClockWidget { + screenWidth: bgRoot.screen.width + screenHeight: bgRoot.screen.height + scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale + scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale + wallpaperScale: bgRoot.effectiveWallpaperScale + wallpaperSafetyTriggered: bgRoot.wallpaperSafetyTriggered } } } } } - - // ComponentsCookieClock {} - component ClockText: StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - animateChange: Config.options.background.clock.digital.animateChange - } - component ClockStatusText: Row { - id: statusTextRow - property alias statusIcon: statusIconWidget.text - property alias statusText: statusTextWidget.text - property bool shown: true - property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : bgRoot.colText - opacity: shown ? 1 : 0 - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - spacing: 4 - MaterialSymbol { - id: statusIconWidget - anchors.verticalCenter: statusTextRow.verticalCenter - iconSize: Appearance.font.pixelSize.huge - color: statusTextRow.textColor - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - ClockText { - id: statusTextWidget - color: statusTextRow.textColor - anchors.verticalCenter: statusTextRow.verticalCenter - font { - pixelSize: Appearance.font.pixelSize.large - weight: Font.Normal - } - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - } } diff --git a/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml new file mode 100644 index 000000000..93960148d --- /dev/null +++ b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml @@ -0,0 +1,97 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets.widgetCanvas + +AbstractWidget { + id: root + + required property string configEntryName + required property int screenWidth + required property int screenHeight + required property int scaledScreenWidth + required property int scaledScreenHeight + required property real wallpaperScale + property bool visibleWhenLocked: false + property var configEntry: Config.options.background.widgets[configEntryName] + property string placementStrategy: configEntry.placementStrategy + property real targetX: Math.max(0, Math.min(configEntry.x, scaledScreenWidth - width)) + property real targetY : Math.max(0, Math.min(configEntry.y, scaledScreenHeight - height)) + x: targetX + y: targetY + visible: opacity > 0 + opacity: (GlobalStates.screenLocked && !visibleWhenLocked) ? 0 : 1 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + draggable: placementStrategy === "free" + onReleased: { + root.targetX = root.x; + root.targetY = root.y; + configEntry.x = root.targetX; + configEntry.y = root.targetY ; + } + + property bool needsColText: false + property color dominantColor: Appearance.colors.colPrimary + property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 + property color colText: { + const onNormalBackground = (GlobalStates.screenLocked && Config.options.lock.blur.enable) + const adaptiveColor = ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + return onNormalBackground ? Appearance.colors.colOnLayer0 : adaptiveColor; + } + + property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") + property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath + + onWallpaperPathChanged: refreshPlacementIfNeeded() + onPlacementStrategyChanged: refreshPlacementIfNeeded() + Connections { + target: Config + function onReadyChanged() { refreshPlacementIfNeeded() } + } + function refreshPlacementIfNeeded() { + if (!Config.ready || (root.placementStrategy === "free" && root.needsColText)) return; + leastBusyRegionProc.wallpaperPath = root.wallpaperPath; + leastBusyRegionProc.running = false; + leastBusyRegionProc.running = true; + } + Process { + id: leastBusyRegionProc + property string wallpaperPath: root.wallpaperPath + // TODO: make these less arbitrary + property int contentWidth: 300 + property int contentHeight: 300 + property int horizontalPadding: 200 + property int verticalPadding: 200 + command: [Quickshell.shellPath("scripts/images/least-busy-region-venv.sh") // Comments to force the formatter to break lines + , "--screen-width", Math.round(root.scaledScreenWidth) // + , "--screen-height", Math.round(root.scaledScreenHeight) // + , "--width", contentWidth // + , "--height", contentHeight // + , "--horizontal-padding", horizontalPadding // + , "--vertical-padding", verticalPadding // + , wallpaperPath // + , ...(root.placementStrategy === "mostBusy" ? ["--busiest"] : []) + // "--visual-output", + ] + stdout: StdioCollector { + id: leastBusyRegionOutputCollector + onStreamFinished: { + const output = leastBusyRegionOutputCollector.text; + // console.log("[Background] Least busy region output:", output) + if (output.length === 0) return; + const parsedContent = JSON.parse(output); + root.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary; + if (root.placementStrategy === "free") return; + root.targetX = parsedContent.center_x * root.wallpaperScale - root.width / 2; + root.targetY = parsedContent.center_y * root.wallpaperScale - root.height / 2; + } + } + } +} + diff --git a/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml new file mode 100644 index 000000000..2c3852f4e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml @@ -0,0 +1,195 @@ +import QtQuick +import QtQuick.Layouts +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas +import qs.modules.background.widgets + +AbstractBackgroundWidget { + id: root + + configEntryName: "clock" + + implicitHeight: contentColumn.implicitHeight + implicitWidth: contentColumn.implicitWidth + + property string clockStyle: Config.options.background.widgets.clock.style + property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock) + property bool wallpaperSafetyTriggered: false + needsColText: clockStyle === "digital" + x: forceCenter ? ((root.screenWidth - root.width) / 2) : targetX + y: forceCenter ? ((root.screenHeight - root.height) / 2) : targetY + visibleWhenLocked: true + + property var textHorizontalAlignment: { + if (root.forceCenter) + return Text.AlignHCenter; + if (root.x < root.scaledScreenWidth / 3) + return Text.AlignLeft; + if (root.x > root.scaledScreenWidth * 2 / 3) + return Text.AlignRight; + return Text.AlignHCenter; + } + + Column { + id: contentColumn + anchors.centerIn: parent + spacing: 10 + + FadeLoader { + id: cookieClockLoader + anchors.horizontalCenter: parent.horizontalCenter + shown: root.clockStyle === "cookie" + sourceComponent: Column { + CookieClock { + anchors.horizontalCenter: parent.horizontalCenter + } + FadeLoader { + anchors.horizontalCenter: parent.horizontalCenter + shown: Config.options.background.showQuote && Config.options.background.quote !== "" + sourceComponent: CookieQuote {} + } + } + } + + FadeLoader { + id: digitalClockLoader + anchors.horizontalCenter: parent.horizontalCenter + shown: root.clockStyle === "digital" + sourceComponent: ColumnLayout { + id: clockColumn + spacing: 6 + + ClockText { + font.pixelSize: 90 + text: DateTime.time + } + ClockText { + Layout.topMargin: -5 + text: DateTime.date + } + StyledText { + // Somehow gets fucked up if made a ClockText??? + visible: Config.options.background.showQuote && Config.options.background.quote.length > 0 + Layout.fillWidth: true + horizontalAlignment: root.textHorizontalAlignment + font { + pixelSize: Appearance.font.pixelSize.normal + weight: 350 + } + color: root.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: Config.options.background.quote + } + } + } + Item { + id: statusText + anchors.horizontalCenter: parent.horizontalCenter + implicitHeight: statusTextBg.implicitHeight + implicitWidth: statusTextBg.implicitWidth + StyledRectangularShadow { + target: statusTextBg + visible: statusTextBg.visible && root.clockStyle === "cookie" + opacity: statusTextBg.opacity + } + Rectangle { + id: statusTextBg + anchors.centerIn: parent + clip: true + opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 + visible: opacity > 0 + implicitHeight: statusTextRow.implicitHeight + 5 * 2 + implicitWidth: statusTextRow.implicitWidth + 5 * 2 + radius: Appearance.rounding.small + color: ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1) + + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + RowLayout { + id: statusTextRow + anchors.centerIn: parent + spacing: 14 + Item { + Layout.fillWidth: root.textHorizontalAlignment !== Text.AlignLeft + implicitWidth: 1 + } + ClockStatusText { + id: safetyStatusText + shown: root.wallpaperSafetyTriggered + statusIcon: "hide_image" + statusText: Translation.tr("Wallpaper safety enforced") + } + ClockStatusText { + id: lockStatusText + shown: GlobalStates.screenLocked && Config.options.lock.showLockedText + statusIcon: "lock" + statusText: Translation.tr("Locked") + } + Item { + Layout.fillWidth: root.textHorizontalAlignment !== Text.AlignRight + implicitWidth: 1 + } + } + } + } + } + + component ClockText: StyledText { + Layout.fillWidth: true + horizontalAlignment: root.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: Font.DemiBold + } + color: root.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: Config.options.background.widgets.clock.digital.animateChange + } + component ClockStatusText: Row { + id: statusTextRow + property alias statusIcon: statusIconWidget.text + property alias statusText: statusTextWidget.text + property bool shown: true + property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : root.colText + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + spacing: 4 + MaterialSymbol { + id: statusIconWidget + anchors.verticalCenter: statusTextRow.verticalCenter + iconSize: Appearance.font.pixelSize.huge + color: statusTextRow.textColor + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + ClockText { + id: statusTextWidget + color: statusTextRow.textColor + anchors.verticalCenter: statusTextRow.verticalCenter + font { + pixelSize: Appearance.font.pixelSize.large + weight: Font.Normal + } + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + } +} diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/CookieClock.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml similarity index 73% rename from dots/.config/quickshell/ii/modules/background/cookieClock/CookieClock.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml index a730535ec..1dbaea8cf 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/CookieClock.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml @@ -9,13 +9,13 @@ import QtQuick.Layouts import Qt5Compat.GraphicalEffects import Quickshell.Io -import qs.modules.background.cookieClock.dateIndicator -import qs.modules.background.cookieClock.minuteMarks +import qs.modules.background.widgets.clock.dateIndicator +import qs.modules.background.widgets.clock.minuteMarks Item { id: root - readonly property string clockStyle: Config.options.background.clock.style + readonly property string clockStyle: Config.options.background.widgets.clock.style property real implicitSize: 230 @@ -36,16 +36,16 @@ Item { implicitHeight: implicitSize function applyStyle(sides, dialStyle, hourHandStyle, minuteHandStyle, secondHandStyle, dateStyle) { - Config.options.background.clock.cookie.sides = sides - Config.options.background.clock.cookie.dialNumberStyle = dialStyle - Config.options.background.clock.cookie.hourHandStyle = hourHandStyle - Config.options.background.clock.cookie.minuteHandStyle = minuteHandStyle - Config.options.background.clock.cookie.secondHandStyle = secondHandStyle - Config.options.background.clock.cookie.dateStyle = dateStyle + Config.options.background.widgets.clock.cookie.sides = sides + Config.options.background.widgets.clock.cookie.dialNumberStyle = dialStyle + Config.options.background.widgets.clock.cookie.hourHandStyle = hourHandStyle + Config.options.background.widgets.clock.cookie.minuteHandStyle = minuteHandStyle + Config.options.background.widgets.clock.cookie.secondHandStyle = secondHandStyle + Config.options.background.widgets.clock.cookie.dateStyle = dateStyle } function setClockPreset(category) { - if (!Config.options.background.clock.cookie.aiStyling) return; + if (!Config.options.background.widgets.clock.cookie.aiStyling) return; if (category === "") return; print("[Cookie clock] Setting clock preset for category: " + category) // "abstract", "anime", "city", "minimalist", "landscape", "plants", "person", "space" @@ -83,7 +83,7 @@ Item { } } - property bool useSineCookie: Config.options.background.clock.cookie.useSineCookie + property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie DropShadow { source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader anchors.fill: source @@ -93,7 +93,7 @@ Item { transparentBorder: true RotationAnimation on rotation { - running: Config.options.background.clock.cookie.constantlyRotate + running: Config.options.background.widgets.clock.cookie.constantlyRotate duration: 30000 easing.type: Easing.Linear loops: Animation.Infinite @@ -108,7 +108,7 @@ Item { active: useSineCookie sourceComponent: SineCookie { implicitSize: root.implicitSize - sides: Config.options.background.clock.cookie.sides + sides: Config.options.background.widgets.clock.cookie.sides color: root.colBackground } } @@ -119,7 +119,7 @@ Item { active: !useSineCookie sourceComponent: MaterialCookie { implicitSize: root.implicitSize - sides: Config.options.background.clock.cookie.sides + sides: Config.options.background.widgets.clock.cookie.sides color: root.colBackground } } @@ -134,7 +134,7 @@ Item { FadeLoader { id: hourMarksLoader anchors.centerIn: parent - shown: Config.options.background.clock.cookie.hourMarks + shown: Config.options.background.widgets.clock.cookie.hourMarks sourceComponent: HourMarks { implicitSize: 135 * (1.75 - 0.75 * hourMarksLoader.opacity) color: root.colOnBackground @@ -146,7 +146,7 @@ Item { FadeLoader { id: timeColumnLoader anchors.centerIn: parent - shown: Config.options.background.clock.cookie.timeIndicators + shown: Config.options.background.widgets.clock.cookie.timeIndicators scale: 1.4 - 0.4 * timeColumnLoader.shown Behavior on scale { animation: Appearance.animation.elementResize.numberAnimation.createObject(this) @@ -161,11 +161,11 @@ Item { FadeLoader { anchors.fill: parent z: 1 - shown: Config.options.background.clock.cookie.minuteHandStyle !== "hide" + shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "hide" sourceComponent: MinuteHand { anchors.fill: parent clockMinute: root.clockMinute - style: Config.options.background.clock.cookie.minuteHandStyle + style: Config.options.background.widgets.clock.cookie.minuteHandStyle color: root.colMinuteHand } } @@ -174,11 +174,11 @@ Item { FadeLoader { anchors.fill: parent z: item?.style === "hollow" ? 0 : 2 - shown: Config.options.background.clock.cookie.hourHandStyle !== "hide" + shown: Config.options.background.widgets.clock.cookie.hourHandStyle !== "hide" sourceComponent: HourHand { clockHour: root.clockHour clockMinute: root.clockMinute - style: Config.options.background.clock.cookie.hourHandStyle + style: Config.options.background.widgets.clock.cookie.hourHandStyle color: root.colHourHand } } @@ -186,13 +186,13 @@ Item { // Second hand FadeLoader { id: secondHandLoader - z: (Config.options.background.clock.cookie.secondHandStyle === "line") ? 2 : 3 - shown: Config.options.time.secondPrecision && Config.options.background.clock.cookie.secondHandStyle !== "hide" + z: (Config.options.background.widgets.clock.cookie.secondHandStyle === "line") ? 2 : 3 + shown: Config.options.time.secondPrecision && Config.options.background.widgets.clock.cookie.secondHandStyle !== "hide" anchors.fill: parent sourceComponent: SecondHand { id: secondHand clockSecond: root.clockSecond - style: Config.options.background.clock.cookie.secondHandStyle + style: Config.options.background.widgets.clock.cookie.secondHandStyle color: root.colSecondHand } } @@ -201,9 +201,9 @@ Item { FadeLoader { z: 4 anchors.centerIn: parent - shown: Config.options.background.clock.cookie.minuteHandStyle !== "bold" + shown: Config.options.background.widgets.clock.cookie.minuteHandStyle !== "bold" sourceComponent: Rectangle { - color: Config.options.background.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand + color: Config.options.background.widgets.clock.cookie.minuteHandStyle === "medium" ? root.colBackground : root.colMinuteHand implicitWidth: 6 implicitHeight: implicitWidth radius: width / 2 @@ -213,11 +213,11 @@ Item { // Date FadeLoader { anchors.fill: parent - shown: Config.options.background.clock.cookie.dateStyle !== "hide" + shown: Config.options.background.widgets.clock.cookie.dateStyle !== "hide" sourceComponent: DateIndicator { color: root.colBackgroundInfo - style: Config.options.background.clock.cookie.dateStyle + style: Config.options.background.widgets.clock.cookie.dateStyle } } } diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/CookieQuote.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/CookieQuote.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/HourHand.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/HourHand.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/HourHand.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/HourHand.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/HourMarks.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/HourMarks.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/HourMarks.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/HourMarks.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/MinuteHand.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/MinuteHand.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/MinuteHand.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/MinuteHand.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/SecondHand.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/SecondHand.qml similarity index 94% rename from dots/.config/quickshell/ii/modules/background/cookieClock/SecondHand.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/SecondHand.qml index 93ca1cf48..2c436a292 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/SecondHand.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/SecondHand.qml @@ -18,7 +18,7 @@ Item { rotation: (360 / 60 * clockSecond) + 90 Behavior on rotation { - enabled: Config.options.background.clock.cookie.constantlyRotate // Animating every second is expensive... + enabled: Config.options.background.widgets.clock.cookie.constantlyRotate // Animating every second is expensive... animation: RotationAnimation { direction: RotationAnimation.Clockwise duration: 1000 // 1 second diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/TimeColumn.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/TimeColumn.qml similarity index 86% rename from dots/.config/quickshell/ii/modules/background/cookieClock/TimeColumn.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/TimeColumn.qml index e2d00c23c..6b73c851a 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/TimeColumn.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/TimeColumn.qml @@ -8,10 +8,10 @@ import QtQuick Column { id: root property list clockNumbers: DateTime.time.split(/[: ]/) - property bool isEnabled: Config.options.background.clock.cookie.timeIndicators + property bool isEnabled: Config.options.background.widgets.clock.cookie.timeIndicators property color color: Appearance.colors.colOnSecondaryContainer - property bool hourMarksEnabled: Config.options.background.clock.cookie.hourMarks + property bool hourMarksEnabled: Config.options.background.widgets.clock.cookie.hourMarks spacing: -16 Repeater { diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/BubbleDate.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/BubbleDate.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/BubbleDate.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/BubbleDate.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/DateIndicator.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/DateIndicator.qml similarity index 96% rename from dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/DateIndicator.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/DateIndicator.qml index 66b1852ee..820a96dea 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/DateIndicator.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/DateIndicator.qml @@ -14,7 +14,7 @@ Item { // Rotating date FadeLoader { anchors.fill: parent - shown: Config.options.background.clock.cookie.dateStyle === "border" + shown: Config.options.background.widgets.clock.cookie.dateStyle === "border" sourceComponent: RotatingDate { color: root.color } diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RectangleDate.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RectangleDate.qml similarity index 92% rename from dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RectangleDate.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RectangleDate.qml index d8ea9ab2e..0c09db194 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RectangleDate.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RectangleDate.qml @@ -6,7 +6,7 @@ import QtQuick Rectangle { id: rect - readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle + readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle StyledText { anchors.centerIn: parent diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RotatingDate.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RotatingDate.qml similarity index 90% rename from dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RotatingDate.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RotatingDate.qml index ec25c024f..3c8354d74 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/dateIndicator/RotatingDate.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/dateIndicator/RotatingDate.qml @@ -8,14 +8,14 @@ import QtQuick Item { id: root - property string style: Config.options.background.clock.cookie.dateStyle + property string style: Config.options.background.widgets.clock.cookie.dateStyle property color color: Appearance.colors.colOnSecondaryContainer property real angleStep: 12 * Math.PI / 180 property string dateText: Qt.locale().toString(DateTime.clock.date, "ddd dd") readonly property int clockSecond: DateTime.clock.seconds - readonly property string dialStyle: Config.options.background.clock.cookie.dialNumberStyle - readonly property bool timeIndicators: Config.options.background.clock.cookie.timeIndicators + readonly property string dialStyle: Config.options.background.widgets.clock.cookie.dialNumberStyle + readonly property bool timeIndicators: Config.options.background.widgets.clock.cookie.timeIndicators property real radius: style === "border" ? 90 : 0 Behavior on radius { diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/BigHourNumbers.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/BigHourNumbers.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/BigHourNumbers.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/BigHourNumbers.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/Dots.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/Dots.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/Dots.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/Dots.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/Lines.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/Lines.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/Lines.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/Lines.qml diff --git a/dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/MinuteMarks.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/MinuteMarks.qml similarity index 84% rename from dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/MinuteMarks.qml rename to dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/MinuteMarks.qml index 07cf0dc5e..02d8f2582 100644 --- a/dots/.config/quickshell/ii/modules/background/cookieClock/minuteMarks/MinuteMarks.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/minuteMarks/MinuteMarks.qml @@ -8,8 +8,8 @@ Item { id: root property color color: Appearance.colors.colOnSecondaryContainer - property string style: Config.options.background.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide" - property string dateStyle : Config.options.background.clock.cookie.dateStyle + property string style: Config.options.background.widgets.clock.cookie.dialNumberStyle // "dots", "numbers", "full", "hide" + property string dateStyle : Config.options.background.widgets.clock.cookie.dateStyle // 12 Dots FadeLoader { diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 4df736237..948f92601 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -145,31 +145,33 @@ Singleton { } property JsonObject background: JsonObject { - property JsonObject clock: JsonObject { - property bool fixedPosition: false - property real x: -500 - property real y: -500 - property bool show: true - property string style: "cookie" // Options: "cookie", "digital" - property real scale: 1 - property JsonObject cookie: JsonObject { - property bool aiStyling: false - property int sides: 14 - property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none" - property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide" - property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide" - property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide" - property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide" - property bool timeIndicators: true - property bool hourMarks: false - property bool dateInClock: true - property bool constantlyRotate: false - property bool useSineCookie: false + property JsonObject widgets: JsonObject { + property JsonObject clock: JsonObject { + property bool enable: true + property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy" + property real x: 100 + property real y: 100 + property string style: "cookie" // Options: "cookie", "digital" + property real scale: 1 + property JsonObject cookie: JsonObject { + property bool aiStyling: false + property int sides: 14 + property string dialNumberStyle: "full" // Options: "dots" , "numbers", "full" , "none" + property string hourHandStyle: "fill" // Options: "classic", "fill", "hollow", "hide" + property string minuteHandStyle: "medium" // Options "classic", "thin", "medium", "bold", "hide" + property string secondHandStyle: "dot" // Options: "dot", "line", "classic", "hide" + property string dateStyle: "bubble" // Options: "border", "rect", "bubble" , "hide" + property bool timeIndicators: true + property bool hourMarks: false + property bool dateInClock: true + property bool constantlyRotate: false + property bool useSineCookie: false + } + property JsonObject digital: JsonObject { + property bool animateChange: true + } + } - property JsonObject digital: JsonObject { - property bool animateChange: true - } - } property string wallpaperPath: "" property string thumbnailPath: "" @@ -182,7 +184,7 @@ Singleton { property bool enableWorkspace: true property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property bool enableSidebar: true - property real clockFactor: 1.2 + property real widgetsFactor: 1.2 } } @@ -318,7 +320,7 @@ Singleton { property bool useHyprlock: false property bool launchOnStartup: false property JsonObject blur: JsonObject { - property bool enable: false + property bool enable: true property real radius: 100 property real extraZoom: 1.1 } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml new file mode 100644 index 000000000..26d8c9547 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml @@ -0,0 +1,26 @@ +import QtQuick +import Quickshell +import qs.modules.common + +/* + * Widget to be placed on a WidgetCanvas + */ +MouseArea { + id: root + + property bool draggable: true + drag.target: draggable ? root : undefined + cursorShape: drag.active ? Qt.ClosedHandCursor : draggable ? Qt.OpenHandCursor : Qt.ArrowCursor + + function center() { + root.x = (root.parent.width - root.width) / 2 + root.y = (root.parent.height - root.height) / 2 + } + + Behavior on x { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on y { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml new file mode 100644 index 000000000..b40091432 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml @@ -0,0 +1,7 @@ +import QtQuick + +Item { + id: root + + // uh this is stupid turns out we don't need anything here +} diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 89e202e0c..d1b8030ec 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -14,9 +14,9 @@ ContentPage { ConfigSwitch { buttonIcon: "nest_clock_farsight_analog" text: Translation.tr("Show clock") - checked: Config.options.background.clock.show + checked: Config.options.background.widgets.clock.enable onCheckedChanged: { - Config.options.background.clock.show = checked; + Config.options.background.widgets.clock.enable = checked; } } @@ -24,21 +24,48 @@ ContentPage { ConfigSpinBox { icon: "loupe" text: Translation.tr("Scale (%)") - value: Config.options.background.clock.scale * 100 + value: Config.options.background.widgets.clock.scale * 100 from: 1 to: 200 stepSize: 2 onValueChanged: { - Config.options.background.clock.scale = value / 100; + Config.options.background.widgets.clock.scale = value / 100; + } + } + + ContentSubsection { + title: Translation.tr("Clock placement strategy") + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.placementStrategy + onSelected: newValue => { + Config.options.background.widgets.clock.placementStrategy = newValue; + } + options: [ + { + displayName: Translation.tr("Draggable"), + icon: "drag_pan", + value: "free" + }, + { + displayName: Translation.tr("Least busy"), + icon: "arrows_output", + value: "leastBusy" + }, + { + displayName: Translation.tr("Most busy"), + icon: "arrows_input", + value: "mostBusy" + }, + ] } } ContentSubsection { title: Translation.tr("Clock style") ConfigSelectionArray { - currentValue: Config.options.background.clock.style + currentValue: Config.options.background.widgets.clock.style onSelected: newValue => { - Config.options.background.clock.style = newValue; + Config.options.background.widgets.clock.style = newValue; } options: [ { @@ -56,29 +83,29 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "digital" + visible: Config.options.background.widgets.clock.style === "digital" title: Translation.tr("Digital clock settings") ConfigSwitch { buttonIcon: "animation" text: Translation.tr("Animate time change") - checked: Config.options.background.clock.digital.animateChange + checked: Config.options.background.widgets.clock.digital.animateChange onCheckedChanged: { - Config.options.background.clock.digital.animateChange = checked; + Config.options.background.widgets.clock.digital.animateChange = checked; } } } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Cookie clock settings") ConfigSwitch { buttonIcon: "wand_stars" text: Translation.tr("Auto styling with Gemini") - checked: Config.options.background.clock.cookie.aiStyling + checked: Config.options.background.widgets.clock.cookie.aiStyling onCheckedChanged: { - Config.options.background.clock.cookie.aiStyling = checked; + Config.options.background.widgets.clock.cookie.aiStyling = checked; } StyledToolTip { text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.") @@ -88,9 +115,9 @@ ContentPage { ConfigSwitch { buttonIcon: "airwave" text: Translation.tr("Use old sine wave cookie implementation") - checked: Config.options.background.clock.cookie.useSineCookie + checked: Config.options.background.widgets.clock.cookie.useSineCookie onCheckedChanged: { - Config.options.background.clock.cookie.useSineCookie = checked; + Config.options.background.widgets.clock.cookie.useSineCookie = checked; } StyledToolTip { text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing" @@ -100,21 +127,21 @@ ContentPage { ConfigSpinBox { icon: "add_triangle" text: Translation.tr("Sides") - value: Config.options.background.clock.cookie.sides + value: Config.options.background.widgets.clock.cookie.sides from: 0 to: 40 stepSize: 1 onValueChanged: { - Config.options.background.clock.cookie.sides = value; + Config.options.background.widgets.clock.cookie.sides = value; } } ConfigSwitch { buttonIcon: "autoplay" text: Translation.tr("Constantly rotate") - checked: Config.options.background.clock.cookie.constantlyRotate + checked: Config.options.background.widgets.clock.cookie.constantlyRotate onCheckedChanged: { - Config.options.background.clock.cookie.constantlyRotate = checked; + Config.options.background.widgets.clock.cookie.constantlyRotate = checked; } StyledToolTip { text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical." @@ -124,15 +151,15 @@ ContentPage { ConfigRow { ConfigSwitch { - enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle === "dots" || Config.options.background.clock.cookie.dialNumberStyle === "full" + enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full" buttonIcon: "brightness_7" text: Translation.tr("Hour marks") - checked: Config.options.background.clock.cookie.hourMarks + checked: Config.options.background.widgets.clock.cookie.hourMarks onEnabledChanged: { - checked = Config.options.background.clock.cookie.hourMarks; + checked = Config.options.background.widgets.clock.cookie.hourMarks; } onCheckedChanged: { - Config.options.background.clock.cookie.hourMarks = checked; + Config.options.background.widgets.clock.cookie.hourMarks = checked; } StyledToolTip { text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons" @@ -140,15 +167,15 @@ ContentPage { } ConfigSwitch { - enabled: Config.options.background.clock.style === "cookie" && Config.options.background.clock.cookie.dialNumberStyle !== "numbers" + enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers" buttonIcon: "timer_10" text: Translation.tr("Digits in the middle") - checked: Config.options.background.clock.cookie.timeIndicators + checked: Config.options.background.widgets.clock.cookie.timeIndicators onEnabledChanged: { - checked = Config.options.background.clock.cookie.timeIndicators; + checked = Config.options.background.widgets.clock.cookie.timeIndicators; } onCheckedChanged: { - Config.options.background.clock.cookie.timeIndicators = checked; + Config.options.background.widgets.clock.cookie.timeIndicators = checked; } StyledToolTip { text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons" @@ -158,17 +185,17 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Dial style") ConfigSelectionArray { - currentValue: Config.options.background.clock.cookie.dialNumberStyle + currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle onSelected: newValue => { - Config.options.background.clock.cookie.dialNumberStyle = newValue; + Config.options.background.widgets.clock.cookie.dialNumberStyle = newValue; if (newValue !== "dots" && newValue !== "full") { - Config.options.background.clock.cookie.hourMarks = false; + Config.options.background.widgets.clock.cookie.hourMarks = false; } if (newValue === "numbers") { - Config.options.background.clock.cookie.timeIndicators = false; + Config.options.background.widgets.clock.cookie.timeIndicators = false; } } options: [ @@ -197,12 +224,12 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Hour hand") ConfigSelectionArray { - currentValue: Config.options.background.clock.cookie.hourHandStyle + currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle onSelected: newValue => { - Config.options.background.clock.cookie.hourHandStyle = newValue; + Config.options.background.widgets.clock.cookie.hourHandStyle = newValue; } options: [ { @@ -230,13 +257,13 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Minute hand") ConfigSelectionArray { - currentValue: Config.options.background.clock.cookie.minuteHandStyle + currentValue: Config.options.background.widgets.clock.cookie.minuteHandStyle onSelected: newValue => { - Config.options.background.clock.cookie.minuteHandStyle = newValue; + Config.options.background.widgets.clock.cookie.minuteHandStyle = newValue; } options: [ { @@ -269,13 +296,13 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Second hand") ConfigSelectionArray { - currentValue: Config.options.background.clock.cookie.secondHandStyle + currentValue: Config.options.background.widgets.clock.cookie.secondHandStyle onSelected: newValue => { - Config.options.background.clock.cookie.secondHandStyle = newValue; + Config.options.background.widgets.clock.cookie.secondHandStyle = newValue; } options: [ { @@ -303,13 +330,13 @@ ContentPage { } ContentSubsection { - visible: Config.options.background.clock.style === "cookie" + visible: Config.options.background.widgets.clock.style === "cookie" title: Translation.tr("Date style") ConfigSelectionArray { - currentValue: Config.options.background.clock.cookie.dateStyle + currentValue: Config.options.background.widgets.clock.cookie.dateStyle onSelected: newValue => { - Config.options.background.clock.cookie.dateStyle = newValue; + Config.options.background.widgets.clock.cookie.dateStyle = newValue; } options: [ { diff --git a/dots/.config/quickshell/ii/scripts/images/least_busy_region.py b/dots/.config/quickshell/ii/scripts/images/least_busy_region.py index 1d64033c6..b9e1c8963 100755 --- a/dots/.config/quickshell/ii/scripts/images/least_busy_region.py +++ b/dots/.config/quickshell/ii/scripts/images/least_busy_region.py @@ -18,7 +18,7 @@ def center_crop(img, target_w, target_h): y2 = y1 + target_h return img[y1:y2, x1:x2] -def find_least_busy_region(image_path, region_width=300, region_height=200, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", horizontal_padding=50, vertical_padding=50): +def find_least_busy_region(image_path, region_width=300, region_height=200, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", horizontal_padding=50, vertical_padding=50, busiest=False): img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) if img is None: raise FileNotFoundError(f"Image not found: {image_path}") @@ -77,7 +77,9 @@ def find_least_busy_region(image_path, region_width=300, region_height=200, scre total += ii[y1-1, x1-1] return total min_var = None + max_var = None min_coords = (horizontal_padding, vertical_padding) + max_coords = (horizontal_padding, vertical_padding) area = region_width * region_height x_start = horizontal_padding y_start = vertical_padding @@ -100,7 +102,13 @@ def find_least_busy_region(image_path, region_width=300, region_height=200, scre if (min_var is None) or (var < min_var): min_var = var min_coords = (x, y) - return min_coords, min_var + if (max_var is None) or (var > max_var): + max_var = var + max_coords = (x, y) + if busiest: + return max_coords, max_var + else: + return min_coords, min_var def find_largest_region(image_path, screen_width=None, screen_height=None, verbose=False, stride=2, screen_mode="fill", threshold=100.0, aspect_ratio=1.0, horizontal_padding=50, vertical_padding=50): img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) @@ -313,6 +321,7 @@ def main(): parser.add_argument("--aspect-ratio", type=float, default=1.78, help="Aspect ratio (width/height) for largest region mode") parser.add_argument("--horizontal-padding", "-hp", type=int, default=50, help="Minimum horizontal distance from region to image edge") parser.add_argument("--vertical-padding", "-vp", type=int, default=50, help="Minimum vertical distance from region to image edge") + parser.add_argument("--busiest", action="store_true", help="Find the busiest region instead of the least busy") args = parser.parse_args() if args.largest_region: @@ -363,7 +372,8 @@ def main(): stride=args.stride, screen_mode=args.screen_mode, horizontal_padding=args.horizontal_padding, - vertical_padding=args.vertical_padding + vertical_padding=args.vertical_padding, + busiest=args.busiest ) if args.visual_output: draw_region(args.image_path, coords, region_width=args.width, region_height=args.height, screen_width=args.screen_width, screen_height=args.screen_height, screen_mode=args.screen_mode) From ba6fba447a3c72713ff375ea718a62137179b8fe Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:40:34 +0100 Subject: [PATCH 18/57] fix some undefined warnings --- .../quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml | 2 +- .../ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml index 9ede16e33..74d5c5f6a 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml @@ -329,7 +329,7 @@ Rectangle { segmentContent: thisBlock.content messageData: root.messageData done: root.messageData?.done ?? false - forceDisableChunkSplitting: root.messageData.content.includes("```") + forceDisableChunkSplitting: root.messageData?.content.includes("```") ?? true } } } } diff --git a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml index 2c471d3f8..de508e169 100644 --- a/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml +++ b/dots/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageTextBlock.qml @@ -159,7 +159,7 @@ ColumnLayout { selectedTextColor: Appearance.m3colors.m3onSecondaryContainer selectionColor: Appearance.colors.colSecondaryContainer wrapMode: TextEdit.Wrap - color: messageData.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1 + color: root.messageData?.thinking ? Appearance.colors.colSubtext : Appearance.colors.colOnLayer1 textFormat: renderMarkdown ? TextEdit.MarkdownText : TextEdit.PlainText text: modelData From 1f208125bf17cf08e652593b83af79f95991c7a3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:41:28 +0100 Subject: [PATCH 19/57] remove clock scale option (it has never been good) --- .../.config/quickshell/ii/modules/common/Config.qml | 1 - .../ii/modules/settings/InterfaceConfig.qml | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 948f92601..11ab5d571 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -152,7 +152,6 @@ Singleton { property real x: 100 property real y: 100 property string style: "cookie" // Options: "cookie", "digital" - property real scale: 1 property JsonObject cookie: JsonObject { property bool aiStyling: false property int sides: 14 diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index d1b8030ec..dfacd8129 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -19,19 +19,6 @@ ContentPage { Config.options.background.widgets.clock.enable = checked; } } - - - ConfigSpinBox { - icon: "loupe" - text: Translation.tr("Scale (%)") - value: Config.options.background.widgets.clock.scale * 100 - from: 1 - to: 200 - stepSize: 2 - onValueChanged: { - Config.options.background.widgets.clock.scale = value / 100; - } - } ContentSubsection { title: Translation.tr("Clock placement strategy") From 8a35609b0dd29d85fb5d36b197a52102a15924e4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:41:44 +0100 Subject: [PATCH 20/57] refractor StyledDropShadow --- .../background/widgets/clock/CookieClock.qml | 9 ++------- .../ii/modules/common/widgets/StyledDropShadow.qml | 13 +++++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/StyledDropShadow.qml diff --git a/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml index 1dbaea8cf..1284e4299 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieClock.qml @@ -84,13 +84,8 @@ Item { } property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie - DropShadow { - source: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader - anchors.fill: source - radius: 8 - samples: radius * 2 + 1 - color: root.colShadow - transparentBorder: true + StyledDropShadow { + target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader RotationAnimation on rotation { running: Config.options.background.widgets.clock.cookie.constantlyRotate diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledDropShadow.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledDropShadow.qml new file mode 100644 index 000000000..5daca8915 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledDropShadow.qml @@ -0,0 +1,13 @@ +import QtQuick +import Qt5Compat.GraphicalEffects +import qs.modules.common + +DropShadow { + required property var target + source: target + anchors.fill: source + radius: 8 + samples: radius * 2 + 1 + color: Appearance.colors.colShadow + transparentBorder: true +} From c82a2e835ba7501ed04757f8ff4c08fc783b81ed Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:43:19 +0100 Subject: [PATCH 21/57] move weather icons to Icons Co-Authored-By: Vague Syntax <173799252+vaguesyntax@users.noreply.github.com> --- .../ii/modules/bar/weather/WeatherBar.qml | 2 +- .../ii/modules/bar/weather/WeatherIcons.qml | 59 ------------------- .../quickshell/ii/modules/common/Icons.qml | 59 +++++++++++++++++++ 3 files changed, 60 insertions(+), 60 deletions(-) delete mode 100644 dots/.config/quickshell/ii/modules/bar/weather/WeatherIcons.qml diff --git a/dots/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml b/dots/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml index 66923834d..1fcde7176 100644 --- a/dots/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml +++ b/dots/.config/quickshell/ii/modules/bar/weather/WeatherBar.qml @@ -30,7 +30,7 @@ MouseArea { MaterialSymbol { fill: 0 - text: WeatherIcons.codeToName[Weather.data.wCode] ?? "cloud" + text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud" iconSize: Appearance.font.pixelSize.large color: Appearance.colors.colOnLayer1 Layout.alignment: Qt.AlignVCenter diff --git a/dots/.config/quickshell/ii/modules/bar/weather/WeatherIcons.qml b/dots/.config/quickshell/ii/modules/bar/weather/WeatherIcons.qml deleted file mode 100644 index bd74d4e17..000000000 --- a/dots/.config/quickshell/ii/modules/bar/weather/WeatherIcons.qml +++ /dev/null @@ -1,59 +0,0 @@ -pragma Singleton - -import Quickshell - -Singleton { - // credits: calestia - // this snippet is taken from - // https://github.com/caelestia-dots/shell - readonly property var codeToName: ({ - "113": "clear_day", - "116": "partly_cloudy_day", - "119": "cloud", - "122": "cloud", - "143": "foggy", - "176": "rainy", - "179": "rainy", - "182": "rainy", - "185": "rainy", - "200": "thunderstorm", - "227": "cloudy_snowing", - "230": "snowing_heavy", - "248": "foggy", - "260": "foggy", - "263": "rainy", - "266": "rainy", - "281": "rainy", - "284": "rainy", - "293": "rainy", - "296": "rainy", - "299": "rainy", - "302": "weather_hail", - "305": "rainy", - "308": "weather_hail", - "311": "rainy", - "314": "rainy", - "317": "rainy", - "320": "cloudy_snowing", - "323": "cloudy_snowing", - "326": "cloudy_snowing", - "329": "snowing_heavy", - "332": "snowing_heavy", - "335": "snowing", - "338": "snowing_heavy", - "350": "rainy", - "353": "rainy", - "356": "rainy", - "359": "weather_hail", - "362": "rainy", - "365": "rainy", - "368": "cloudy_snowing", - "371": "snowing", - "374": "rainy", - "377": "rainy", - "386": "thunderstorm", - "389": "thunderstorm", - "392": "thunderstorm", - "395": "snowing" - }) -} diff --git a/dots/.config/quickshell/ii/modules/common/Icons.qml b/dots/.config/quickshell/ii/modules/common/Icons.qml index 454aea11e..9858d2963 100644 --- a/dots/.config/quickshell/ii/modules/common/Icons.qml +++ b/dots/.config/quickshell/ii/modules/common/Icons.qml @@ -20,4 +20,63 @@ Singleton { return "keyboard"; return "bluetooth"; } + + readonly property var weatherIconMap: ({ + "113": "clear_day", + "116": "partly_cloudy_day", + "119": "cloud", + "122": "cloud", + "143": "foggy", + "176": "rainy", + "179": "rainy", + "182": "rainy", + "185": "rainy", + "200": "thunderstorm", + "227": "cloudy_snowing", + "230": "snowing_heavy", + "248": "foggy", + "260": "foggy", + "263": "rainy", + "266": "rainy", + "281": "rainy", + "284": "rainy", + "293": "rainy", + "296": "rainy", + "299": "rainy", + "302": "weather_hail", + "305": "rainy", + "308": "weather_hail", + "311": "rainy", + "314": "rainy", + "317": "rainy", + "320": "cloudy_snowing", + "323": "cloudy_snowing", + "326": "cloudy_snowing", + "329": "snowing_heavy", + "332": "snowing_heavy", + "335": "snowing", + "338": "snowing_heavy", + "350": "rainy", + "353": "rainy", + "356": "rainy", + "359": "weather_hail", + "362": "rainy", + "365": "rainy", + "368": "cloudy_snowing", + "371": "snowing", + "374": "rainy", + "377": "rainy", + "386": "thunderstorm", + "389": "thunderstorm", + "392": "thunderstorm", + "395": "snowing" + }) + + + function getWeatherIcon(code) { + const key = String(code) + if (weatherIconMap.hasOwnProperty(key)) { + return weatherIconMap[key] + } + } } From 37244dc0f776579fec7b29abb69f3f1e721d7457 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 00:44:22 +0100 Subject: [PATCH 22/57] background: add weather widget Co-Authored-By: Vague Syntax <173799252+vaguesyntax@users.noreply.github.com> --- .../ii/modules/background/Background.qml | 12 ++++ .../widgets/weather/WeatherWidget.qml | 58 +++++++++++++++++++ .../quickshell/ii/modules/common/Config.qml | 7 ++- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml diff --git a/dots/.config/quickshell/ii/modules/background/Background.qml b/dots/.config/quickshell/ii/modules/background/Background.qml index 583c085ea..7057da1cd 100644 --- a/dots/.config/quickshell/ii/modules/background/Background.qml +++ b/dots/.config/quickshell/ii/modules/background/Background.qml @@ -16,6 +16,7 @@ import Quickshell.Hyprland import qs.modules.background.widgets import qs.modules.background.widgets.clock +import qs.modules.background.widgets.weather Variants { id: root @@ -267,6 +268,17 @@ Variants { } } + FadeLoader { + shown: Config.options.background.widgets.weather.enable + sourceComponent: WeatherWidget { + screenWidth: bgRoot.screen.width + screenHeight: bgRoot.screen.height + scaledScreenWidth: bgRoot.screen.width / bgRoot.effectiveWallpaperScale + scaledScreenHeight: bgRoot.screen.height / bgRoot.effectiveWallpaperScale + wallpaperScale: bgRoot.effectiveWallpaperScale + } + } + FadeLoader { shown: Config.options.background.widgets.clock.enable sourceComponent: ClockWidget { diff --git a/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml new file mode 100644 index 000000000..8e055b542 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml @@ -0,0 +1,58 @@ +import QtQuick +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas +import qs.modules.background.widgets + +AbstractBackgroundWidget { + id: root + + configEntryName: "weather" + + implicitHeight: backgroundShape.implicitHeight + implicitWidth: backgroundShape.implicitWidth + + StyledDropShadow { + target: backgroundShape + } + + MaterialShape { + id: backgroundShape + anchors.fill: parent + shape: MaterialShape.Shape.Pill + color: Appearance.colors.colPrimaryContainer + implicitSize: 230 + + StyledText { + font { + pixelSize: 84 + family: Appearance.font.family.expressive + weight: Font.Medium + } + color: Appearance.colors.colPrimary + text: Weather.data?.temp.substring(0,Weather.data?.temp.length - 1) ?? "--°" + anchors { + right: parent.right + top: parent.top + rightMargin: 26 + topMargin: 30 + } + } + + MaterialSymbol { + iconSize: 84 + color: Appearance.colors.colOnPrimaryContainer + text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud" + anchors { + left: parent.left + bottom: parent.bottom + + leftMargin: 26 + bottomMargin: 30 + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 11ab5d571..c457f674d 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -169,7 +169,12 @@ Singleton { property JsonObject digital: JsonObject { property bool animateChange: true } - + } + property JsonObject weather: JsonObject { + property bool enable: false + property string placementStrategy: "free" // "free", "leastBusy", "mostBusy" + property real x: 400 + property real y: 100 } } property string wallpaperPath: "" From a1606c9c239961dfd9315d5202f82206a3071210 Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 13:47:09 +0800 Subject: [PATCH 23/57] Update comment --- sdata/dist-nix/home-manager/home.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdata/dist-nix/home-manager/home.nix b/sdata/dist-nix/home-manager/home.nix index e8f96cde2..a70f7603a 100644 --- a/sdata/dist-nix/home-manager/home.nix +++ b/sdata/dist-nix/home-manager/home.nix @@ -18,7 +18,9 @@ # "org.freedesktop.impl.portal.ScreenCast" = [ "gnome" ]; #}; }; - # The following seems to generate ~/.config/fontconfig conflicting with the one under dots/ + # TODO: The following seems to generate ~/.config/fontconfig conflicting with the one under dots/ + # However, fontconfig may rely on this to properly find fonts installed via Nix. + # Need a proper way to deal with it. #fonts.fontconfig.enable = true; wayland.windowManager.hyprland = { From aca19d6903775d60cb274d100c59782db54f097e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:14:17 +0100 Subject: [PATCH 24/57] adjust weather widget size --- .../background/widgets/weather/WeatherWidget.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml index 8e055b542..5b598d24a 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml @@ -24,11 +24,11 @@ AbstractBackgroundWidget { anchors.fill: parent shape: MaterialShape.Shape.Pill color: Appearance.colors.colPrimaryContainer - implicitSize: 230 + implicitSize: 200 StyledText { font { - pixelSize: 84 + pixelSize: 80 family: Appearance.font.family.expressive weight: Font.Medium } @@ -37,21 +37,21 @@ AbstractBackgroundWidget { anchors { right: parent.right top: parent.top - rightMargin: 26 - topMargin: 30 + rightMargin: 20 + topMargin: 24 } } MaterialSymbol { - iconSize: 84 + iconSize: 80 color: Appearance.colors.colOnPrimaryContainer text: Icons.getWeatherIcon(Weather.data.wCode) ?? "cloud" anchors { left: parent.left bottom: parent.bottom - leftMargin: 26 - bottomMargin: 30 + leftMargin: 20 + bottomMargin: 24 } } } From b1007f2ded816b143d9c185a5fa2fe91833482de Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:14:36 +0100 Subject: [PATCH 25/57] make abstractwidget's cursor shape more accurate --- .../ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml index 26d8c9547..20b0c27c1 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractWidget.qml @@ -10,7 +10,7 @@ MouseArea { property bool draggable: true drag.target: draggable ? root : undefined - cursorShape: drag.active ? Qt.ClosedHandCursor : draggable ? Qt.OpenHandCursor : Qt.ArrowCursor + cursorShape: (draggable && containsPress) ? Qt.ClosedHandCursor : draggable ? Qt.OpenHandCursor : Qt.ArrowCursor function center() { root.x = (root.parent.width - root.width) / 2 From 2667751a1cdd58f7fdd22f07f3b9682665fc5f37 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:15:06 +0100 Subject: [PATCH 26/57] add settings entries for bg widgets --- .../ii/modules/settings/BackgroundConfig.qml | 471 ++++++++++++++++++ .../ii/modules/settings/InterfaceConfig.qml | 409 --------------- dots/.config/quickshell/ii/settings.qml | 6 +- 3 files changed, 476 insertions(+), 410 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml new file mode 100644 index 000000000..d1a3bf920 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -0,0 +1,471 @@ +import QtQuick +import QtQuick.Layouts +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +ContentPage { + forceWidth: true + + ContentSection { + icon: "sync_alt" + title: Translation.tr("Parallax") + + ConfigSwitch { + buttonIcon: "unfold_more_double" + text: Translation.tr("Vertical") + checked: Config.options.background.parallax.vertical + onCheckedChanged: { + Config.options.background.parallax.vertical = checked; + } + } + + ConfigRow { + uniform: true + ConfigSwitch { + buttonIcon: "counter_1" + text: Translation.tr("Depends on workspace") + checked: Config.options.background.parallax.enableWorkspace + onCheckedChanged: { + Config.options.background.parallax.enableWorkspace = checked; + } + } + ConfigSwitch { + buttonIcon: "side_navigation" + text: Translation.tr("Depends on sidebars") + checked: Config.options.background.parallax.enableSidebar + onCheckedChanged: { + Config.options.background.parallax.enableSidebar = checked; + } + } + } + ConfigSpinBox { + icon: "loupe" + text: Translation.tr("Preferred wallpaper zoom (%)") + value: Config.options.background.parallax.workspaceZoom * 100 + from: 100 + to: 150 + stepSize: 1 + onValueChanged: { + Config.options.background.parallax.workspaceZoom = value / 100; + } + } + } + + ContentSection { + icon: "clock_loader_40" + title: Translation.tr("Widget: Clock") + + ConfigRow { + Layout.fillWidth: true + + ConfigSwitch { + Layout.fillWidth: false + buttonIcon: "check" + text: Translation.tr("Enable") + checked: Config.options.background.widgets.clock.enable + onCheckedChanged: { + Config.options.background.widgets.clock.enable = checked; + } + } + Item { + Layout.fillWidth: true + } + ConfigSelectionArray { + Layout.fillWidth: false + currentValue: Config.options.background.widgets.clock.placementStrategy + onSelected: newValue => { + Config.options.background.widgets.clock.placementStrategy = newValue; + } + options: [ + { + displayName: Translation.tr("Draggable"), + icon: "drag_pan", + value: "free" + }, + { + displayName: Translation.tr("Least busy"), + icon: "category", + value: "leastBusy" + }, + { + displayName: Translation.tr("Most busy"), + icon: "shapes", + value: "mostBusy" + }, + ] + } + } + + ContentSubsection { + title: Translation.tr("Clock style") + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.style + onSelected: newValue => { + Config.options.background.widgets.clock.style = newValue; + } + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "digital" + title: Translation.tr("Digital clock settings") + + ConfigSwitch { + buttonIcon: "animation" + text: Translation.tr("Animate time change") + checked: Config.options.background.widgets.clock.digital.animateChange + onCheckedChanged: { + Config.options.background.widgets.clock.digital.animateChange = checked; + } + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Cookie clock settings") + + ConfigSwitch { + buttonIcon: "wand_stars" + text: Translation.tr("Auto styling with Gemini") + checked: Config.options.background.widgets.clock.cookie.aiStyling + onCheckedChanged: { + Config.options.background.widgets.clock.cookie.aiStyling = checked; + } + StyledToolTip { + text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.") + } + } + + ConfigSwitch { + buttonIcon: "airwave" + text: Translation.tr("Use old sine wave cookie implementation") + checked: Config.options.background.widgets.clock.cookie.useSineCookie + onCheckedChanged: { + Config.options.background.widgets.clock.cookie.useSineCookie = checked; + } + StyledToolTip { + text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing" + } + } + + ConfigSpinBox { + icon: "add_triangle" + text: Translation.tr("Sides") + value: Config.options.background.widgets.clock.cookie.sides + from: 0 + to: 40 + stepSize: 1 + onValueChanged: { + Config.options.background.widgets.clock.cookie.sides = value; + } + } + + ConfigSwitch { + buttonIcon: "autoplay" + text: Translation.tr("Constantly rotate") + checked: Config.options.background.widgets.clock.cookie.constantlyRotate + onCheckedChanged: { + Config.options.background.widgets.clock.cookie.constantlyRotate = checked; + } + StyledToolTip { + text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical." + } + } + + ConfigRow { + + ConfigSwitch { + enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full" + buttonIcon: "brightness_7" + text: Translation.tr("Hour marks") + checked: Config.options.background.widgets.clock.cookie.hourMarks + onEnabledChanged: { + checked = Config.options.background.widgets.clock.cookie.hourMarks; + } + onCheckedChanged: { + Config.options.background.widgets.clock.cookie.hourMarks = checked; + } + StyledToolTip { + text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons" + } + } + + ConfigSwitch { + enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers" + buttonIcon: "timer_10" + text: Translation.tr("Digits in the middle") + checked: Config.options.background.widgets.clock.cookie.timeIndicators + onEnabledChanged: { + checked = Config.options.background.widgets.clock.cookie.timeIndicators; + } + onCheckedChanged: { + Config.options.background.widgets.clock.cookie.timeIndicators = checked; + } + StyledToolTip { + text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons" + } + } + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Dial style") + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle + onSelected: newValue => { + Config.options.background.widgets.clock.cookie.dialNumberStyle = newValue; + if (newValue !== "dots" && newValue !== "full") { + Config.options.background.widgets.clock.cookie.hourMarks = false; + } + if (newValue === "numbers") { + Config.options.background.widgets.clock.cookie.timeIndicators = false; + } + } + options: [ + { + displayName: "", + icon: "block", + value: "none" + }, + { + displayName: Translation.tr("Dots"), + icon: "graph_6", + value: "dots" + }, + { + displayName: Translation.tr("Full"), + icon: "history_toggle_off", + value: "full" + }, + { + displayName: Translation.tr("Numbers"), + icon: "counter_1", + value: "numbers" + } + ] + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Hour hand") + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle + onSelected: newValue => { + Config.options.background.widgets.clock.cookie.hourHandStyle = newValue; + } + options: [ + { + displayName: "", + icon: "block", + value: "hide" + }, + { + displayName: Translation.tr("Classic"), + icon: "radio", + value: "classic" + }, + { + displayName: Translation.tr("Hollow"), + icon: "circle", + value: "hollow" + }, + { + displayName: Translation.tr("Fill"), + icon: "eraser_size_5", + value: "fill" + }, + ] + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Minute hand") + + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.cookie.minuteHandStyle + onSelected: newValue => { + Config.options.background.widgets.clock.cookie.minuteHandStyle = newValue; + } + options: [ + { + displayName: "", + icon: "block", + value: "hide" + }, + { + displayName: Translation.tr("Classic"), + icon: "radio", + value: "classic" + }, + { + displayName: Translation.tr("Thin"), + icon: "line_end", + value: "thin" + }, + { + displayName: Translation.tr("Medium"), + icon: "eraser_size_2", + value: "medium" + }, + { + displayName: Translation.tr("Bold"), + icon: "eraser_size_4", + value: "bold" + }, + ] + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Second hand") + + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.cookie.secondHandStyle + onSelected: newValue => { + Config.options.background.widgets.clock.cookie.secondHandStyle = newValue; + } + options: [ + { + displayName: "", + icon: "block", + value: "hide" + }, + { + displayName: Translation.tr("Classic"), + icon: "radio", + value: "classic" + }, + { + displayName: Translation.tr("Line"), + icon: "line_end", + value: "line" + }, + { + displayName: Translation.tr("Dot"), + icon: "adjust", + value: "dot" + }, + ] + } + } + + ContentSubsection { + visible: Config.options.background.widgets.clock.style === "cookie" + title: Translation.tr("Date style") + + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.cookie.dateStyle + onSelected: newValue => { + Config.options.background.widgets.clock.cookie.dateStyle = newValue; + } + options: [ + { + displayName: "", + icon: "block", + value: "hide" + }, + { + displayName: Translation.tr("Bubble"), + icon: "bubble_chart", + value: "bubble" + }, + { + displayName: Translation.tr("Border"), + icon: "rotate_right", + value: "border" + }, + { + displayName: Translation.tr("Rect"), + icon: "rectangle", + value: "rect" + } + ] + } + } + + ContentSubsection { + title: Translation.tr("Quote") + + ConfigSwitch { + buttonIcon: "check" + text: Translation.tr("Enable") + checked: Config.options.background.showQuote + onCheckedChanged: { + Config.options.background.showQuote = checked; + } + } + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Quote") + text: Config.options.background.quote + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.background.quote = text; + } + } + } + } + + ContentSection { + icon: "weather_mix" + title: Translation.tr("Widget: Weather") + + ConfigRow { + Layout.fillWidth: true + + ConfigSwitch { + Layout.fillWidth: false + buttonIcon: "check" + text: Translation.tr("Enable") + checked: Config.options.background.widgets.weather.enable + onCheckedChanged: { + Config.options.background.widgets.weather.enable = checked; + } + } + Item { + Layout.fillWidth: true + } + ConfigSelectionArray { + Layout.fillWidth: false + currentValue: Config.options.background.widgets.weather.placementStrategy + onSelected: newValue => { + Config.options.background.widgets.weather.placementStrategy = newValue; + } + options: [ + { + displayName: Translation.tr("Draggable"), + icon: "drag_pan", + value: "free" + }, + { + displayName: Translation.tr("Least busy"), + icon: "category", + value: "leastBusy" + }, + { + displayName: Translation.tr("Most busy"), + icon: "shapes", + value: "mostBusy" + }, + ] + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index dfacd8129..b7bff73b1 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -7,415 +7,6 @@ import qs.modules.common.widgets ContentPage { forceWidth: true - ContentSection { - icon: "wallpaper" - title: Translation.tr("Background") - - ConfigSwitch { - buttonIcon: "nest_clock_farsight_analog" - text: Translation.tr("Show clock") - checked: Config.options.background.widgets.clock.enable - onCheckedChanged: { - Config.options.background.widgets.clock.enable = checked; - } - } - - ContentSubsection { - title: Translation.tr("Clock placement strategy") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.placementStrategy - onSelected: newValue => { - Config.options.background.widgets.clock.placementStrategy = newValue; - } - options: [ - { - displayName: Translation.tr("Draggable"), - icon: "drag_pan", - value: "free" - }, - { - displayName: Translation.tr("Least busy"), - icon: "arrows_output", - value: "leastBusy" - }, - { - displayName: Translation.tr("Most busy"), - icon: "arrows_input", - value: "mostBusy" - }, - ] - } - } - - ContentSubsection { - title: Translation.tr("Clock style") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.style - onSelected: newValue => { - Config.options.background.widgets.clock.style = newValue; - } - options: [ - { - displayName: Translation.tr("Simple digital"), - icon: "timer_10", - value: "digital" - }, - { - displayName: Translation.tr("Material cookie"), - icon: "cookie", - value: "cookie" - } - ] - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "digital" - title: Translation.tr("Digital clock settings") - - ConfigSwitch { - buttonIcon: "animation" - text: Translation.tr("Animate time change") - checked: Config.options.background.widgets.clock.digital.animateChange - onCheckedChanged: { - Config.options.background.widgets.clock.digital.animateChange = checked; - } - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Cookie clock settings") - - ConfigSwitch { - buttonIcon: "wand_stars" - text: Translation.tr("Auto styling with Gemini") - checked: Config.options.background.widgets.clock.cookie.aiStyling - onCheckedChanged: { - Config.options.background.widgets.clock.cookie.aiStyling = checked; - } - StyledToolTip { - text: Translation.tr("Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.") - } - } - - ConfigSwitch { - buttonIcon: "airwave" - text: Translation.tr("Use old sine wave cookie implementation") - checked: Config.options.background.widgets.clock.cookie.useSineCookie - onCheckedChanged: { - Config.options.background.widgets.clock.cookie.useSineCookie = checked; - } - StyledToolTip { - text: "Looks a bit softer and more consistent with different number of sides,\nbut has less impressive morphing" - } - } - - ConfigSpinBox { - icon: "add_triangle" - text: Translation.tr("Sides") - value: Config.options.background.widgets.clock.cookie.sides - from: 0 - to: 40 - stepSize: 1 - onValueChanged: { - Config.options.background.widgets.clock.cookie.sides = value; - } - } - - ConfigSwitch { - buttonIcon: "autoplay" - text: Translation.tr("Constantly rotate") - checked: Config.options.background.widgets.clock.cookie.constantlyRotate - onCheckedChanged: { - Config.options.background.widgets.clock.cookie.constantlyRotate = checked; - } - StyledToolTip { - text: "Makes the clock always rotate. This is extremely expensive\n(expect 50% usage on Intel UHD Graphics) and thus impractical." - } - } - - ConfigRow { - - ConfigSwitch { - enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full" - buttonIcon: "brightness_7" - text: Translation.tr("Hour marks") - checked: Config.options.background.widgets.clock.cookie.hourMarks - onEnabledChanged: { - checked = Config.options.background.widgets.clock.cookie.hourMarks; - } - onCheckedChanged: { - Config.options.background.widgets.clock.cookie.hourMarks = checked; - } - StyledToolTip { - text: "Can only be turned on using the 'Dots' or 'Full' dial style for aesthetic reasons" - } - } - - ConfigSwitch { - enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers" - buttonIcon: "timer_10" - text: Translation.tr("Digits in the middle") - checked: Config.options.background.widgets.clock.cookie.timeIndicators - onEnabledChanged: { - checked = Config.options.background.widgets.clock.cookie.timeIndicators; - } - onCheckedChanged: { - Config.options.background.widgets.clock.cookie.timeIndicators = checked; - } - StyledToolTip { - text: "Can't be turned on when using 'Numbers' dial style for aesthetic reasons" - } - } - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Dial style") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle - onSelected: newValue => { - Config.options.background.widgets.clock.cookie.dialNumberStyle = newValue; - if (newValue !== "dots" && newValue !== "full") { - Config.options.background.widgets.clock.cookie.hourMarks = false; - } - if (newValue === "numbers") { - Config.options.background.widgets.clock.cookie.timeIndicators = false; - } - } - options: [ - { - displayName: "", - icon: "block", - value: "none" - }, - { - displayName: Translation.tr("Dots"), - icon: "graph_6", - value: "dots" - }, - { - displayName: Translation.tr("Full"), - icon: "history_toggle_off", - value: "full" - }, - { - displayName: Translation.tr("Numbers"), - icon: "counter_1", - value: "numbers" - } - ] - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Hour hand") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle - onSelected: newValue => { - Config.options.background.widgets.clock.cookie.hourHandStyle = newValue; - } - options: [ - { - displayName: "", - icon: "block", - value: "hide" - }, - { - displayName: Translation.tr("Classic"), - icon: "radio", - value: "classic" - }, - { - displayName: Translation.tr("Hollow"), - icon: "circle", - value: "hollow" - }, - { - displayName: Translation.tr("Fill"), - icon: "eraser_size_5", - value: "fill" - }, - ] - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Minute hand") - - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.cookie.minuteHandStyle - onSelected: newValue => { - Config.options.background.widgets.clock.cookie.minuteHandStyle = newValue; - } - options: [ - { - displayName: "", - icon: "block", - value: "hide" - }, - { - displayName: Translation.tr("Classic"), - icon: "radio", - value: "classic" - }, - { - displayName: Translation.tr("Thin"), - icon: "line_end", - value: "thin" - }, - { - displayName: Translation.tr("Medium"), - icon: "eraser_size_2", - value: "medium" - }, - { - displayName: Translation.tr("Bold"), - icon: "eraser_size_4", - value: "bold" - }, - ] - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Second hand") - - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.cookie.secondHandStyle - onSelected: newValue => { - Config.options.background.widgets.clock.cookie.secondHandStyle = newValue; - } - options: [ - { - displayName: "", - icon: "block", - value: "hide" - }, - { - displayName: Translation.tr("Classic"), - icon: "radio", - value: "classic" - }, - { - displayName: Translation.tr("Line"), - icon: "line_end", - value: "line" - }, - { - displayName: Translation.tr("Dot"), - icon: "adjust", - value: "dot" - }, - ] - } - } - - ContentSubsection { - visible: Config.options.background.widgets.clock.style === "cookie" - title: Translation.tr("Date style") - - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.cookie.dateStyle - onSelected: newValue => { - Config.options.background.widgets.clock.cookie.dateStyle = newValue; - } - options: [ - { - displayName: "", - icon: "block", - value: "hide" - }, - { - displayName: Translation.tr("Bubble"), - icon: "bubble_chart", - value: "bubble" - }, - { - displayName: Translation.tr("Border"), - icon: "rotate_right", - value: "border" - }, - { - displayName: Translation.tr("Rect"), - icon: "rectangle", - value: "rect" - } - ] - } - } - - ContentSubsection { - title: Translation.tr("Quote settings") - ConfigSwitch { - buttonIcon: "format_quote" - text: Translation.tr("Show quote") - checked: Config.options.background.showQuote - onCheckedChanged: { - Config.options.background.showQuote = checked; - } - } - MaterialTextArea { - Layout.fillWidth: true - placeholderText: Translation.tr("Quote") - text: Config.options.background.quote - wrapMode: TextEdit.Wrap - onTextChanged: { - Config.options.background.quote = text; - } - } - } - - ContentSubsection { - title: Translation.tr("Wallpaper parallax") - - ConfigSwitch { - buttonIcon: "unfold_more_double" - text: Translation.tr("Vertical") - checked: Config.options.background.parallax.vertical - onCheckedChanged: { - Config.options.background.parallax.vertical = checked; - } - } - - ConfigRow { - uniform: true - ConfigSwitch { - buttonIcon: "counter_1" - text: Translation.tr("Depends on workspace") - checked: Config.options.background.parallax.enableWorkspace - onCheckedChanged: { - Config.options.background.parallax.enableWorkspace = checked; - } - } - ConfigSwitch { - buttonIcon: "side_navigation" - text: Translation.tr("Depends on sidebars") - checked: Config.options.background.parallax.enableSidebar - onCheckedChanged: { - Config.options.background.parallax.enableSidebar = checked; - } - } - } - ConfigSpinBox { - icon: "loupe" - text: Translation.tr("Preferred wallpaper zoom (%)") - value: Config.options.background.parallax.workspaceZoom * 100 - from: 100 - to: 150 - stepSize: 1 - onValueChanged: { - Config.options.background.parallax.workspaceZoom = value / 100; - } - } - } - } - ContentSection { icon: "point_scan" title: Translation.tr("Crosshair overlay") diff --git a/dots/.config/quickshell/ii/settings.qml b/dots/.config/quickshell/ii/settings.qml index a836c06a1..65c64c164 100644 --- a/dots/.config/quickshell/ii/settings.qml +++ b/dots/.config/quickshell/ii/settings.qml @@ -31,7 +31,6 @@ ApplicationWindow { { name: Translation.tr("General"), icon: "browse", - iconRotation: 180, component: "modules/settings/GeneralConfig.qml" }, { @@ -40,6 +39,11 @@ ApplicationWindow { iconRotation: 180, component: "modules/settings/BarConfig.qml" }, + { + name: Translation.tr("Background"), + icon: "texture", + component: "modules/settings/BackgroundConfig.qml" + }, { name: Translation.tr("Interface"), icon: "bottom_app_bar", From 025a819b63c70b5a408ed4ba19e50e48ab43b81a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:15:21 +0100 Subject: [PATCH 27/57] scale up bg widgets when dragging --- .../modules/background/widgets/AbstractBackgroundWidget.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml index 93960148d..4f16c51cd 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml @@ -27,6 +27,10 @@ AbstractWidget { Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } + scale: (draggable && containsPress) ? 1.05 : 1 + Behavior on scale { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } draggable: placementStrategy === "free" onReleased: { From b3f81f350c28a73f104c3ba6687f8cfe1b560d13 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:16:08 +0100 Subject: [PATCH 28/57] reduce persistent low battery warnings --- dots/.config/quickshell/ii/services/Battery.qml | 9 ++++++--- dots/.config/quickshell/ii/services/Notifications.qml | 7 ++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Battery.qml b/dots/.config/quickshell/ii/services/Battery.qml index b07bd5305..f40087216 100644 --- a/dots/.config/quickshell/ii/services/Battery.qml +++ b/dots/.config/quickshell/ii/services/Battery.qml @@ -38,7 +38,8 @@ Singleton { Translation.tr("Low battery"), Translation.tr("Consider plugging in your device"), "-u", "critical", - "-a", "Shell" + "-a", "Shell", + "--hint=int:transient:1", ]) if (root.soundEnabled) Audio.playSystemSound("dialog-warning"); @@ -51,7 +52,8 @@ Singleton { Translation.tr("Critically low battery"), Translation.tr("Please charge!\nAutomatic suspend triggers at %1%").arg(Config.options.battery.suspend), "-u", "critical", - "-a", "Shell" + "-a", "Shell", + "--hint=int:transient:1", ]); if (root.soundEnabled) Audio.playSystemSound("suspend-error"); @@ -69,7 +71,8 @@ Singleton { "notify-send", Translation.tr("Battery full"), Translation.tr("Please unplug the charger"), - "-a", "Shell" + "-a", "Shell", + "--hint=int:transient:1", ]); if (root.soundEnabled) Audio.playSystemSound("complete"); diff --git a/dots/.config/quickshell/ii/services/Notifications.qml b/dots/.config/quickshell/ii/services/Notifications.qml index 37d72f5b3..702da0f8a 100644 --- a/dots/.config/quickshell/ii/services/Notifications.qml +++ b/dots/.config/quickshell/ii/services/Notifications.qml @@ -25,6 +25,7 @@ Singleton { "text": action.text, })) ?? [] property bool popup: false + property bool isTransient: notification?.hints.transient ?? false property string appIcon: notification?.appIcon ?? "" property string appName: notification?.appName ?? "" property string body: notification?.body ?? "" @@ -63,7 +64,11 @@ Singleton { interval: 7000 running: true onTriggered: () => { - root.timeoutNotification(notificationId); + const index = root.list.findIndex((notif) => notif.notificationId === notificationId); + const notifObject = root.list[index]; + print("[Notifications] Notification timer triggered for ID: " + notificationId + ", transient: " + notifObject?.isTransient); + if (notifObject.isTransient) root.discardNotification(notificationId); + else root.timeoutNotification(notificationId); destroy() } } From 64f6081b149de14a75b84073678a306f44e84a98 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:23:20 +0100 Subject: [PATCH 29/57] config: move quote settings to clock widget subsection --- .../ii/modules/background/widgets/clock/ClockWidget.qml | 6 +++--- .../ii/modules/background/widgets/clock/CookieQuote.qml | 4 ++-- dots/.config/quickshell/ii/modules/common/Config.qml | 6 ++++-- .../quickshell/ii/modules/settings/BackgroundConfig.qml | 8 ++++---- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml index 2c3852f4e..3fcd00d46 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml @@ -49,7 +49,7 @@ AbstractBackgroundWidget { } FadeLoader { anchors.horizontalCenter: parent.horizontalCenter - shown: Config.options.background.showQuote && Config.options.background.quote !== "" + shown: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text !== "" sourceComponent: CookieQuote {} } } @@ -73,7 +73,7 @@ AbstractBackgroundWidget { } StyledText { // Somehow gets fucked up if made a ClockText??? - visible: Config.options.background.showQuote && Config.options.background.quote.length > 0 + visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 Layout.fillWidth: true horizontalAlignment: root.textHorizontalAlignment font { @@ -83,7 +83,7 @@ AbstractBackgroundWidget { color: root.colText style: Text.Raised styleColor: Appearance.colors.colShadow - text: Config.options.background.quote + text: Config.options.background.widgets.clock.quote.text } } } diff --git a/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml index 45b726fa3..19567aeb4 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/CookieQuote.qml @@ -7,7 +7,7 @@ import Qt5Compat.GraphicalEffects Item { id: root - readonly property string quoteText: Config.options.background.quote + readonly property string quoteText: Config.options.background.widgets.clock.quote.text implicitWidth: quoteBox.implicitWidth implicitHeight: quoteBox.implicitHeight @@ -47,7 +47,7 @@ Item { StyledText { id: quoteStyledText horizontalAlignment: Text.AlignLeft - text: Config.options.background.quote + text: Config.options.background.widgets.clock.quote.text color: Appearance.colors.colOnSecondaryContainer font { family: Appearance.font.family.reading diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index c457f674d..455bc2345 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -169,6 +169,10 @@ Singleton { property JsonObject digital: JsonObject { property bool animateChange: true } + property JsonObject quote: JsonObject { + property bool enable: false + property string text: "" + } } property JsonObject weather: JsonObject { property bool enable: false @@ -179,8 +183,6 @@ Singleton { } property string wallpaperPath: "" property string thumbnailPath: "" - property string quote: "" - property bool showQuote: false property bool hideWhenFullscreen: true property JsonObject parallax: JsonObject { property bool vertical: false diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index d1a3bf920..3b388583f 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -406,18 +406,18 @@ ContentPage { ConfigSwitch { buttonIcon: "check" text: Translation.tr("Enable") - checked: Config.options.background.showQuote + checked: Config.options.background.widgets.clock.quote.enable onCheckedChanged: { - Config.options.background.showQuote = checked; + Config.options.background.widgets.clock.quote.enable = checked; } } MaterialTextArea { Layout.fillWidth: true placeholderText: Translation.tr("Quote") - text: Config.options.background.quote + text: Config.options.background.widgets.clock.quote.text wrapMode: TextEdit.Wrap onTextChanged: { - Config.options.background.quote = text; + Config.options.background.widgets.clock.quote.text = text; } } } From bf376c8aaa0079037943f834825befb005c01f09 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:46:52 +0100 Subject: [PATCH 30/57] make clock closer to quote --- .../ii/modules/background/widgets/clock/ClockWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml index 3fcd00d46..84cc7e01b 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/clock/ClockWidget.qml @@ -37,7 +37,7 @@ AbstractBackgroundWidget { Column { id: contentColumn anchors.centerIn: parent - spacing: 10 + spacing: 6 FadeLoader { id: cookieClockLoader From eff52332b528b3d2924bc96f0609186a0c6f79ed Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 18:51:09 +0800 Subject: [PATCH 31/57] Update dist-nix/README --- sdata/dist-nix/README.md | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index 5febf4698..c027518d8 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -5,6 +5,13 @@ **NOTE: The sdata/dist-nix is not for NixOS but every distro, using Nix and home-manager.** +As we all know Nix and Home-manager has two major functionalities: +- Handling dependencies (i.e. package installation) +- Handling dotfiles + +They are discussed below. + +# Handling dependencies ## plan Note that this script must be idempotent. @@ -29,3 +36,41 @@ See also https://github.com/caelestia-dots/shell/issues/668 ### NixGL On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem. + +# Handling dot files + +## Requirements +About handling the dotfiles, i.e. `dots/`, if we are doing this using Nix then the following requirements must be fulfilled. + +**1. Allow modifications over the existing dotfiles.** + +Current state of `./setup install`: +- After finishing running `./setup install`, users can modify any dotfiles in a traditional way, and if they run `./setup install` again to update then they need to skip the steps which overwrite the targets that they have modified and later sync the upgrade manually for such targets by themselves. + - For Hyprland, specially we have a `custom` folder along with `~/.config/hypr/hyprland.conf` which will only get overwritten the first time but not the later times running `./setup install`. +- This works but is not elegant. An experimental solution is using yaml config to store the selected behavior for each target, see #2137. + +If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation. + +**2. Allow choosing targets.** + +This is similar to the above. For example user may want to use their own `~/.config/foot` instead of the files under `dots/.config/foot` entirely. + +**3. Easy developing dotfiles or at least not worse than current state.** + +About the current state: +- @clsty: "If I were the one who develops the dotfiles, I will make changes to the local Git repo `dots-hyprland` and rerun `./setup install-files -f` to apply the changes to observe the outcome." +- @end-4 (who develops the dots; see [comment](https://github.com/end-4/dots-hyprland/pull/2278#issuecomment-3454929577)): "I modify my local copy of stuff, copy the relevant parts over, optionally selectively pick changes then commit. It's.... the most obvious way but I guess not necessarily the cleanest" + +If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation. + +**4. Others** + +Find out a good method to avoid what @end-4 [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-2954725029): + +> About home-manager, from my limited understanding of and experience with it, any change to the config files require a rebuild right? If this is indeed the case, switching entirely to this is not okay. Having to wait 20 seconds for each change is absurd. + +Some information may help, e.g. @darsh032 [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3336839862): + +> I mean thats not really needed you can use mkOutOfStoreSymlink or use hjem-impure to change the configs without rebuilding + +And also the "hmrice" [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3353345504) by @Markus328 , and the `flake.nix` (for quickshell only) [mentioned](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3354387126) by @darsh032 . From 6ca5175bb0395f29da89f7bbe48f85ecc995465c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:13:13 +0100 Subject: [PATCH 32/57] silent some stupid prints --- dots/.config/quickshell/ii/modules/lock/LockContext.qml | 2 +- dots/.config/quickshell/ii/services/KeyringStorage.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/lock/LockContext.qml index 17893bb18..b242cdc05 100644 --- a/dots/.config/quickshell/ii/modules/lock/LockContext.qml +++ b/dots/.config/quickshell/ii/modules/lock/LockContext.qml @@ -87,7 +87,7 @@ Scope { } onExited: (exitCode, exitStatus) => { if (exitCode !== 0) { - console.warn("fprintd-list command exited with error:", exitCode, exitStatus); + // console.warn("[LockContext] fprintd-list command exited with error:", exitCode, exitStatus); root.fingerprintsConfigured = false; } } diff --git a/dots/.config/quickshell/ii/services/KeyringStorage.qml b/dots/.config/quickshell/ii/services/KeyringStorage.qml index 1e8562cac..ae49496d2 100644 --- a/dots/.config/quickshell/ii/services/KeyringStorage.qml +++ b/dots/.config/quickshell/ii/services/KeyringStorage.qml @@ -108,7 +108,7 @@ Singleton { } } onExited: (exitCode, exitStatus) => { - console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); + // console.log("[KeyringStorage] Keyring data fetch process exited with code:", exitCode); if (exitCode === 1) { console.error("[KeyringStorage] Entry not found, initializing."); root.keyringData = {}; From 923841cb56955df825f8d2522c8a11f86dacb007 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:13:46 +0100 Subject: [PATCH 33/57] region selector: add selection padding to target regions --- dots/.config/quickshell/ii/modules/common/Config.qml | 1 + .../ii/modules/regionSelector/RegionSelection.qml | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 455bc2345..7d07360b3 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -377,6 +377,7 @@ Singleton { property bool showLabel: false property real opacity: 0.3 property real contentRegionOpacity: 0.8 + property int selectionPadding: 5 } property JsonObject rect: JsonObject { property bool showAimLines: true diff --git a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml index a0853cc89..31f28e45c 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml @@ -121,10 +121,11 @@ PanelWindow { return (root.targetedRegionX >= 0 && root.targetedRegionY >= 0) } function setRegionToTargeted() { - root.regionX = root.targetedRegionX; - root.regionY = root.targetedRegionY; - root.regionWidth = root.targetedRegionWidth; - root.regionHeight = root.targetedRegionHeight; + const padding = Config.options.regionSelector.targetRegions.selectionPadding; // Make borders not cut off n stuff + root.regionX = root.targetedRegionX - padding; + root.regionY = root.targetedRegionY - padding; + root.regionWidth = root.targetedRegionWidth + padding * 2; + root.regionHeight = root.targetedRegionHeight + padding * 2; } function updateTargetedRegion(x, y) { From d497e00474d05013ab2efc8215831f5bcbba927f Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 19:18:47 +0800 Subject: [PATCH 34/57] Update dist-nix/README --- sdata/dist-nix/README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index c027518d8..527192e62 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -3,21 +3,23 @@ - See also [Install scripts | illogical-impulse](https://ii.clsty.link/en/dev/inst-script/) - See also [#1061](https://github.com/end-4/dots-hyprland/issues/1061) -**NOTE: The sdata/dist-nix is not for NixOS but every distro, using Nix and home-manager.** +**NOTE: The `dist-nix` is not for NixOS but every distro, using Nix and home-manager.** As we all know Nix and Home-manager has two major functionalities: - Handling dependencies (i.e. package installation) - Handling dotfiles -They are discussed below. +They are discussed in following sections. # Handling dependencies +## Status +Partially works. See [Discussion #2382](https://github.com/end-4/dots-hyprland/discussions/2382). ## plan Note that this script must be idempotent. TODO: -- [ ] Fix all TODOs inside `dist-nix`. -- [ ] Warn user if inode-limited filesystem (typically ext4) is used. +- [ ] Fix all TODOs inside `dist-nix`. ([search online](https://github.com/search?q=repo%3Aend-4%2Fdots-hyprland+path%3A%2F%5Esdata%5C%2Fdist-nix%5C%2F%2F+TODO&type=code)) +- [ ] Since Nix uses a large number of inodes, need to warn user if inode-limited filesystem (typically ext4) is used. - [ ] Deal with error when running `systemctl --user enable ydotool --now`: ```plain Failed to connect to user scope bus via local transport: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=@.host --user to connect to bus of other user) @@ -30,15 +32,16 @@ On non-NixOS distros, programs using PAM (typically screen locker) will not work - One problem is that Debian(-based) distros use modified version of PAM which supports `@include` directive in `/etc/pam.d` config files but the PAM from Nix does not support it, see [this comment](https://github.com/NixOS/nixpkgs/issues/128523#issuecomment-1086106614). - Another problem is the location of a suid helper binary that is necessary, see [this comment](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3403195230). -The problem could be solved by using the system-provided libpam instead. +As [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3403195230) by @Cu3PO42 , both the problem could be solved by using the system-provided libpam instead. -See also https://github.com/caelestia-dots/shell/issues/668 +See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668). ### NixGL On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem. # Handling dot files - +## Status +Paused, until some suitable method has been confirmed to meet the requirements below. ## Requirements About handling the dotfiles, i.e. `dots/`, if we are doing this using Nix then the following requirements must be fulfilled. From 520068e52380046cfcd21eb97cd46e79f6ca9261 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:23:44 +0100 Subject: [PATCH 35/57] rename font config file (#2384) --- dots/.config/fontconfig/{conf.d => fonts.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dots/.config/fontconfig/{conf.d => fonts.conf} (100%) diff --git a/dots/.config/fontconfig/conf.d b/dots/.config/fontconfig/fonts.conf similarity index 100% rename from dots/.config/fontconfig/conf.d rename to dots/.config/fontconfig/fonts.conf From bd04346f163194fcd390545cfb967facedcfd572 Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 19:31:35 +0800 Subject: [PATCH 36/57] Enable fontconfig in home.nix (#2384) --- sdata/dist-nix/home-manager/home.nix | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sdata/dist-nix/home-manager/home.nix b/sdata/dist-nix/home-manager/home.nix index a70f7603a..b6ab39cbe 100644 --- a/sdata/dist-nix/home-manager/home.nix +++ b/sdata/dist-nix/home-manager/home.nix @@ -18,10 +18,9 @@ # "org.freedesktop.impl.portal.ScreenCast" = [ "gnome" ]; #}; }; - # TODO: The following seems to generate ~/.config/fontconfig conflicting with the one under dots/ - # However, fontconfig may rely on this to properly find fonts installed via Nix. - # Need a proper way to deal with it. - #fonts.fontconfig.enable = true; + # Note: The following generate files under ~/.config/fontconfig/conf.d/ + # fontconfig may rely on this to properly find fonts installed via Nix. + fonts.fontconfig.enable = true; wayland.windowManager.hyprland = { ## Make sure home-manager not generate ~/.config/hypr/hyprland.conf From d14d1700169ef8308670ed192acbccb6f43dfccc Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 19:34:03 +0800 Subject: [PATCH 37/57] Update dist-nix/README --- sdata/dist-nix/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index 527192e62..cc1c8dd07 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -50,7 +50,7 @@ About handling the dotfiles, i.e. `dots/`, if we are doing this using Nix then t Current state of `./setup install`: - After finishing running `./setup install`, users can modify any dotfiles in a traditional way, and if they run `./setup install` again to update then they need to skip the steps which overwrite the targets that they have modified and later sync the upgrade manually for such targets by themselves. - For Hyprland, specially we have a `custom` folder along with `~/.config/hypr/hyprland.conf` which will only get overwritten the first time but not the later times running `./setup install`. -- This works but is not elegant. An experimental solution is using yaml config to store the selected behavior for each target, see #2137. +- This works but is not elegant. An experimental solution is using yaml config to store the selected behavior for each target, see [#2137](https://github.com/end-4/dots-hyprland/issues/2137). If we use Nix to handle dotfiles, then it must be at least better than the current state described above, mainly in terms of convenience and automation. From 6d6fa42857e91a0ac31e2c1eb62ef8e91c15f639 Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 19:34:56 +0800 Subject: [PATCH 38/57] Move ar/conf.d to ar/fonts.conf (#2384) --- dots-extra/fontsets/ar/{conf.d => fonts.conf} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dots-extra/fontsets/ar/{conf.d => fonts.conf} (100%) diff --git a/dots-extra/fontsets/ar/conf.d b/dots-extra/fontsets/ar/fonts.conf similarity index 100% rename from dots-extra/fontsets/ar/conf.d rename to dots-extra/fontsets/ar/fonts.conf From 11f7adc6434b40eca73fe876891c2a695710ccce Mon Sep 17 00:00:00 2001 From: clsty Date: Wed, 5 Nov 2025 21:12:16 +0800 Subject: [PATCH 39/57] Update swaylock color in vianix/hypridle.conf --- dots-extra/vianix/hypridle.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots-extra/vianix/hypridle.conf b/dots-extra/vianix/hypridle.conf index 6f1858607..11abc7e4e 100644 --- a/dots-extra/vianix/hypridle.conf +++ b/dots-extra/vianix/hypridle.conf @@ -1,4 +1,4 @@ -$lock_cmd = swaylock +$lock_cmd = swaylock -c 000000 # $lock_cmd = pidof hyprlock || hyprlock $suspend_cmd = systemctl suspend || loginctl suspend From cd0d49032a08d5dd02248bb0f95a8b50dab244e1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:37:44 +0100 Subject: [PATCH 40/57] readme: add warning for noobs --- .github/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/README.md b/.github/README.md index 2020ab82b..de4c9c790 100644 --- a/.github/README.md +++ b/.github/README.md @@ -28,6 +28,7 @@
Installation (illogical-impulse Quickshell) + - _If you're new to Linux and decide to use Hyprland, you're in for a tough ride._ - Just run `bash <(curl -s https://ii.clsty.link/get)` - Or, clone this repo and run `./setup install` - See [document](https://ii.clsty.link/en/ii-qs/01setup/) for details. From 32af8bf2576b41513cfcbf0782e61e7d1eb50c5d Mon Sep 17 00:00:00 2001 From: clsty Date: Thu, 6 Nov 2025 16:49:54 +0800 Subject: [PATCH 41/57] Fix gh action --- .github/workflows/dist-update-notification.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dist-update-notification.yml b/.github/workflows/dist-update-notification.yml index 341a906e8..93be79ca7 100644 --- a/.github/workflows/dist-update-notification.yml +++ b/.github/workflows/dist-update-notification.yml @@ -2,8 +2,12 @@ name: Comment on Discussion When sdata/dist-arch/ Changes on: push: + branches: + - main paths: - 'sdata/dist-arch/**' + paths-ignore: + - 'sdata/dist-arch/README.md' # workflow_dispatch: jobs: From 25a63b593d4bbbd67e34d7bc517b0ea0b29291e7 Mon Sep 17 00:00:00 2001 From: clsty Date: Thu, 6 Nov 2025 16:56:22 +0800 Subject: [PATCH 42/57] Fix gh action --- .github/workflows/dist-update-notification.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dist-update-notification.yml b/.github/workflows/dist-update-notification.yml index 93be79ca7..f9e971e12 100644 --- a/.github/workflows/dist-update-notification.yml +++ b/.github/workflows/dist-update-notification.yml @@ -5,9 +5,8 @@ on: branches: - main paths: - - 'sdata/dist-arch/**' - paths-ignore: - - 'sdata/dist-arch/README.md' + - "sdata/dist-arch/**" + - "!sdata/dist-arch/README.md" # workflow_dispatch: jobs: From 56a7e8cbdd10804ff5cb161e8c636ada91544c4f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:25:45 +0100 Subject: [PATCH 43/57] right sidebar: refractor volume mixer dialog content --- .../sidebarRight/volumeMixer/VolumeDialog.qml | 93 +-------------- .../volumeMixer/VolumeDialogContent.qml | 107 ++++++++++++++++++ 2 files changed, 109 insertions(+), 91 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialog.qml b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialog.qml index 58a7af71a..004440b25 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialog.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialog.qml @@ -11,87 +11,14 @@ import Quickshell.Services.Pipewire WindowDialog { id: root property bool isSink: true - function correctType(node) { - return (node.isSink === root.isSink) && node.audio - } - readonly property list appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list but it breaks ScriptModel - return root.correctType(node) && node.isStream - }) - readonly property bool hasApps: appPwNodes.length > 0 backgroundHeight: 700 WindowDialogTitle { text: root.isSink ? Translation.tr("Audio output") : Translation.tr("Audio input") } - WindowDialogSectionHeader { - visible: root.hasApps - text: Translation.tr("Applications") - } - - WindowDialogSeparator { - visible: root.hasApps - Layout.topMargin: -22 - Layout.leftMargin: 0 - Layout.rightMargin: 0 - } - - DialogSectionListView { - visible: root.hasApps - Layout.fillHeight: true - - model: ScriptModel { - values: root.appPwNodes - } - delegate: VolumeMixerEntry { - anchors { - left: parent?.left - right: parent?.right - } - required property var modelData - node: modelData - } - } - - WindowDialogSectionHeader { - text: Translation.tr("Devices") - } - - WindowDialogSeparator { - Layout.topMargin: -22 - Layout.leftMargin: 0 - Layout.rightMargin: 0 - } - - DialogSectionListView { - Layout.fillHeight: !root.hasApps - Layout.preferredHeight: 180 - - model: ScriptModel { - values: Pipewire.nodes.values.filter(node => { - return root.correctType(node) && !node.isStream - }) - } - delegate: StyledRadioButton { - id: radioButton - required property var modelData - anchors { - left: parent?.left - right: parent?.right - } - - description: modelData.description - checked: modelData.id === (root.isSink ? Pipewire.preferredDefaultAudioSink?.id : Pipewire.preferredDefaultAudioSource?.id) - - onCheckedChanged: { - if (!checked) return; - if (root.isSink) { - Pipewire.preferredDefaultAudioSink = modelData - } else { - Pipewire.preferredDefaultAudioSource = modelData - } - } - } + VolumeDialogContent { + isSink: root.isSink } WindowDialogSeparator { @@ -117,20 +44,4 @@ WindowDialog { onClicked: root.dismiss() } } - - component DialogSectionListView: StyledListView { - Layout.fillWidth: true - Layout.topMargin: -22 - Layout.bottomMargin: -16 - Layout.leftMargin: -Appearance.rounding.large - Layout.rightMargin: -Appearance.rounding.large - topMargin: 12 - bottomMargin: 12 - leftMargin: 20 - rightMargin: 20 - - clip: true - spacing: 4 - animateAppearance: false - } } diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml new file mode 100644 index 000000000..56f524968 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/sidebarRight/volumeMixer/VolumeDialogContent.qml @@ -0,0 +1,107 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.Pipewire + +ColumnLayout { + id: root + required property bool isSink + function correctType(node) { + return (node.isSink === root.isSink) && node.audio + } + readonly property list appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list but it breaks ScriptModel + return root.correctType(node) && node.isStream + }) + readonly property bool hasApps: appPwNodes.length > 0 + spacing: 16 + + WindowDialogSectionHeader { + visible: root.hasApps + text: Translation.tr("Applications") + } + + WindowDialogSeparator { + visible: root.hasApps + Layout.topMargin: -22 + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } + + DialogSectionListView { + visible: root.hasApps + Layout.fillHeight: true + + model: ScriptModel { + values: root.appPwNodes + } + delegate: VolumeMixerEntry { + anchors { + left: parent?.left + right: parent?.right + } + required property var modelData + node: modelData + } + } + + WindowDialogSectionHeader { + text: Translation.tr("Devices") + } + + WindowDialogSeparator { + Layout.topMargin: -22 + Layout.leftMargin: 0 + Layout.rightMargin: 0 + } + + DialogSectionListView { + Layout.fillHeight: !root.hasApps + Layout.preferredHeight: 180 + + model: ScriptModel { + values: Pipewire.nodes.values.filter(node => { + return root.correctType(node) && !node.isStream + }) + } + delegate: StyledRadioButton { + id: radioButton + required property var modelData + anchors { + left: parent?.left + right: parent?.right + } + + description: modelData.description + checked: modelData.id === (root.isSink ? Pipewire.preferredDefaultAudioSink?.id : Pipewire.preferredDefaultAudioSource?.id) + + onCheckedChanged: { + if (!checked) return; + if (root.isSink) { + Pipewire.preferredDefaultAudioSink = modelData + } else { + Pipewire.preferredDefaultAudioSource = modelData + } + } + } + } + + component DialogSectionListView: StyledListView { + Layout.fillWidth: true + Layout.topMargin: -22 + Layout.bottomMargin: -16 + Layout.leftMargin: -Appearance.rounding.large + Layout.rightMargin: -Appearance.rounding.large + topMargin: 12 + bottomMargin: 12 + leftMargin: 20 + rightMargin: 20 + + clip: true + spacing: 4 + animateAppearance: false + } +} From 4f68e9e61afb2a75452d26a6c0ec86830e005fee Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:29:59 +0100 Subject: [PATCH 44/57] add overlay --- dots/.config/hypr/hyprland/keybinds.conf | 2 +- dots/.config/hypr/hyprland/rules.conf | 2 +- dots/.config/quickshell/ii/GlobalStates.qml | 1 + .../widgets/AbstractBackgroundWidget.qml | 4 +- .../ii/modules/common/Persistent.qml | 16 ++ .../widgetCanvas/AbstractOverlayWidget.qml | 13 + .../widgets/widgetCanvas/WidgetCanvas.qml | 2 +- .../ii/modules/crosshair/Crosshair.qml | 57 ----- .../quickshell/ii/modules/overlay/Overlay.qml | 69 ++++++ .../ii/modules/overlay/OverlayContent.qml | 66 +++++ .../ii/modules/overlay/OverlayContext.qml | 37 +++ .../ii/modules/overlay/OverlayTaskbar.qml | 113 +++++++++ .../overlay/OverlayWidgetDelegateChooser.qml | 18 ++ .../modules/overlay/StyledOverlayWidget.qml | 230 ++++++++++++++++++ .../modules/overlay/crosshair/Crosshair.qml | 9 + .../crosshair/CrosshairContent.qml | 0 .../overlay/volumeMixer/VolumeMixer.qml | 24 ++ .../ii/modules/settings/InterfaceConfig.qml | 2 +- dots/.config/quickshell/ii/shell.qml | 6 +- 19 files changed, 605 insertions(+), 66 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractOverlayWidget.qml delete mode 100644 dots/.config/quickshell/ii/modules/crosshair/Crosshair.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/Overlay.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/OverlayTaskbar.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml create mode 100644 dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml rename dots/.config/quickshell/ii/modules/{ => overlay}/crosshair/CrosshairContent.qml (100%) create mode 100644 dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index b77166eb2..294854d67 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -33,7 +33,7 @@ bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls -bind = Super, G, global, quickshell:crosshairToggle # Toggle crosshair +bind = Super, G, global, quickshell:overlayToggle # Toggle overlay bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback) diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 7eb985c94..01ac1056d 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -134,11 +134,11 @@ layerrule = blur, quickshell:.* layerrule = ignorealpha 0.79, quickshell:.* layerrule = animation slide, quickshell:bar layerrule = animation slide bottom, quickshell:cheatsheet -layerrule = noanim, quickshell:crosshair layerrule = animation slide bottom, quickshell:dock layerrule = animation popin 120%, quickshell:screenCorners layerrule = noanim, quickshell:lockWindowPusher layerrule = animation fade, quickshell:notificationPopup +layerrule = noanim, quickshell:overlay layerrule = noanim, quickshell:overview layerrule = animation slide bottom, quickshell:osk layerrule = noanim, quickshell:polkit diff --git a/dots/.config/quickshell/ii/GlobalStates.qml b/dots/.config/quickshell/ii/GlobalStates.qml index 5cee09d96..972495c64 100644 --- a/dots/.config/quickshell/ii/GlobalStates.qml +++ b/dots/.config/quickshell/ii/GlobalStates.qml @@ -17,6 +17,7 @@ Singleton { property bool osdBrightnessOpen: false property bool osdVolumeOpen: false property bool oskOpen: false + property bool overlayOpen: false property bool overviewOpen: false property bool regionSelectorOpen: false property bool screenLocked: false diff --git a/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml index 4f16c51cd..b24c33aaa 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/AbstractBackgroundWidget.qml @@ -35,9 +35,9 @@ AbstractWidget { draggable: placementStrategy === "free" onReleased: { root.targetX = root.x; - root.targetY = root.y; + root.targetY = root.y; configEntry.x = root.targetX; - configEntry.y = root.targetY ; + configEntry.y = root.targetY; } property bool needsColText: false diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index a705de098..814938452 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -79,6 +79,22 @@ Singleton { property bool inhibit: false } + property JsonObject overlay: JsonObject { + property list open: ["crosshair"] + property JsonObject crosshair: JsonObject { + property bool pinned: false + property bool clickthrough: true + property real x: 100 + property real y: 100 + } + property JsonObject volumeMixer: JsonObject { + property bool pinned: false + property bool clickthrough: false + property real x: 55 + property real y: 188 + } + } + property JsonObject timer: JsonObject { property JsonObject pomodoro: JsonObject { property bool running: false diff --git a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractOverlayWidget.qml b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractOverlayWidget.qml new file mode 100644 index 000000000..10ce0f5cb --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/AbstractOverlayWidget.qml @@ -0,0 +1,13 @@ +import QtQuick +import Quickshell +import qs.modules.common + +/* + * Abstract widgets for an overlay. Doesn't contain any visuals. + */ +AbstractWidget { + id: root + + property bool pinned: false // Whether to stay visible when the overlay is dismissed + property bool clickthrough: true // When pinned, whether to allow clicks go through +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml index b40091432..d348ffae4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/widgetCanvas/WidgetCanvas.qml @@ -1,6 +1,6 @@ import QtQuick -Item { +MouseArea { id: root // uh this is stupid turns out we don't need anything here diff --git a/dots/.config/quickshell/ii/modules/crosshair/Crosshair.qml b/dots/.config/quickshell/ii/modules/crosshair/Crosshair.qml deleted file mode 100644 index f47902132..000000000 --- a/dots/.config/quickshell/ii/modules/crosshair/Crosshair.qml +++ /dev/null @@ -1,57 +0,0 @@ -import qs -import qs.modules.common -import qs.modules.common.widgets -import qs.services -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import Quickshell.Io -import Quickshell.Wayland -import Quickshell.Hyprland - -Scope { - id: root - - Loader { - id: crosshairLoader - active: GlobalStates.crosshairOpen - sourceComponent: PanelWindow { - id: crosshairWindow - exclusionMode: ExclusionMode.Ignore - WlrLayershell.namespace: "quickshell:crosshair" - WlrLayershell.layer: WlrLayer.Overlay - visible: true - color: "transparent" - - mask: Region { // Crosshair should not block mouse input - item: null - } - - implicitWidth: crosshairContent.implicitWidth - implicitHeight: crosshairContent.implicitHeight - - CrosshairContent { - id: crosshairContent - anchors.centerIn: parent - } - } - } - - IpcHandler { - target: "crosshair" - - function toggle(): void { - GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; - } - } - - GlobalShortcut { - name: "crosshairToggle" - description: "Toggles crosshair on press" - - onPressed: { - GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; - } - } -} diff --git a/dots/.config/quickshell/ii/modules/overlay/Overlay.qml b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml new file mode 100644 index 000000000..5054b9d1c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/Overlay.qml @@ -0,0 +1,69 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + + property Component regionComponent: Component { + Region {} + } + + Loader { + id: overlayLoader + active: GlobalStates.overlayOpen || OverlayContext.hasPinnedWidgets + sourceComponent: PanelWindow { + id: overlayWindow + exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "quickshell:overlay" + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + visible: true + color: "transparent" + + mask: Region { + item: GlobalStates.overlayOpen ? overlayContent : null + regions: OverlayContext.clickableWidgets.map((widget) => regionComponent.createObject(this, { + item: widget + })); + } + + anchors { + top: true + bottom: true + left: true + right: true + } + + OverlayContent { + id: overlayContent + anchors.fill: parent + } + } + } + + IpcHandler { + target: "overlay" + + function toggle(): void { + GlobalStates.overlayOpen = !GlobalStates.overlayOpen; + } + } + + GlobalShortcut { + name: "overlayToggle" + description: "Toggles overlay on press" + + onPressed: { + GlobalStates.overlayOpen = !GlobalStates.overlayOpen; + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml new file mode 100644 index 000000000..9322bd777 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml @@ -0,0 +1,66 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas + +import qs.modules.overlay.crosshair + +Item { + id: root + readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true + + Keys.onPressed: (event) => { // Esc to close + if (event.key === Qt.Key_Escape) { + GlobalStates.overlayOpen = false; + } + } + + property real initScale: 1.08 + scale: initScale + Component.onCompleted: { + scale = 1 + } + Behavior on scale { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) + } + + Rectangle { + id: bg + anchors.fill: parent + color: Appearance.colors.colScrim + opacity: (GlobalStates.overlayOpen && root.scale !== initScale) ? 1 : 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + + WidgetCanvas { + anchors.fill: parent + onClicked: GlobalStates.overlayOpen = false + + OverlayTaskbar { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: 50 + } + } + + Repeater { + model: ScriptModel { + values: Persistent.states.overlay.open.map(identifier => { + return OverlayContext.availableWidgets.find(w => w.identifier === identifier); + }) + objectProp: "identifier" + } + delegate: OverlayWidgetDelegateChooser { + + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml new file mode 100644 index 000000000..1417c8467 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml @@ -0,0 +1,37 @@ +pragma Singleton +pragma ComponentBehavior: Bound +import Quickshell + +Singleton { + id: root + + readonly property list availableWidgets: [ + { identifier: "crosshair", materialSymbol: "point_scan" }, + { identifier: "volumeMixer", materialSymbol: "volume_up" } + ] + + readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0 + + property list pinnedWidgetIdentifiers: [] + property list clickableWidgets: [] + + function pin(identifier: string, pin = true) { + if (pin) { + if (!root.pinnedWidgetIdentifiers.includes(identifier)) { + root.pinnedWidgetIdentifiers.push(identifier) + } + } else { + root.pinnedWidgetIdentifiers = root.pinnedWidgetIdentifiers.filter(id => id !== identifier) + } + } + + function registerClickableWidget(widget: var, clickable = true) { + if (clickable) { + if (!root.clickableWidgets.includes(widget)) { + root.clickableWidgets.push(widget) + } + } else { + root.clickableWidgets = root.clickableWidgets.filter(w => w !== widget) + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayTaskbar.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayTaskbar.qml new file mode 100644 index 000000000..65bb2a27e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayTaskbar.qml @@ -0,0 +1,113 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas + +Rectangle { + id: root + + property real padding: 8 + + opacity: GlobalStates.overlayOpen ? 1 : 0 + implicitWidth: contentRow.implicitWidth + (padding * 2) + implicitHeight: contentRow.implicitHeight + (padding * 2) + color: Appearance.m3colors.m3surfaceContainer + radius: Appearance.rounding.large + border.color: Appearance.colors.colOutlineVariant + border.width: 1 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + RowLayout { + id: contentRow + anchors { + fill: parent + margins: root.padding + } + spacing: 6 + + Row { + spacing: 4 + Repeater { + model: ScriptModel { + values: OverlayContext.availableWidgets + } + delegate: WidgetButton { + required property var modelData + identifier: modelData.identifier + materialSymbol: modelData.materialSymbol + } + } + } + + Separator {} + + TimeWidget {} + } + + component Separator: Rectangle { + implicitWidth: 1 + color: Appearance.colors.colOutlineVariant + Layout.fillHeight: true + Layout.topMargin: 10 + Layout.bottomMargin: 10 + } + + component TimeWidget: StyledText { + Layout.alignment: Qt.AlignVCenter + Layout.leftMargin: 8 + Layout.rightMargin: 6 + + text: DateTime.time + font { + family: Appearance.font.family.numbers + variableAxes: Appearance.font.variableAxes.numbers + pixelSize: 22 + } + } + + component WidgetButton: RippleButton { + id: widgetButton + required property string identifier + required property string materialSymbol + + Layout.alignment: Qt.AlignVCenter + + toggled: Persistent.states.overlay.open.includes(identifier) + onClicked: { + if (widgetButton.toggled) { + Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== identifier); + } else { + Persistent.states.overlay.open.push(identifier); + } + } + implicitWidth: implicitHeight + + colBackgroundToggled: Appearance.colors.colSecondaryContainer + colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover + colRippleToggled: Appearance.colors.colSecondaryContainerActive + + buttonRadius: root.radius - (root.height - height) / 2 + + contentItem: Item { + anchors.centerIn: parent + implicitWidth: 32 + implicitHeight: 32 + MaterialSymbol { + id: iconWidget + anchors.centerIn: parent + iconSize: 24 + text: widgetButton.materialSymbol + color: widgetButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurfaceVariant + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml new file mode 100644 index 000000000..e7420f625 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml @@ -0,0 +1,18 @@ +pragma ComponentBehavior: Bound +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Bluetooth +import qs.modules.overlay.crosshair +import qs.modules.overlay.volumeMixer + +DelegateChooser { + id: root + role: "identifier" + + DelegateChoice { roleValue: "crosshair"; Crosshair {} } + DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml new file mode 100644 index 000000000..9e00f6f92 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -0,0 +1,230 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Qt5Compat.GraphicalEffects +import qs +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.common.widgets.widgetCanvas + +/* + * To make an overlay widget: + * 1. Create a modules/overlay//.qml, using this as the base class + * 2. Add an entry to OverlayContext.availableWidgets with identifier= + * 3. Add an entry in Persistent.states.overlay. with x, y, pinned, clickthrough properties set to reasonable defaults + * 4. Add an entry in OverlayWidgetDelegateChooser with roleValue= and Declare your widget in there + * Use existing entries as reference. + */ +AbstractOverlayWidget { + id: root + + required property var modelData + required property Item contentItem + + readonly property string identifier: modelData.identifier + readonly property string materialSymbol: modelData.materialSymbol ?? "widgets" + property string title: identifier.replace(/([A-Z])/g, " $1").replace(/^./, function(str){ return str.toUpperCase(); }) + property var persistentStateEntry: Persistent.states.overlay[identifier] + property real radius: Appearance.rounding.windowRounding + property real minWidth: 250 + + draggable: GlobalStates.overlayOpen + x: Math.round(persistentStateEntry.x) // Round or it'll be blurry + y: Math.round(persistentStateEntry.y) // Round or it'll be blurry + pinned: persistentStateEntry.pinned + clickthrough: persistentStateEntry.clickthrough + drag { + minimumX: 0 + minimumY: 0 + maximumX: root.parent.width - root.width + maximumY: root.parent.height - root.height + } + + // Guarded states & registration funcs + property bool open: Persistent.states.overlay.open + property bool actuallyPinned: pinned && open + property bool actuallyClickable: !clickthrough && actuallyPinned && open + onActuallyPinnedChanged: reportPinnedState(); + onActuallyClickableChanged: reportClickableState(); + function reportPinnedState() { + OverlayContext.pin(identifier, actuallyPinned); + } + function reportClickableState() { + OverlayContext.registerClickableWidget(contentItem, actuallyClickable); + } + + // Self-registeration with OverlayContext + Component.onCompleted: { + reportPinnedState(); + reportClickableState(); + } + + // Hooks + onReleased: savePosition(); + + function close() { + Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== root.identifier); + } + + function togglePinned() { + persistentStateEntry.pinned = !persistentStateEntry.pinned; + } + + function toggleClickthrough() { + persistentStateEntry.clickthrough = !persistentStateEntry.clickthrough; + } + + function savePosition(xPos = root.x, yPos = root.y) { + persistentStateEntry.x = xPos; + persistentStateEntry.y = yPos; + } + + function center() { + const targetX = (root.parent.width - contentColumn.width) / 2 + const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight + root.x = targetX + root.y = targetY + root.savePosition(targetX, targetY) + } + + visible: GlobalStates.overlayOpen || actuallyPinned + implicitWidth: Math.max(contentColumn.implicitWidth, minWidth) + implicitHeight: contentColumn.implicitHeight + + Rectangle { + id: border + anchors.fill: parent + color: "transparent" + radius: root.radius + border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1) + border.width: 1 + + layer.enabled: GlobalStates.overlayOpen + layer.effect: OpacityMask { + maskSource: Rectangle { + width: border.width + height: border.height + radius: root.radius + } + } + + Column { + id: contentColumn + z: -1 + anchors.fill: parent + + // Title bar + Rectangle { + id: titleBar + opacity: GlobalStates.overlayOpen ? 1 : 0 + anchors { + left: parent.left + right: parent.right + } + property real padding: 2 + implicitWidth: titleBarRow.implicitWidth + padding * 2 + implicitHeight: titleBarRow.implicitHeight + padding * 2 + color: Appearance.m3colors.m3surfaceContainer + border.color: Appearance.colors.colOutlineVariant + border.width: 1 + + RowLayout { + id: titleBarRow + anchors { + fill: parent + margins: titleBar.padding + leftMargin: titleBar.padding + 8 + } + spacing: 0 + + MaterialSymbol { + text: root.materialSymbol + iconSize: 20 + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: 4 + } + + StyledText { + text: root.title + Layout.fillWidth: true + elide: Text.ElideRight + } + + TitlebarButton { + materialSymbol: "recenter" + onClicked: root.center() + StyledToolTip { + text: "Center" + } + } + + TitlebarButton { + materialSymbol: "mouse" + toggled: !root.clickthrough + onClicked: root.toggleClickthrough() + StyledToolTip { + text: "Clickable when pinned" + } + } + + TitlebarButton { + materialSymbol: "keep" + toggled: root.pinned + onClicked: root.togglePinned() + StyledToolTip { + text: "Pin" + } + } + + TitlebarButton { + materialSymbol: "close" + onClicked: root.close() + StyledToolTip { + text: "Close" + } + } + } + } + + // Content + Item { + id: contentContainer + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: root.contentItem.implicitWidth + implicitHeight: root.contentItem.implicitHeight + children: [root.contentItem] + } + } + } + + + component TitlebarButton: RippleButton { + id: titlebarButton + required property string materialSymbol + buttonRadius: height / 2 + implicitHeight: contentItem.implicitHeight + implicitWidth: implicitHeight + padding: 0 + + colBackgroundToggled: Appearance.colors.colSecondaryContainer + colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover + colRippleToggled: Appearance.colors.colSecondaryContainerActive + + contentItem: Item { + anchors.centerIn: parent + implicitWidth: 30 + implicitHeight: 30 + + MaterialSymbol { + id: iconWidget + anchors.centerIn: parent + iconSize: 20 + text: titlebarButton.materialSymbol + fill: titlebarButton.toggled + color: titlebarButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurface + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml new file mode 100644 index 000000000..abb1cbf26 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/crosshair/Crosshair.qml @@ -0,0 +1,9 @@ +import QtQuick +import Quickshell +import qs.modules.common +import qs.modules.overlay + +StyledOverlayWidget { + id: root + contentItem: CrosshairContent {} +} diff --git a/dots/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml b/dots/.config/quickshell/ii/modules/overlay/crosshair/CrosshairContent.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml rename to dots/.config/quickshell/ii/modules/overlay/crosshair/CrosshairContent.qml diff --git a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml new file mode 100644 index 000000000..433a2e11f --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml @@ -0,0 +1,24 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.modules.common +import qs.modules.overlay +import qs.modules.sidebarRight.volumeMixer + +StyledOverlayWidget { + id: root + contentItem: Rectangle { + anchors.centerIn: parent + color: Appearance.m3colors.m3surfaceContainer + property real padding: 16 + implicitHeight: 700 + implicitWidth: 400 + + VolumeDialogContent { + anchors.fill: parent + anchors.margins: parent.padding + isSink: true + } + + } +} diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index b7bff73b1..a373e9959 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -26,7 +26,7 @@ ContentPage { Layout.leftMargin: 10 color: Appearance.colors.colSubtext font.pixelSize: Appearance.font.pixelSize.smallie - text: Translation.tr("Press Super+G to toggle appearance") + text: Translation.tr("Press Super+G to open the overlay and pin the crosshair") } Item { Layout.fillWidth: true diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index d50f6640f..209597fcf 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -11,7 +11,6 @@ import qs.modules.common import qs.modules.background import qs.modules.bar import qs.modules.cheatsheet -import qs.modules.crosshair import qs.modules.dock import qs.modules.lock import qs.modules.mediaControls @@ -25,6 +24,7 @@ import qs.modules.screenCorners import qs.modules.sessionScreen import qs.modules.sidebarLeft import qs.modules.sidebarRight +import qs.modules.overlay import qs.modules.verticalBar import qs.modules.wallpaperSelector @@ -39,7 +39,6 @@ ShellRoot { property bool enableBar: true property bool enableBackground: true property bool enableCheatsheet: true - property bool enableCrosshair: true property bool enableDock: true property bool enableLock: true property bool enableMediaControls: true @@ -47,6 +46,7 @@ ShellRoot { property bool enablePolkit: true property bool enableOnScreenDisplay: true property bool enableOnScreenKeyboard: true + property bool enableOverlay: true property bool enableOverview: true property bool enableRegionSelector: true property bool enableReloadPopup: true @@ -70,13 +70,13 @@ ShellRoot { LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} } LazyLoader { active: enableBackground; component: Background {} } LazyLoader { active: enableCheatsheet; component: Cheatsheet {} } - LazyLoader { active: enableCrosshair; component: Crosshair {} } LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} } LazyLoader { active: enableLock; component: Lock {} } LazyLoader { active: enableMediaControls; component: MediaControls {} } LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} } LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} } LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} } + LazyLoader { active: enableOverlay; component: Overlay {} } LazyLoader { active: enableOverview; component: Overview {} } LazyLoader { active: enablePolkit; component: Polkit {} } LazyLoader { active: enableRegionSelector; component: RegionSelector {} } From 6e9f2c14ce071778ca2ae0323bb54b3b22d03f50 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:38:35 +0100 Subject: [PATCH 45/57] clarify overlay widget instructions a bit --- .../quickshell/ii/modules/overlay/StyledOverlayWidget.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml index 9e00f6f92..5e6a0ce3f 100644 --- a/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/overlay/StyledOverlayWidget.qml @@ -11,7 +11,7 @@ import qs.modules.common.widgets.widgetCanvas /* * To make an overlay widget: - * 1. Create a modules/overlay//.qml, using this as the base class + * 1. Create a modules/overlay//.qml, using this as the base class and declare your widget content as contentItem * 2. Add an entry to OverlayContext.availableWidgets with identifier= * 3. Add an entry in Persistent.states.overlay. with x, y, pinned, clickthrough properties set to reasonable defaults * 4. Add an entry in OverlayWidgetDelegateChooser with roleValue= and Declare your widget in there @@ -20,9 +20,9 @@ import qs.modules.common.widgets.widgetCanvas AbstractOverlayWidget { id: root - required property var modelData required property Item contentItem + required property var modelData readonly property string identifier: modelData.identifier readonly property string materialSymbol: modelData.materialSymbol ?? "widgets" property string title: identifier.replace(/([A-Z])/g, " $1").replace(/^./, function(str){ return str.toUpperCase(); }) From 3bebabd95eba9d0941376fe238bc5143f8e6aedc Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:54:00 +0100 Subject: [PATCH 46/57] settings: extend japenis/roman ws numbers --- dots/.config/quickshell/ii/modules/settings/BarConfig.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml index 2ad39ceca..9738bf49c 100644 --- a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml @@ -318,17 +318,17 @@ ContentPage { { displayName: Translation.tr("Normal"), icon: "timer_10", - value: '["1","2","3","4","5","6","7","8","9","10"]' + value: '[]' }, { displayName: Translation.tr("Japanese"), icon: "square_dot", - value: '["一","二","三","四","五","六","七","八","九","十"]' + value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]' }, { displayName: Translation.tr("Roman"), icon: "account_balance", - value: '["I","II","III","IV","V","VI","VII","VIII","IX","X"]' + value: '["I","II","III","IV","V","VI","VII","VIII","IX","X","XI","XII","XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX"]' } ] } From 6be3fe0c65595eef7396f97a2ba3c0417c577523 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:55:33 +0100 Subject: [PATCH 47/57] overlay: add option for zoom anim and darkening surface --- .../quickshell/ii/modules/common/Config.qml | 5 + .../ii/modules/overlay/OverlayContent.qml | 3 +- .../ii/modules/settings/InterfaceConfig.qml | 100 +++++++++++------- 3 files changed, 68 insertions(+), 40 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 7d07360b3..6cfcd3637 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -361,6 +361,11 @@ Singleton { property bool pinnedOnStartup: false } + property JsonObject overlay: JsonObject { + property bool openingZoomAnimation: true + property bool darkenScreen: true + } + property JsonObject overview: JsonObject { property bool enable: true property real scale: 0.18 // Relative to screen size diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml index 9322bd777..838267b80 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContent.qml @@ -20,7 +20,7 @@ Item { } } - property real initScale: 1.08 + property real initScale: Config.options.overlay.openingZoomAnimation ? 1.08 : 1.000001 scale: initScale Component.onCompleted: { scale = 1 @@ -33,6 +33,7 @@ Item { id: bg anchors.fill: parent color: Appearance.colors.colScrim + visible: Config.options.overlay.darkenScreen && opacity > 0 opacity: (GlobalStates.overlayOpen && root.scale !== initScale) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index a373e9959..c26cce455 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -7,45 +7,6 @@ import qs.modules.common.widgets ContentPage { forceWidth: true - ContentSection { - icon: "point_scan" - title: Translation.tr("Crosshair overlay") - - MaterialTextArea { - Layout.fillWidth: true - placeholderText: Translation.tr("Crosshair code (in Valorant's format)") - text: Config.options.crosshair.code - wrapMode: TextEdit.Wrap - onTextChanged: { - Config.options.crosshair.code = text; - } - } - - RowLayout { - StyledText { - Layout.leftMargin: 10 - color: Appearance.colors.colSubtext - font.pixelSize: Appearance.font.pixelSize.smallie - text: Translation.tr("Press Super+G to open the overlay and pin the crosshair") - } - Item { - Layout.fillWidth: true - } - RippleButtonWithIcon { - id: editorButton - buttonRadius: Appearance.rounding.full - materialIcon: "open_in_new" - mainText: Translation.tr("Open editor") - onClicked: { - Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`); - } - StyledToolTip { - text: "www.vcrdb.net" - } - } - } - } - ContentSection { icon: "call_to_action" title: Translation.tr("Dock") @@ -214,6 +175,67 @@ ContentPage { } } + ContentSection { + icon: "select_window" + title: Translation.tr("Overlay: General") + + ConfigSwitch { + buttonIcon: "high_density" + text: Translation.tr("Enable opening zoom animation") + checked: Config.options.overlay.openingZoomAnimation + onCheckedChanged: { + Config.options.overlay.openingZoomAnimation = checked; + } + } + ConfigSwitch { + buttonIcon: "texture" + text: Translation.tr("Darken screen") + checked: Config.options.overlay.darkenScreen + onCheckedChanged: { + Config.options.overlay.darkenScreen = checked; + } + } + } + + ContentSection { + icon: "point_scan" + title: Translation.tr("Overlay: Crosshair") + + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Crosshair code (in Valorant's format)") + text: Config.options.crosshair.code + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.crosshair.code = text; + } + } + + RowLayout { + StyledText { + Layout.leftMargin: 10 + color: Appearance.colors.colSubtext + font.pixelSize: Appearance.font.pixelSize.smallie + text: Translation.tr("Press Super+G to open the overlay and pin the crosshair") + } + Item { + Layout.fillWidth: true + } + RippleButtonWithIcon { + id: editorButton + buttonRadius: Appearance.rounding.full + materialIcon: "open_in_new" + mainText: Translation.tr("Open editor") + onClicked: { + Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`); + } + StyledToolTip { + text: "www.vcrdb.net" + } + } + } + } + ContentSection { icon: "screenshot_frame_2" title: Translation.tr("Region selector (screen snipping/Google Lens)") From ad9f25c34684636ce961090233e3daff80b1cbd2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:08:14 +0100 Subject: [PATCH 48/57] weather widget: make number and icon further apart --- .../modules/background/widgets/weather/WeatherWidget.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml index 5b598d24a..322772cbe 100644 --- a/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml +++ b/dots/.config/quickshell/ii/modules/background/widgets/weather/WeatherWidget.qml @@ -37,8 +37,8 @@ AbstractBackgroundWidget { anchors { right: parent.right top: parent.top - rightMargin: 20 - topMargin: 24 + rightMargin: 16 + topMargin: 20 } } @@ -50,8 +50,8 @@ AbstractBackgroundWidget { left: parent.left bottom: parent.bottom - leftMargin: 20 - bottomMargin: 24 + leftMargin: 16 + bottomMargin: 20 } } } From 2f1c66570f5ed0135b368cba5bf9b6e81f8e70f4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:14:51 +0100 Subject: [PATCH 49/57] Change battery health icon from 'healing' to 'heart_check' --- dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 47345ee1f..037313e3f 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -90,7 +90,7 @@ StyledPopup { Layout.fillWidth: true MaterialSymbol { - text: "healing" + text: "heart_check" color: Appearance.colors.colOnSurfaceVariant iconSize: Appearance.font.pixelSize.large } From 06c51553ba5636edce9a67fe1d817763b6c3dde8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:35:31 +0100 Subject: [PATCH 50/57] refractor bar tooltips --- .../ii/modules/bar/BatteryPopup.qml | 161 +++++------------- .../quickshell/ii/modules/bar/ClockWidget.qml | 2 +- .../ii/modules/bar/ClockWidgetPopup.qml | 70 ++++++++ .../ii/modules/bar/ClockWidgetTooltip.qml | 110 ------------ .../ii/modules/bar/ResourcesPopup.qml | 73 ++------ .../ii/modules/bar/StyledPopupHeaderRow.qml | 30 ++++ .../ii/modules/bar/StyledPopupValueRow.qml | 29 ++++ .../verticalBar/VerticalClockWidget.qml | 2 +- .../ii/modules/verticalBar/VerticalMedia.qml | 25 +-- 9 files changed, 184 insertions(+), 318 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/bar/ClockWidgetPopup.qml delete mode 100644 dots/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml create mode 100644 dots/.config/quickshell/ii/modules/bar/StyledPopupHeaderRow.qml create mode 100644 dots/.config/quickshell/ii/modules/bar/StyledPopupValueRow.qml diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 037313e3f..9e3dc49bd 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -13,143 +13,58 @@ StyledPopup { spacing: 4 // Header - Row { - id: header - spacing: 5 - - MaterialSymbol { - anchors.verticalCenter: parent.verticalCenter - fill: 0 - font.weight: Font.Medium - text: "battery_android_full" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: "Battery" - font { - weight: Font.Medium - pixelSize: Appearance.font.pixelSize.normal - } - color: Appearance.colors.colOnSurfaceVariant - } + StyledPopupHeaderRow { + icon: "battery_android_full" + label: Translation.tr("Battery") } - // This row is hidden when the battery is full. - RowLayout { - spacing: 5 - Layout.fillWidth: true - property bool rowVisible: { + StyledPopupValueRow { + visible: { let timeValue = Battery.isCharging ? Battery.timeToFull : Battery.timeToEmpty; let power = Battery.energyRate; return !(Battery.chargeState == 4 || timeValue <= 0 || power <= 0.01); } - visible: rowVisible - opacity: rowVisible ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 500 - } - } - - MaterialSymbol { - text: "schedule" - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - StyledText { - text: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: { - function formatTime(seconds) { - var h = Math.floor(seconds / 3600); - var m = Math.floor((seconds % 3600) / 60); - if (h > 0) - return `${h}h, ${m}m`; - else - return `${m}m`; - } - if (Battery.isCharging) - return formatTime(Battery.timeToFull); + icon: "schedule" + label: Battery.isCharging ? Translation.tr("Time to full:") : Translation.tr("Time to empty:") + value: { + function formatTime(seconds) { + var h = Math.floor(seconds / 3600); + var m = Math.floor((seconds % 3600) / 60); + if (h > 0) + return `${h}h, ${m}m`; else - return formatTime(Battery.timeToEmpty); + return `${m}m`; } + if (Battery.isCharging) + return formatTime(Battery.timeToFull); + else + return formatTime(Battery.timeToEmpty); } } - RowLayout { - spacing: 5 - visible: Battery.health > 0 - Layout.fillWidth: true + StyledPopupValueRow { + icon: "heart_check" + label: Translation.tr("Health:") + value: `${(Battery.health).toFixed(1)}%` + } - MaterialSymbol { - text: "heart_check" - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - - StyledText { - text: Translation.tr("Health:") - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: `${(Battery.health).toFixed(1)}%` + StyledPopupValueRow { + visible: !(Battery.chargeState != 4 && Battery.energyRate == 0) + icon: "bolt" + label: { + if (Battery.chargeState == 4) { + return Translation.tr("Fully charged"); + } else if (Battery.chargeState == 1) { + return Translation.tr("Charging:"); + } else { + return Translation.tr("Discharging:"); } } - - RowLayout { - spacing: 5 - Layout.fillWidth: true - - property bool rowVisible: !(Battery.chargeState != 4 && Battery.energyRate == 0) - visible: rowVisible - opacity: rowVisible ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 500 - } - } - - MaterialSymbol { - text: "bolt" - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - - StyledText { - text: { - if (Battery.chargeState == 4) { - return Translation.tr("Fully charged"); - } else if (Battery.chargeState == 1) { - return Translation.tr("Charging:"); - } else { - return Translation.tr("Discharging:"); - } - } - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: { - if (Battery.chargeState == 4) { - return ""; - } else { - return `${Battery.energyRate.toFixed(2)}W`; - } + value: { + if (Battery.chargeState == 4) { + return ""; + } else { + return `${Battery.energyRate.toFixed(2)}W`; } } } diff --git a/dots/.config/quickshell/ii/modules/bar/ClockWidget.qml b/dots/.config/quickshell/ii/modules/bar/ClockWidget.qml index 4d6fb61f2..895ad9be0 100644 --- a/dots/.config/quickshell/ii/modules/bar/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/bar/ClockWidget.qml @@ -43,7 +43,7 @@ Item { hoverEnabled: true acceptedButtons: Qt.NoButton - ClockWidgetTooltip { + ClockWidgetPopup { hoverTarget: mouseArea } } diff --git a/dots/.config/quickshell/ii/modules/bar/ClockWidgetPopup.qml b/dots/.config/quickshell/ii/modules/bar/ClockWidgetPopup.qml new file mode 100644 index 000000000..c23edb7b4 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/bar/ClockWidgetPopup.qml @@ -0,0 +1,70 @@ +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Layouts + +StyledPopup { + id: root + property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy") + property string formattedTime: DateTime.time + property string formattedUptime: DateTime.uptime + property string todosSection: getUpcomingTodos() + + function getUpcomingTodos() { + const unfinishedTodos = Todo.list.filter(function (item) { + return !item.done; + }); + if (unfinishedTodos.length === 0) { + return Translation.tr("No pending tasks"); + } + + // Limit to first 5 todos to keep popup manageable + const limitedTodos = unfinishedTodos.slice(0, 5); + let todoText = limitedTodos.map(function (item, index) { + return ` ${index + 1}. ${item.content}`; + }).join('\n'); + + if (unfinishedTodos.length > 5) { + todoText += `\n ${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`; + } + + return todoText; + } + + ColumnLayout { + id: columnLayout + anchors.centerIn: parent + spacing: 4 + + StyledPopupHeaderRow { + icon: "calendar_month" + label: root.formattedDate + } + + StyledPopupValueRow { + icon: "timelapse" + label: Translation.tr("System uptime:") + value: root.formattedUptime + } + + // Tasks + Column { + spacing: 0 + Layout.fillWidth: true + + StyledPopupValueRow { + icon: "checklist" + label: Translation.tr("To Do:") + value: "" + } + + StyledText { + horizontalAlignment: Text.AlignLeft + wrapMode: Text.Wrap + color: Appearance.colors.colOnSurfaceVariant + text: root.todosSection + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml b/dots/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml deleted file mode 100644 index 734d89b22..000000000 --- a/dots/.config/quickshell/ii/modules/bar/ClockWidgetTooltip.qml +++ /dev/null @@ -1,110 +0,0 @@ -import qs.modules.common -import qs.modules.common.widgets -import qs.services -import QtQuick -import QtQuick.Layouts - -StyledPopup { - id: root - property string formattedDate: Qt.locale().toString(DateTime.clock.date, "dddd, MMMM dd, yyyy") - property string formattedTime: DateTime.time - property string formattedUptime: DateTime.uptime - property string todosSection: getUpcomingTodos() - - function getUpcomingTodos() { - const unfinishedTodos = Todo.list.filter(function (item) { - return !item.done; - }); - if (unfinishedTodos.length === 0) { - return Translation.tr("No pending tasks"); - } - - // Limit to first 5 todos to keep popup manageable - const limitedTodos = unfinishedTodos.slice(0, 5); - let todoText = limitedTodos.map(function (item, index) { - return `${index + 1}. ${item.content}`; - }).join('\n'); - - if (unfinishedTodos.length > 5) { - todoText += `\n${Translation.tr("... and %1 more").arg(unfinishedTodos.length - 5)}`; - } - - return todoText; - } - - ColumnLayout { - id: columnLayout - anchors.centerIn: parent - spacing: 4 - - // Date + Time row - Row { - spacing: 5 - - MaterialSymbol { - anchors.verticalCenter: parent.verticalCenter - fill: 0 - font.weight: Font.Medium - text: "calendar_month" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - color: Appearance.colors.colOnSurfaceVariant - text: `${root.formattedDate}` - font.weight: Font.Medium - } - } - - // Uptime row - RowLayout { - spacing: 5 - Layout.fillWidth: true - MaterialSymbol { - text: "timelapse" - color: Appearance.colors.colOnSurfaceVariant - font.pixelSize: Appearance.font.pixelSize.large - } - StyledText { - text: Translation.tr("System uptime:") - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - color: Appearance.colors.colOnSurfaceVariant - text: root.formattedUptime - } - } - - // Tasks - Column { - spacing: 0 - Layout.fillWidth: true - - Row { - spacing: 4 - MaterialSymbol { - anchors.verticalCenter: parent.verticalCenter - text: "checklist" - color: Appearance.colors.colOnSurfaceVariant - font.pixelSize: Appearance.font.pixelSize.large - } - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: Translation.tr("To Do:") - color: Appearance.colors.colOnSurfaceVariant - } - } - - StyledText { - horizontalAlignment: Text.AlignLeft - wrapMode: Text.Wrap - color: Appearance.colors.colOnSurfaceVariant - text: root.todosSection - } - } - } -} diff --git a/dots/.config/quickshell/ii/modules/bar/ResourcesPopup.qml b/dots/.config/quickshell/ii/modules/bar/ResourcesPopup.qml index 1cac240d8..40ed72756 100644 --- a/dots/.config/quickshell/ii/modules/bar/ResourcesPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/ResourcesPopup.qml @@ -12,57 +12,6 @@ StyledPopup { return (kb / (1024 * 1024)).toFixed(1) + " GB"; } - component ResourceItem: RowLayout { - id: resourceItem - required property string icon - required property string label - required property string value - spacing: 4 - - MaterialSymbol { - text: resourceItem.icon - color: Appearance.colors.colOnSurfaceVariant - iconSize: Appearance.font.pixelSize.large - } - StyledText { - text: resourceItem.label - color: Appearance.colors.colOnSurfaceVariant - } - StyledText { - Layout.fillWidth: true - horizontalAlignment: Text.AlignRight - visible: resourceItem.value !== "" - color: Appearance.colors.colOnSurfaceVariant - text: resourceItem.value - } - } - - component ResourceHeaderItem: Row { - id: headerItem - required property var icon - required property var label - spacing: 5 - - MaterialSymbol { - anchors.verticalCenter: parent.verticalCenter - fill: 0 - font.weight: Font.Medium - text: headerItem.icon - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: headerItem.label - font { - weight: Font.Medium - pixelSize: Appearance.font.pixelSize.normal - } - color: Appearance.colors.colOnSurfaceVariant - } - } - Row { anchors.centerIn: parent spacing: 12 @@ -71,23 +20,23 @@ StyledPopup { anchors.top: parent.top spacing: 8 - ResourceHeaderItem { + StyledPopupHeaderRow { icon: "memory" label: "RAM" } Column { spacing: 4 - ResourceItem { + StyledPopupValueRow { icon: "clock_loader_60" label: Translation.tr("Used:") value: root.formatKB(ResourceUsage.memoryUsed) } - ResourceItem { + StyledPopupValueRow { icon: "check_circle" label: Translation.tr("Free:") value: root.formatKB(ResourceUsage.memoryFree) } - ResourceItem { + StyledPopupValueRow { icon: "empty_dashboard" label: Translation.tr("Total:") value: root.formatKB(ResourceUsage.memoryTotal) @@ -100,23 +49,23 @@ StyledPopup { anchors.top: parent.top spacing: 8 - ResourceHeaderItem { + StyledPopupHeaderRow { icon: "swap_horiz" label: "Swap" } Column { spacing: 4 - ResourceItem { + StyledPopupValueRow { icon: "clock_loader_60" label: Translation.tr("Used:") value: root.formatKB(ResourceUsage.swapUsed) } - ResourceItem { + StyledPopupValueRow { icon: "check_circle" label: Translation.tr("Free:") value: root.formatKB(ResourceUsage.swapFree) } - ResourceItem { + StyledPopupValueRow { icon: "empty_dashboard" label: Translation.tr("Total:") value: root.formatKB(ResourceUsage.swapTotal) @@ -128,16 +77,16 @@ StyledPopup { anchors.top: parent.top spacing: 8 - ResourceHeaderItem { + StyledPopupHeaderRow { icon: "planner_review" label: "CPU" } Column { spacing: 4 - ResourceItem { + StyledPopupValueRow { icon: "bolt" label: Translation.tr("Load:") - value: (ResourceUsage.cpuUsage > 0.8 ? Translation.tr("High") : ResourceUsage.cpuUsage > 0.4 ? Translation.tr("Medium") : Translation.tr("Low")) + ` (${Math.round(ResourceUsage.cpuUsage * 100)}%)` + value: `${Math.round(ResourceUsage.cpuUsage * 100)}%` } } } diff --git a/dots/.config/quickshell/ii/modules/bar/StyledPopupHeaderRow.qml b/dots/.config/quickshell/ii/modules/bar/StyledPopupHeaderRow.qml new file mode 100644 index 000000000..f5c7ba445 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/bar/StyledPopupHeaderRow.qml @@ -0,0 +1,30 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets + +Row { + id: root + required property var icon + required property var label + spacing: 5 + + MaterialSymbol { + anchors.verticalCenter: parent.verticalCenter + fill: 0 + font.weight: Font.DemiBold + text: root.icon + iconSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnSurfaceVariant + } + + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: root.label + font { + weight: Font.DemiBold + pixelSize: Appearance.font.pixelSize.normal + } + color: Appearance.colors.colOnSurfaceVariant + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/bar/StyledPopupValueRow.qml b/dots/.config/quickshell/ii/modules/bar/StyledPopupValueRow.qml new file mode 100644 index 000000000..de8ac579e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/bar/StyledPopupValueRow.qml @@ -0,0 +1,29 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets + +RowLayout { + id: root + required property string icon + required property string label + required property string value + spacing: 4 + + MaterialSymbol { + text: root.icon + color: Appearance.colors.colOnSurfaceVariant + iconSize: Appearance.font.pixelSize.large + } + StyledText { + text: root.label + color: Appearance.colors.colOnSurfaceVariant + } + StyledText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + visible: root.value !== "" + color: Appearance.colors.colOnSurfaceVariant + text: root.value + } +} diff --git a/dots/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml b/dots/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml index 391d2e78c..921f63603 100644 --- a/dots/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/verticalBar/VerticalClockWidget.qml @@ -36,7 +36,7 @@ Item { hoverEnabled: true acceptedButtons: Qt.NoButton - Bar.ClockWidgetTooltip { + Bar.ClockWidgetPopup { hoverTarget: mouseArea } } diff --git a/dots/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml b/dots/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml index 7a512564a..677941d64 100644 --- a/dots/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml +++ b/dots/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml @@ -74,30 +74,13 @@ MouseArea { anchors.centerIn: parent spacing: 4 - Row { - spacing: 4 - - MaterialSymbol { - anchors.verticalCenter: parent.verticalCenter - fill: 0 - font.weight: Font.Medium - text: "music_note" - iconSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnSurfaceVariant - } - - StyledText { - anchors.verticalCenter: parent.verticalCenter - text: "Media" - font { - weight: Font.Medium - pixelSize: Appearance.font.pixelSize.normal - } - color: Appearance.colors.colOnSurfaceVariant - } + Bar.StyledPopupHeaderRow { + icon: "music_note" + label: Translation.tr("Media") } StyledText { + color: Appearance.colors.colOnSurfaceVariant text: `${cleanedTitle}${activePlayer?.trackArtist ? '\n' + activePlayer.trackArtist : ''}` } } From 47aa8232f7e583b0b9e9ac5a8462be8dafb97b81 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Thu, 6 Nov 2025 23:39:32 +0300 Subject: [PATCH 51/57] fixes --- .../regionSelector/RegionSelection.qml | 25 +++++++++++-------- .../quickshell/ii/services/DateTime.qml | 1 - 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml index 9d984d5ec..2af55f014 100644 --- a/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/regionSelector/RegionSelection.qml @@ -33,9 +33,9 @@ PanelWindow { property var selectionMode: RegionSelection.SelectionMode.RectCorners signal dismiss() - property string permanentScreenshotDir: Config.options.screenSnip.savePath && Config.options.screenSnip.savePath !== "" - ? Config.options.screenSnip.savePath - : "" + property string saveScreenshotDir: Config.options.screenSnip.savePath !== "" + ? Config.options.screenSnip.savePath + : "" property string screenshotDir: Directories.screenshotTemp property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl @@ -262,20 +262,23 @@ PanelWindow { } switch (root.action) { case RegionSelection.SnipAction.Copy: - if (permanentScreenshotDir === "") { - // no permanent dir, just copy to clipboard + if (saveScreenshotDir === "") { + // not saving the screenshot, just copy to clipboard snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] break; } - const saveFileName = 'screenshot-' + DateTime.fileDateTime + '.png' - const savePath = `${root.permanentScreenshotDir}/${saveFileName}` + + const savePathBase = root.saveScreenshotDir snipProc.command = [ - "bash", "-c", - `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.permanentScreenshotDir)}' ` + - `&& ${cropToStdout} | tee >(wl-copy) > '${StringUtils.shellSingleQuoteEscape(savePath)}' ` + - `&& ${cleanup}` + "bash", "-c", + `mkdir -p '${StringUtils.shellSingleQuoteEscape(savePathBase)}' && \ + saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \ + savePath="${savePathBase}/$saveFileName" && \ + ${cropToStdout} | tee >(wl-copy) > "$savePath" && \ + ${cleanup}` ] + break; case RegionSelection.SnipAction.Edit: snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`] diff --git a/dots/.config/quickshell/ii/services/DateTime.qml b/dots/.config/quickshell/ii/services/DateTime.qml index c6998f056..62d296dbc 100644 --- a/dots/.config/quickshell/ii/services/DateTime.qml +++ b/dots/.config/quickshell/ii/services/DateTime.qml @@ -22,7 +22,6 @@ Singleton { property string shortDate: Qt.locale().toString(clock.date, Config.options?.time.shortDateFormat ?? "dd/MM") property string date: Qt.locale().toString(clock.date, Config.options?.time.dateFormat ?? "dddd, dd/MM") property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy") - property string fileDateTime: Qt.locale().toString(clock.date, "dd-MM-yyyy_HH.mm") property string uptime: "0h, 0m" Timer { From e5e598853fb387f86b26ef7cde2eee07ded088d3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:49:03 +0100 Subject: [PATCH 52/57] japanese borrows chinese chars... --- dots/.config/quickshell/ii/modules/settings/BarConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml index 9738bf49c..c00ef30cd 100644 --- a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml @@ -321,7 +321,7 @@ ContentPage { value: '[]' }, { - displayName: Translation.tr("Japanese"), + displayName: Translation.tr("Chenis/Japenis"), icon: "square_dot", value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]' }, From 1a4b4b8befd43b78a11f8f11a46e18bc4aca3dfc Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 22:59:09 +0100 Subject: [PATCH 53/57] use ipc calls more for compatibility --- .../quickshell/ii/modules/bar/UtilButtons.qml | 2 +- .../quickshell/ii/modules/lock/Lock.qml | 18 +++++++++++------- .../ii/modules/overview/SearchBar.qml | 2 +- .../androidStyle/AndroidScreenSnipToggle.qml | 2 +- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/bar/UtilButtons.qml b/dots/.config/quickshell/ii/modules/bar/UtilButtons.qml index cc3eb64a2..2f7197b58 100644 --- a/dots/.config/quickshell/ii/modules/bar/UtilButtons.qml +++ b/dots/.config/quickshell/ii/modules/bar/UtilButtons.qml @@ -25,7 +25,7 @@ Item { visible: Config.options.bar.utilButtons.showScreenSnip sourceComponent: CircleUtilButton { Layout.alignment: Qt.AlignVCenter - onClicked: Hyprland.dispatch("global quickshell:regionScreenshot") + onClicked: Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]); MaterialSymbol { horizontalAlignment: Qt.AlignHCenter fill: 1 diff --git a/dots/.config/quickshell/ii/modules/lock/Lock.qml b/dots/.config/quickshell/ii/modules/lock/Lock.qml index e5557425b..4266d03f7 100644 --- a/dots/.config/quickshell/ii/modules/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/lock/Lock.qml @@ -128,11 +128,19 @@ Scope { } } + function lock() { + if (Config.options.lock.useHyprlock) { + Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]); + return; + } + GlobalStates.screenLocked = true; + } + IpcHandler { target: "lock" function activate(): void { - GlobalStates.screenLocked = true; + root.lock(); } function focus(): void { lockContext.shouldReFocus(); @@ -144,11 +152,7 @@ Scope { description: "Locks the screen" onPressed: { - if (Config.options.lock.useHyprlock) { - Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]); - return; - } - GlobalStates.screenLocked = true; + root.lock() } } @@ -165,7 +169,7 @@ Scope { function initIfReady() { if (!Config.ready || !Persistent.ready) return; if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) { - Hyprland.dispatch("global quickshell:lock") + root.lock(); } else { KeyringStorage.fetchKeyringData(); } diff --git a/dots/.config/quickshell/ii/modules/overview/SearchBar.qml b/dots/.config/quickshell/ii/modules/overview/SearchBar.qml index a0abd54c8..5cd7b9ac5 100644 --- a/dots/.config/quickshell/ii/modules/overview/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/overview/SearchBar.qml @@ -99,7 +99,7 @@ RowLayout { Layout.bottomMargin: 4 onClicked: { GlobalStates.overviewOpen = false; - Hyprland.dispatch("global quickshell:regionSearch") + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "search"]); } text: "image_search" StyledToolTip { diff --git a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml index c4b4a5479..875022249 100644 --- a/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml +++ b/dots/.config/quickshell/ii/modules/sidebarRight/quickToggles/androidStyle/AndroidScreenSnipToggle.qml @@ -23,7 +23,7 @@ AndroidQuickToggleButton { interval: 300 repeat: false onTriggered: { - Hyprland.dispatch("global quickshell:regionScreenshot") + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]); } } From bf70be7f4a20efad2b4895dbb4bcae062dd04ec5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 23:02:21 +0100 Subject: [PATCH 54/57] overlay: add recorder widget --- .../ii/modules/common/Persistent.qml | 12 +- .../ii/modules/overlay/OverlayContext.qml | 3 +- .../overlay/OverlayWidgetDelegateChooser.qml | 2 + .../ii/modules/overlay/recorder/Recorder.qml | 122 ++++++++++++++++++ .../overlay/volumeMixer/VolumeMixer.qml | 4 +- 5 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 814938452..5335deb9d 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -84,14 +84,20 @@ Singleton { property JsonObject crosshair: JsonObject { property bool pinned: false property bool clickthrough: true + property real x: 835 + property real y: 490 + } + property JsonObject recorder: JsonObject { + property bool pinned: false + property bool clickthrough: false property real x: 100 - property real y: 100 + property real y: 130 } property JsonObject volumeMixer: JsonObject { property bool pinned: false property bool clickthrough: false - property real x: 55 - property real y: 188 + property real x: 100 + property real y: 320 } } diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml index 1417c8467..22746c4de 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayContext.qml @@ -7,7 +7,8 @@ Singleton { readonly property list availableWidgets: [ { identifier: "crosshair", materialSymbol: "point_scan" }, - { identifier: "volumeMixer", materialSymbol: "volume_up" } + { identifier: "volumeMixer", materialSymbol: "volume_up" }, + { identifier: "recorder", materialSymbol: "screen_record" }, ] readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0 diff --git a/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml index e7420f625..39df62ac3 100644 --- a/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml +++ b/dots/.config/quickshell/ii/modules/overlay/OverlayWidgetDelegateChooser.qml @@ -8,6 +8,7 @@ import Quickshell import Quickshell.Bluetooth import qs.modules.overlay.crosshair import qs.modules.overlay.volumeMixer +import qs.modules.overlay.recorder DelegateChooser { id: root @@ -15,4 +16,5 @@ DelegateChooser { DelegateChoice { roleValue: "crosshair"; Crosshair {} } DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} } + DelegateChoice { roleValue: "recorder"; Recorder {} } } diff --git a/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml new file mode 100644 index 000000000..d32adedea --- /dev/null +++ b/dots/.config/quickshell/ii/modules/overlay/recorder/Recorder.qml @@ -0,0 +1,122 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.overlay + +StyledOverlayWidget { + id: root + + contentItem: Rectangle { + id: contentItem + anchors.centerIn: parent + color: Appearance.m3colors.m3surfaceContainer + property real padding: 8 + implicitHeight: contentColumn.implicitHeight + padding * 2 + implicitWidth: 350 + ColumnLayout { + id: contentColumn + anchors { + fill: parent + margins: parent.padding + } + spacing: 10 + + Row { + Layout.alignment: Qt.AlignHCenter + spacing: 10 + + BigRecorderButton { + materialSymbol: "screenshot_region" + name: "Screenshot region" + onClicked: { + GlobalStates.overlayOpen = false; + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "screenshot"]); + } + } + + BigRecorderButton { + materialSymbol: "photo_camera" + name: "Screenshot" + onClicked: { + GlobalStates.overlayOpen = false; + Quickshell.execDetached(["bash", "-c", "grim - | wl-copy"]); + } + } + + BigRecorderButton { + materialSymbol: "screen_record" + name: "Record region" + onClicked: { + GlobalStates.overlayOpen = false; + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "region", "recordWithSound"]); + } + } + + BigRecorderButton { + materialSymbol: "capture" + name: "Record screen" + onClicked: { + GlobalStates.overlayOpen = false; + Quickshell.execDetached([Directories.recordScriptPath, "--fullscreen", "--sound"]); + } + } + } + + RippleButton { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: false + buttonRadius: height / 2 + colBackground: Appearance.colors.colLayer3 + colBackgroundHover: Appearance.colors.colLayer3Hover + colRipple: Appearance.colors.colLayer3Active + onClicked: { + GlobalStates.overlayOpen = false; + Qt.openUrlExternally(Directories.videos); + } + contentItem: Row { + anchors.centerIn: parent + spacing: 6 + MaterialSymbol { + anchors.verticalCenter: parent.verticalCenter + text: "animated_images" + iconSize: 20 + } + StyledText { + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Open recordings folder") + } + } + } + } + } + + component BigRecorderButton: RippleButton { + id: bigButton + required property string materialSymbol + required property string name + implicitHeight: 66 + implicitWidth: 66 + buttonRadius: height / 2 + + colBackground: Appearance.colors.colLayer3 + colBackgroundHover: Appearance.colors.colLayer3Hover + colRipple: Appearance.colors.colLayer3Active + + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: bigButton.materialSymbol + iconSize: 28 + } + + StyledToolTip { + text: bigButton.name + } + } +} diff --git a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml index 433a2e11f..14aa53006 100644 --- a/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml +++ b/dots/.config/quickshell/ii/modules/overlay/volumeMixer/VolumeMixer.qml @@ -11,8 +11,8 @@ StyledOverlayWidget { anchors.centerIn: parent color: Appearance.m3colors.m3surfaceContainer property real padding: 16 - implicitHeight: 700 - implicitWidth: 400 + implicitHeight: 600 + implicitWidth: 350 VolumeDialogContent { anchors.fill: parent From 6afe810d69ab63264169bf315ddf0fdb28ce7c6f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 23:04:42 +0100 Subject: [PATCH 55/57] batterypopup: move health to below energy rate --- .../quickshell/ii/modules/bar/BatteryPopup.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml index 9e3dc49bd..26eda569e 100644 --- a/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml +++ b/dots/.config/quickshell/ii/modules/bar/BatteryPopup.qml @@ -42,12 +42,6 @@ StyledPopup { } } - StyledPopupValueRow { - icon: "heart_check" - label: Translation.tr("Health:") - value: `${(Battery.health).toFixed(1)}%` - } - StyledPopupValueRow { visible: !(Battery.chargeState != 4 && Battery.energyRate == 0) icon: "bolt" @@ -68,5 +62,11 @@ StyledPopup { } } } + + StyledPopupValueRow { + icon: "heart_check" + label: Translation.tr("Health:") + value: `${(Battery.health).toFixed(1)}%` + } } } From 3365719a4953ed9e9d49ef756ae43f5f2516b0df Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 6 Nov 2025 23:07:50 +0100 Subject: [PATCH 56/57] tray menu: fix weird popin anim origin point --- dots/.config/quickshell/ii/modules/bar/SysTray.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/bar/SysTray.qml b/dots/.config/quickshell/ii/modules/bar/SysTray.qml index 5489eb2f3..0233b2405 100644 --- a/dots/.config/quickshell/ii/modules/bar/SysTray.qml +++ b/dots/.config/quickshell/ii/modules/bar/SysTray.qml @@ -104,7 +104,6 @@ Item { id: overflowPopup hoverTarget: trayOverflowButton active: root.trayOverflowOpen && root.unpinnedItems.length > 0 - popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily) GridLayout { id: trayOverflowLayout From a0c5940a94c06314da5c07c54f928382caebe6c3 Mon Sep 17 00:00:00 2001 From: clsty Date: Fri, 7 Nov 2025 06:08:46 +0800 Subject: [PATCH 57/57] Use "Han chars" --- dots/.config/quickshell/ii/modules/settings/BarConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml index c00ef30cd..a53d30644 100644 --- a/dots/.config/quickshell/ii/modules/settings/BarConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BarConfig.qml @@ -321,7 +321,7 @@ ContentPage { value: '[]' }, { - displayName: Translation.tr("Chenis/Japenis"), + displayName: Translation.tr("Han chars"), icon: "square_dot", value: '["一","二","三","四","五","六","七","八","九","十","十一","十二","十三","十四","十五","十六","十七","十八","十九","二十"]' },