overlay: add kurukuru

This commit is contained in:
end-4
2025-11-09 12:58:48 +01:00
parent 8f4190a939
commit baa3c2a773
8 changed files with 142 additions and 15 deletions
@@ -381,6 +381,10 @@ Singleton {
property bool openingZoomAnimation: true
property bool darkenScreen: true
property real clickthroughOpacity: 0.7
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 {
@@ -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
@@ -11,14 +11,16 @@ 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
}
@@ -0,0 +1,95 @@
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell
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
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) {
persistentStateEntry.x = Math.round(xPos);
persistentStateEntry.y = Math.round(yPos);
persistentStateEntry.width = 0
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;
}
}
}
}
}
@@ -328,6 +328,21 @@ ContentPage {
}
}
ContentSection {
icon: "point_scan"
title: Translation.tr("Overlay: Floating Image")
MaterialTextArea {
Layout.fillWidth: true
placeholderText: Translation.tr("Image source")
text: Config.options.overlay.floatingImage.imageSource
wrapMode: TextEdit.Wrap
onTextChanged: {
Config.options.overlay.floatingImage.imageSource = text;
}
}
}
ContentSection {
icon: "screenshot_frame_2"
title: Translation.tr("Region selector (screen snipping/Google Lens)")