wallpaper safety

This commit is contained in:
end-4
2025-09-21 12:08:09 +02:00
parent b4cced68f1
commit 16995d2ae5
3 changed files with 140 additions and 63 deletions
@@ -45,6 +45,10 @@ Variants {
|| Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".avi")
|| Config.options.background.wallpaperPath.endsWith(".mov") || Config.options.background.wallpaperPath.endsWith(".mov")
property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath
property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && (
CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) &&
CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords)
)
property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height)
property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom
property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated
@@ -57,7 +61,7 @@ Variants {
property real clockX: (modelData.width / 2) property real clockX: (modelData.width / 2)
property real clockY: (modelData.height / 2) property real clockY: (modelData.height / 2)
property var textHorizontalAlignment: { property var textHorizontalAlignment: {
if (Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) if ((Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered)
return Text.AlignHCenter; return Text.AlignHCenter;
if (clockX < screen.width / 3) if (clockX < screen.width / 3)
return Text.AlignLeft; return Text.AlignLeft;
@@ -69,11 +73,61 @@ Variants {
property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable) property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable)
property color dominantColor: Appearance.colors.colPrimary property color dominantColor: Appearance.colors.colPrimary
property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 property bool dominantColorIsDark: dominantColor.hslLightness < 0.5
property color colText: (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) property color colText: {
if (wallpaperSafetyTriggered) return CF.ColorUtils.mix(Appearance.colors.colOnLayer0, Appearance.colors.colPrimary, 0.75);
return (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12))
}
Behavior on colText { Behavior on colText {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
} }
// Components
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: true
}
component ClockStatusText: RowLayout {
id: statusTextRow
property alias statusIcon: statusIconWidget.text
property alias statusText: statusTextWidget.text
property bool shown: true
opacity: shown ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
Layout.fillWidth: false
MaterialSymbol {
id: statusIconWidget
Layout.fillWidth: false
iconSize: Appearance.font.pixelSize.huge
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
ClockText {
id: statusTextWidget
Layout.fillWidth: false
color: bgRoot.colText
font {
family: Appearance.font.family.main
pixelSize: Appearance.font.pixelSize.large
weight: Font.Normal
}
style: Text.Raised
styleColor: Appearance.colors.colShadow
}
}
// Layer props // Layer props
screen: modelData screen: modelData
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore
@@ -86,7 +140,10 @@ Variants {
left: true left: true
right: true right: true
} }
color: "transparent" color: CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75);
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
onWallpaperPathChanged: { onWallpaperPathChanged: {
bgRoot.updateZoomScale() bgRoot.updateZoomScale()
@@ -206,7 +263,12 @@ Variants {
property real effectiveValueY: Math.max(0, Math.min(1, valueY)) property real effectiveValueY: Math.max(0, Math.min(1, valueY))
x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace
y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace
source: bgRoot.wallpaperPath source: {
print("-----------------")
print("Safety triggered:", bgRoot.wallpaperSafetyTriggered);
print("Wallpaper path:", bgRoot.wallpaperPath);
return bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath
}
fillMode: Image.PreserveAspectCrop fillMode: Image.PreserveAspectCrop
Behavior on x { Behavior on x {
NumberAnimation { NumberAnimation {
@@ -278,13 +340,14 @@ Variants {
} }
states: State { states: State {
name: "centered" name: "centered"
when: bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock when: (bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock) || bgRoot.wallpaperSafetyTriggered
AnchorChanges { AnchorChanges {
target: clockLoader target: clockLoader
anchors { anchors {
left: undefined left: undefined
right: undefined right: undefined
top: parent.top top: undefined
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
} }
} }
@@ -306,46 +369,27 @@ Variants {
anchors.centerIn: parent anchors.centerIn: parent
spacing: 6 spacing: 6
StyledText { ClockText {
Layout.fillWidth: true font.pixelSize: 90
horizontalAlignment: bgRoot.textHorizontalAlignment
font {
family: Appearance.font.family.expressive
pixelSize: 90
weight: Font.Bold
}
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
text: DateTime.time text: DateTime.time
} }
StyledText { ClockText {
Layout.fillWidth: true
Layout.topMargin: -5 Layout.topMargin: -5
horizontalAlignment: bgRoot.textHorizontalAlignment
font {
family: Appearance.font.family.expressive
pixelSize: 20
weight: Font.DemiBold
}
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
text: DateTime.date text: DateTime.date
animateChange: true
} }
StyledText { StyledText { // Somehow gets fucked up if made a ClockText???
Layout.fillWidth: true Layout.fillWidth: true
horizontalAlignment: bgRoot.textHorizontalAlignment horizontalAlignment: bgRoot.textHorizontalAlignment
font { font {
family: Appearance.font.family.expressive family: Appearance.font.family.main
pixelSize: 20 pixelSize: Appearance.font.pixelSize.normal
weight: Font.DemiBold weight: 350
italic: true
} }
color: bgRoot.colText color: bgRoot.colText
style: Text.Raised style: Text.Raised
visible: Config.options.background.quote !== ""
styleColor: Appearance.colors.colShadow styleColor: Appearance.colors.colShadow
// visible: Config.options.background.quote.length > 0
text: Config.options.background.quote text: Config.options.background.quote
} }
} }
@@ -356,31 +400,21 @@ Variants {
left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined
right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined
horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined
topMargin: 5 topMargin: 14
leftMargin: -5 leftMargin: -6
rightMargin: -5 rightMargin: -6
}
opacity: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) ? 1 : 0
visible: opacity > 0
Behavior on opacity {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
} }
spacing: 16
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 }
MaterialSymbol { ClockStatusText {
text: "lock" shown: bgRoot.wallpaperSafetyTriggered
Layout.fillWidth: false statusIcon: "hide_image"
iconSize: Appearance.font.pixelSize.huge statusText: qsTr("Wallpaper safety enforced")
color: bgRoot.colText
style: Text.Raised
styleColor: Appearance.colors.colShadow
} }
StyledText { ClockStatusText {
Layout.fillWidth: false shown: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText)
text: "Locked" statusIcon: "lock"
color: bgRoot.colText statusText: qsTr("Locked")
font.pixelSize: Appearance.font.pixelSize.larger
style: Text.Raised
styleColor: Appearance.colors.colShadow
} }
Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 }
@@ -128,6 +128,8 @@ Singleton {
property bool showClock: true property bool showClock: true
property string wallpaperPath: "" property string wallpaperPath: ""
property string thumbnailPath: "" property string thumbnailPath: ""
property string quote: ""
property bool hideWhenFullscreen: true
property JsonObject parallax: JsonObject { property JsonObject parallax: JsonObject {
property bool vertical: false property bool vertical: false
property bool autoVertical: false property bool autoVertical: false
@@ -142,8 +144,13 @@ Singleton {
property bool showLockedText: true property bool showLockedText: true
property real extraZoom: 1.1 property real extraZoom: 1.1
} }
property string quote: "" property JsonObject wallpaperSafety: JsonObject {
property bool hideWhenFullscreen: true property bool enable: true
property JsonObject triggerCondition: JsonObject {
property list<string> wallpaperKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"]
property list<string> networkNameKeywords: ["guest", "public", "free", "airport"]
}
}
} }
property JsonObject bar: JsonObject { property JsonObject bar: JsonObject {
@@ -8,7 +8,7 @@ Singleton {
* Formats a string according to the args that are passed inc * Formats a string according to the args that are passed inc
* @param { string } str * @param { string } str
* @param {...any} args * @param {...any} args
* @returns * @returns { string }
*/ */
function format(str, ...args) { function format(str, ...args) {
return str.replace(/{(\d+)}/g, (match, index) => typeof args[index] !== 'undefined' ? args[index] : match); return str.replace(/{(\d+)}/g, (match, index) => typeof args[index] !== 'undefined' ? args[index] : match);
@@ -35,10 +35,10 @@ Singleton {
} }
/** /**
* Escapes single quotes in shell commands * Escapes single quotes in shell commands
* @param { string } str * @param { string } str
* @returns { string } * @returns { string }
*/ */
function shellSingleQuoteEscape(str) { function shellSingleQuoteEscape(str) {
return String(str) return String(str)
// .replace(/\\/g, '\\\\') // .replace(/\\/g, '\\\\')
@@ -48,6 +48,7 @@ Singleton {
/** /**
* Splits markdown blocks into three different types: text, think, and code. * Splits markdown blocks into three different types: text, think, and code.
* @param { string } markdown * @param { string } markdown
* @returns {Array<{type: "text" | "think" | "code", content: string, lang?: string, completed?: boolean}>}
*/ */
function splitMarkdownBlocks(markdown) { function splitMarkdownBlocks(markdown) {
const regex = /```(\w+)?\n([\s\S]*?)```|<think>([\s\S]*?)<\/think>/g; const regex = /```(\w+)?\n([\s\S]*?)```|<think>([\s\S]*?)<\/think>/g;
@@ -182,6 +183,11 @@ Singleton {
return lines.join("\n"); return lines.join("\n");
} }
/**
* Cleans up a music title by removing bracketed and special characters.
* @param { string } title
* @returns { string }
*/
function cleanMusicTitle(title) { function cleanMusicTitle(title) {
if (!title) if (!title)
return ""; return "";
@@ -198,6 +204,11 @@ Singleton {
return title.trim(); return title.trim();
} }
/**
* Converts seconds to a friendly time string (e.g. 1:23 or 1:02:03).
* @param { number } seconds
* @returns { string }
*/
function friendlyTimeForSeconds(seconds) { function friendlyTimeForSeconds(seconds) {
if (isNaN(seconds) || seconds < 0) if (isNaN(seconds) || seconds < 0)
return "0:00"; return "0:00";
@@ -212,13 +223,38 @@ Singleton {
} }
} }
/**
* Escapes HTML special characters in a string.
* @param { string } str
* @returns { string }
*/
function escapeHtml(str) { function escapeHtml(str) {
if (typeof str !== 'string') if (typeof str !== 'string')
return str; return str;
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;'); return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
} }
/**
* Cleans a cliphist entry by removing leading digits and tab.
* @param { string } str
* @returns { string }
*/
function cleanCliphistEntry(str: string): string { function cleanCliphistEntry(str: string): string {
return str.replace(/^\d+\t/, ""); return str.replace(/^\d+\t/, "");
} }
/**
* Checks if any substring in the list is contained in the string.
* @param { string } str
* @param { string[] } substrings
* @returns { boolean }
*/
function stringListContainsSubstring(str, substrings) {
for (let i = 0; i < substrings.length; ++i) {
if (str.includes(substrings[i])) {
return true;
}
}
return false;
}
} }