Merge branch 'end-4:main' into main

This commit is contained in:
jwihardi
2025-11-09 17:16:12 -05:00
committed by GitHub
207 changed files with 487 additions and 155 deletions
@@ -148,6 +148,7 @@ Singleton {
property JsonObject widgets: JsonObject {
property JsonObject clock: JsonObject {
property bool enable: true
property bool showOnlyWhenLocked: false
property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy"
property real x: 100
property real y: 100
@@ -274,7 +275,7 @@ Singleton {
// 10:  | 11:  | 12:  | 13:  | 14: 󱄛
property string superKey: ""
property bool useMacSymbol: false
property bool splitButtons: true
property bool splitButtons: false
property bool useMouseSymbol: false
property bool useFnSymbol: false
property JsonObject fontSize: JsonObject {
@@ -380,7 +381,11 @@ Singleton {
property JsonObject overlay: JsonObject {
property bool openingZoomAnimation: true
property bool darkenScreen: true
property real clickthroughOpacity: 0.7
property real clickthroughOpacity: 0.8
property JsonObject floatingImage: JsonObject {
property string imageSource: "https://cdn.discordapp.com/attachments/961693710968557598/1369635662390759434/image.gif?ex=6911cb1c&is=6910799c&hm=4450244066c0a7a6e5d2bdd195f47388eb5e7f9dd53d3931e99ad9642c638a00&"
property real scale: 0.5
}
}
property JsonObject overview: JsonObject {
@@ -24,6 +24,7 @@ Singleton {
property string scriptPath: Quickshell.shellPath("scripts")
property string favicons: FileUtils.trimFileProtocol(`${Directories.cache}/media/favicons`)
property string coverArt: FileUtils.trimFileProtocol(`${Directories.cache}/media/coverart`)
property string tempImages: "/tmp/quickshell/media/images"
property string booruPreviews: FileUtils.trimFileProtocol(`${Directories.cache}/media/boorus`)
property string booruDownloads: FileUtils.trimFileProtocol(Directories.pictures + "/homework")
property string booruDownloadsNsfw: FileUtils.trimFileProtocol(Directories.pictures + "/homework/🌶️")
@@ -52,5 +53,6 @@ Singleton {
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
Quickshell.execDetached(["mkdir", "-p", `${aiChats}`])
Quickshell.execDetached(["rm", "-rf", `${tempImages}`])
}
}
@@ -89,6 +89,22 @@ Singleton {
property real width: 250
property real height: 100
}
property JsonObject floatingImage: JsonObject {
property bool pinned: false
property bool clickthrough: false
property real x: 1650
property real y: 390
property real width: 0
property real height: 0
}
property JsonObject fpsLimiter: JsonObject {
property bool pinned: false
property bool clickthrough: false
property real x: 1570
property real y: 615
property real width: 280
property real height: 80
}
property JsonObject recorder: JsonObject {
property bool pinned: false
property bool clickthrough: false
@@ -115,14 +131,6 @@ Singleton {
property real height: 600
property int tabIndex: 0
}
property JsonObject fpsLimiter: JsonObject {
property bool pinned: false
property bool clickthrough: false
property real x: 1576
property real y: 630
property real width: 280
property real height: 80
}
}
property JsonObject timer: JsonObject {
@@ -0,0 +1,31 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.modules.common
import qs.modules.common.functions
Process {
id: root
signal done(string path, int width, int height);
required property string filePath;
required property string sourceUrl;
property string downloadUserAgent: Config.options?.networking.userAgent ?? ""
function processFilePath() {
return StringUtils.shellSingleQuoteEscape(FileUtils.trimFileProtocol(filePath));
}
running: true
command: ["bash", "-c",
`mkdir -p $(dirname '${processFilePath(filePath)}'); [ -f '${processFilePath(filePath)}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath(filePath)}' && magick identify -format '%w %h' '${processFilePath(filePath)}'[0]`
]
stdout: StdioCollector {
id: imageSizeOutputCollector
onStreamFinished: {
const output = imageSizeOutputCollector.text.trim();
const [width, height] = output.split(" ").map(Number);
root.done(root.filePath, width, height);
}
}
}
@@ -14,9 +14,9 @@ import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import qs.modules.background.widgets
import qs.modules.background.widgets.clock
import qs.modules.background.widgets.weather
import qs.modules.ii.background.widgets
import qs.modules.ii.background.widgets.clock
import qs.modules.ii.background.widgets.weather
Variants {
id: root
@@ -6,7 +6,7 @@ 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
import qs.modules.ii.background.widgets
AbstractBackgroundWidget {
id: root
@@ -16,8 +16,9 @@ AbstractBackgroundWidget {
implicitHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
property string clockStyle: Config.options.background.widgets.clock.style
property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock)
readonly property string clockStyle: Config.options.background.widgets.clock.style
readonly property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock)
readonly property bool shouldShow: (!Config.options.background.widgets.clock.showOnlyWhenLocked || GlobalStates.screenLocked)
property bool wallpaperSafetyTriggered: false
needsColText: clockStyle === "digital"
x: forceCenter ? ((root.screenWidth - root.width) / 2) : targetX
@@ -42,7 +43,7 @@ AbstractBackgroundWidget {
FadeLoader {
id: cookieClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "cookie"
shown: root.clockStyle === "cookie" && (root.shouldShow)
sourceComponent: Column {
CookieClock {
anchors.horizontalCenter: parent.horizontalCenter
@@ -58,7 +59,7 @@ AbstractBackgroundWidget {
FadeLoader {
id: digitalClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "digital"
shown: root.clockStyle === "digital" && (root.shouldShow)
sourceComponent: ColumnLayout {
id: clockColumn
spacing: 6
@@ -9,8 +9,8 @@ import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell.Io
import qs.modules.background.widgets.clock.dateIndicator
import qs.modules.background.widgets.clock.minuteMarks
import qs.modules.ii.background.widgets.clock.dateIndicator
import qs.modules.ii.background.widgets.clock.minuteMarks
Item {
id: root
@@ -5,7 +5,7 @@ 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
import qs.modules.ii.background.widgets
AbstractBackgroundWidget {
id: root
@@ -1,4 +1,4 @@
import qs.modules.bar.weather
import qs.modules.ii.bar.weather
import QtQuick
import QtQuick.Layouts
import Quickshell
@@ -178,7 +178,7 @@ Item { // Bar content region
}
BatteryIndicator {
visible: (root.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
visible: (root.useShortenedForm < 2 && Battery.available)
Layout.alignment: Qt.AlignVCenter
}
}
@@ -4,7 +4,7 @@ import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import qs.modules.bar
import qs.modules.ii.bar
StyledPopup {
id: root
@@ -127,13 +127,12 @@ Item {
const keybind = keybindSection.modelData.keybinds[i];
if (!Config.options.cheatsheet.splitButtons) {
for (var j = 0; j < keybind.mods.length; j++) {
keybind.mods[j] = keySubstitutions[keybind.mods[j]] || keybind.mods[j];
}
keybind.mods = [keybind.mods.join(' ') ]
keybind.mods[0] += !keyBlacklist.includes(keybind.key) && keybind.mods[0].length ? ' ' : ''
keybind.mods[0] += !keyBlacklist.includes(keybind.key) ? (keySubstitutions[keybind.key] || keybind.key) : ''
for (var j = 0; j < keybind.mods.length; j++) {
keybind.mods[j] = keySubstitutions[keybind.mods[j]] || keybind.mods[j];
}
keybind.mods = [keybind.mods.join(' ') ]
keybind.mods[0] += !keyBlacklist.includes(keybind.key) && keybind.mods[0].length ? ' ' : ''
keybind.mods[0] += !keyBlacklist.includes(keybind.key) ? (keySubstitutions[keybind.key] || keybind.key) : ''
}
result.push({
@@ -3,7 +3,6 @@ import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.lock
import QtQuick
import Quickshell
import Quickshell.Io
@@ -7,7 +7,7 @@ import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.bar as Bar
import qs.modules.ii.bar as Bar
import Quickshell
import Quickshell.Services.SystemTray
@@ -288,7 +288,7 @@ MouseArea {
opacity: root.toolbarOpacity
IconAndTextPair {
visible: UPower.displayDevice.isLaptopBattery
visible: Battery.available
icon: Battery.isCharging ? "bolt" : "battery_android_full"
text: Math.round(Battery.percentage * 100)
color: (Battery.isLow && !Battery.isCharging) ? Appearance.colors.colError : Appearance.colors.colOnSurfaceVariant
@@ -1,8 +1,8 @@
pragma ComponentBehavior: Bound
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.services
import qs
import qs.modules.common.functions
import QtQuick
import QtQuick.Layouts
@@ -4,7 +4,6 @@ import qs.modules.common.widgets
import qs.services
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import Quickshell.Hyprland
@@ -2,7 +2,7 @@ import qs.services
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.modules.onScreenDisplay
import qs.modules.ii.onScreenDisplay
OsdValueIndicator {
id: root
@@ -1,6 +1,6 @@
import qs.services
import QtQuick
import qs.modules.onScreenDisplay
import qs.modules.ii.onScreenDisplay
OsdValueIndicator {
id: osdValues
@@ -0,0 +1,8 @@
import QtQuick
import qs.modules.common
Rectangle {
id: contentItem
anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer
}
@@ -8,8 +8,6 @@ import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.widgets.widgetCanvas
import qs.modules.overlay.crosshair
Item {
id: root
focus: true
@@ -6,11 +6,12 @@ Singleton {
id: root
readonly property list<var> availableWidgets: [
{ identifier: "recorder", materialSymbol: "screen_record" },
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
{ identifier: "crosshair", materialSymbol: "point_scan" },
{ identifier: "fpsLimiter", materialSymbol: "animation" },
{ identifier: "resources", materialSymbol: "browse_activity" }
{ identifier: "floatingImage", materialSymbol: "imagesmode" },
{ identifier: "recorder", materialSymbol: "screen_record" },
{ identifier: "resources", materialSymbol: "browse_activity" },
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
]
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
@@ -49,8 +49,13 @@ Rectangle {
}
Separator {}
TimeWidget {}
Separator {
visible: Battery.available
}
BatteryWidget {
visible: Battery.available
}
}
component Separator: Rectangle {
@@ -67,12 +72,44 @@ Rectangle {
Layout.rightMargin: 6
text: DateTime.time
color: Appearance.colors.colOnSurface
font {
family: Appearance.font.family.numbers
variableAxes: Appearance.font.variableAxes.numbers
pixelSize: 22
}
}
component BatteryWidget: Row {
id: batteryWidget
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 6
Layout.rightMargin: 6
spacing: 2
property color colText: Battery.isLowAndNotCharging ? Appearance.colors.colError : Appearance.colors.colOnSurface
MaterialSymbol {
id: boltIcon
anchors.verticalCenter: parent.verticalCenter
fill: 1
text: Battery.isCharging ? "bolt" : "battery_android_full"
color: batteryWidget.colText
iconSize: 24
animateChange: true
}
StyledText {
id: batteryText
anchors.verticalCenter: parent.verticalCenter
text: Math.round(Battery.percentage * 100) + "%"
color: batteryWidget.colText
font {
family: Appearance.font.family.numbers
variableAxes: Appearance.font.variableAxes.numbers
pixelSize: 18
}
}
}
component WidgetButton: RippleButton {
id: widgetButton
@@ -6,19 +6,21 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import qs.modules.overlay.crosshair
import qs.modules.overlay.volumeMixer
import qs.modules.overlay.fpsLimiter
import qs.modules.overlay.recorder
import qs.modules.overlay.resources
import qs.modules.ii.overlay.crosshair
import qs.modules.ii.overlay.volumeMixer
import qs.modules.ii.overlay.fpsLimiter
import qs.modules.ii.overlay.recorder
import qs.modules.ii.overlay.resources
import qs.modules.ii.overlay.floatingImage
DelegateChooser {
id: root
role: "identifier"
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
DelegateChoice { roleValue: "floatingImage"; FloatingImage {} }
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
DelegateChoice { roleValue: "recorder"; Recorder {} }
DelegateChoice { roleValue: "resources"; Resources {} }
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
}
@@ -33,8 +33,8 @@ AbstractOverlayWidget {
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 minimumWidth: 250
property real minimumHeight: 100
property real minimumWidth: contentItem.implicitWidth
property real minimumHeight: contentItem.implicitHeight
property real resizeMargin: 8
property real padding: 6
property real contentRadius: radius - padding
@@ -238,8 +238,8 @@ AbstractOverlayWidget {
}
StyledText {
text: root.title
Layout.fillWidth: true
text: root.title
elide: Text.ElideRight
}
@@ -2,7 +2,7 @@ import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.modules.common
import qs.modules.overlay
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
@@ -0,0 +1,95 @@
pragma ComponentBehavior: Bound
import QtQuick
import Qt5Compat.GraphicalEffects
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.utils
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
showClickabilityButton: false
resizable: false
clickthrough: true
property string imageSource: Config.options.overlay.floatingImage.imageSource
property real scaleFactor: Config.options.overlay.floatingImage.scale
property int imageWidth: 0
property int imageHeight: 0
// Override to always save 0 size
function savePosition(xPos = root.x, yPos = root.y, width = 0, height = 0) {
root.persistentStateEntry.x = Math.round(xPos);
root.persistentStateEntry.y = Math.round(yPos);
root.persistentStateEntry.width = 0
root.persistentStateEntry.height = 0
}
onImageSourceChanged: {
imageDownloader.running = false;
imageDownloader.sourceUrl = root.imageSource;
imageDownloader.filePath = Qt.resolvedUrl(Directories.tempImages + "/" + Qt.md5(root.imageSource))
imageDownloader.running = true;
}
onScaleFactorChanged: {
setSize();
}
function setSize() {
bg.implicitWidth = root.imageWidth * root.scaleFactor;
bg.implicitHeight = root.imageHeight * root.scaleFactor;
}
contentItem: OverlayBackground {
id: bg
color: ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainer, root.actuallyPinned ? 1 : 0)
radius: root.contentRadius
WheelHandler {
onWheel: (event) => {
if (event.angleDelta.y < 0) {
Config.options.overlay.floatingImage.scale = Math.max(0.1, Config.options.overlay.floatingImage.scale - 0.1);
}
else if (event.angleDelta.y > 0) {
Config.options.overlay.floatingImage.scale = Math.min(5.0, Config.options.overlay.floatingImage.scale + 0.1);
}
}
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
}
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Rectangle {
width: bg.width
height: bg.height
radius: bg.radius
}
}
AnimatedImage {
id: animatedImage
anchors.centerIn: parent
width: root.imageWidth * root.scaleFactor
height: root.imageHeight * root.scaleFactor
sourceSize.width: width
sourceSize.height: height
playing: visible
asynchronous: true
source: ""
ImageDownloaderProcess {
id: imageDownloader
filePath: Qt.resolvedUrl(Directories.tempImages + "/" + Qt.md5(root.imageSource))
sourceUrl: root.imageSource
onDone: (path, width, height) => {
root.imageWidth = width;
root.imageHeight = height;
root.setSize();
animatedImage.source = path;
}
}
}
}
}
@@ -1,7 +1,7 @@
import QtQuick
import Quickshell
import qs.modules.common
import qs.modules.overlay
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
@@ -6,16 +6,15 @@ import Quickshell
import Quickshell.Io
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.ii.overlay
Rectangle {
OverlayBackground {
id: root
enum State { Normal, Success, Error }
anchors.fill: parent
property real padding: 16
property var currentState: FpsLimiterContent.State.Normal
color: Appearance.m3colors.m3surfaceContainer
implicitWidth: content.implicitWidth + (padding * 2)
implicitHeight: content.implicitHeight + (padding * 2)
@@ -5,18 +5,16 @@ import Quickshell
import qs
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.overlay
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
minimumWidth: 310
minimumHeight: 130
contentItem: Rectangle {
contentItem: OverlayBackground {
id: contentItem
anchors.fill: parent
radius: root.contentRadius
color: Appearance.m3colors.m3surfaceContainer
property real padding: 8
ColumnLayout {
id: contentColumn
@@ -10,7 +10,7 @@ import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.overlay
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
@@ -37,10 +37,8 @@ StyledOverlayWidget {
},
]
contentItem: Rectangle {
contentItem: OverlayBackground {
id: contentItem
anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer
radius: root.contentRadius
property real padding: 4
ColumnLayout {
@@ -5,17 +5,15 @@ import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.overlay
import qs.modules.sidebarRight.volumeMixer
import qs.modules.ii.overlay
import qs.modules.ii.sidebarRight.volumeMixer
StyledOverlayWidget {
id: root
minimumWidth: 300
minimumHeight: 380
contentItem: Rectangle {
anchors.fill: parent
color: Appearance.m3colors.m3surfaceContainer
contentItem: OverlayBackground {
radius: root.contentRadius
property real padding: 6

Some files were not shown because too many files have changed in this diff Show More