mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-08 08:19:26 -05:00
Merge remote-tracking branch 'upstream/main' into fix-dock-first-launch
This commit is contained in:
@@ -28,15 +28,11 @@ Singleton {
|
||||
property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896
|
||||
let x = wallpaperVibrancy
|
||||
let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896
|
||||
return Math.max(0, Math.min(0.22, y))
|
||||
}
|
||||
property real autoContentTransparency: { // y = -10.1734x^2 + 3.4457x + 0.1872
|
||||
let x = autoBackgroundTransparency
|
||||
let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872
|
||||
return Math.max(0, Math.min(0.6, y))
|
||||
return Math.max(0, Math.min(0.22, y)) - 0.12 * (m3colors.darkmode ? 0 : 1)
|
||||
}
|
||||
property real autoContentTransparency: 0.9
|
||||
property real backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0
|
||||
property real contentTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency : 0
|
||||
property real contentTransparency: Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency
|
||||
|
||||
m3colors: QtObject {
|
||||
property bool darkmode: true
|
||||
@@ -114,30 +110,41 @@ Singleton {
|
||||
|
||||
colors: QtObject {
|
||||
property color colSubtext: m3colors.m3outline
|
||||
property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.backgroundTransparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
// Layer 0
|
||||
property color colLayer0Base: ColorUtils.mix(m3colors.m3background, m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1)
|
||||
property color colLayer0: ColorUtils.transparentize(colLayer0Base, root.backgroundTransparency)
|
||||
property color colOnLayer0: m3colors.m3onBackground
|
||||
property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency))
|
||||
property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency))
|
||||
property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4)
|
||||
property color colLayer1: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency);
|
||||
// Layer 1
|
||||
property color colLayer1Base: m3colors.m3surfaceContainerLow
|
||||
property color colLayer1: ColorUtils.solveOverlayColor(colLayer0Base, colLayer1Base, 1 - root.contentTransparency);
|
||||
property color colOnLayer1: m3colors.m3onSurfaceVariant;
|
||||
property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45);
|
||||
property color colLayer2: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency)
|
||||
property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency);
|
||||
property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency)
|
||||
property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency);
|
||||
property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency);
|
||||
property color colLayer3: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
// Layer 2
|
||||
property color colLayer2Base: m3colors.m3surfaceContainer
|
||||
property color colLayer2: ColorUtils.solveOverlayColor(colLayer1Base, colLayer2Base, 1 - root.contentTransparency)
|
||||
property color colLayer2Hover: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.90), 1 - root.contentTransparency)
|
||||
property color colLayer2Active: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.80), 1 - root.contentTransparency);
|
||||
property color colLayer2Disabled: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, m3colors.m3background, 0.8), 1 - root.contentTransparency);
|
||||
property color colOnLayer2: m3colors.m3onSurface;
|
||||
property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4);
|
||||
// Layer 3
|
||||
property color colLayer3Base: m3colors.m3surfaceContainerHigh
|
||||
property color colLayer3: ColorUtils.solveOverlayColor(colLayer2Base, colLayer3Base, 1 - root.contentTransparency)
|
||||
property color colLayer3Hover: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.90), 1 - root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.80), 1 - root.contentTransparency);
|
||||
property color colOnLayer3: m3colors.m3onSurface;
|
||||
property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency)
|
||||
property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency);
|
||||
property color colLayer4: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
// Layer 4
|
||||
property color colLayer4Base: m3colors.m3surfaceContainerHighest
|
||||
property color colLayer4: ColorUtils.solveOverlayColor(colLayer3Base, colLayer4Base, 1 - root.contentTransparency)
|
||||
property color colLayer4Hover: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.90), 1 - root.contentTransparency)
|
||||
property color colLayer4Active: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.80), 1 - root.contentTransparency);
|
||||
property color colOnLayer4: m3colors.m3onSurface;
|
||||
property color colLayer4Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.90), root.contentTransparency)
|
||||
property color colLayer4Active: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.80), root.contentTransparency);
|
||||
// Primary
|
||||
property color colPrimary: m3colors.m3primary
|
||||
property color colOnPrimary: m3colors.m3onPrimary
|
||||
property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87)
|
||||
@@ -146,13 +153,16 @@ Singleton {
|
||||
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.9)
|
||||
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.8)
|
||||
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
|
||||
// Secondary
|
||||
property color colSecondary: m3colors.m3secondary
|
||||
property color colOnSecondary: m3colors.m3onSecondary
|
||||
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
|
||||
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
|
||||
property color colOnSecondary: m3colors.m3onSecondary
|
||||
property color colSecondaryContainer: m3colors.m3secondaryContainer
|
||||
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90)
|
||||
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.54)
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
// Tertiary
|
||||
property color colTertiary: m3colors.m3tertiary
|
||||
property color colTertiaryHover: ColorUtils.mix(m3colors.m3tertiary, colLayer1Hover, 0.85)
|
||||
property color colTertiaryActive: ColorUtils.mix(m3colors.m3tertiary, colLayer1Active, 0.4)
|
||||
@@ -161,16 +171,17 @@ Singleton {
|
||||
property color colTertiaryContainerActive: ColorUtils.mix(m3colors.m3tertiaryContainer, colLayer1Active, 0.54)
|
||||
property color colOnTertiary: m3colors.m3onTertiary
|
||||
property color colOnTertiaryContainer: m3colors.m3onTertiaryContainer
|
||||
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
|
||||
property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency)
|
||||
property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency)
|
||||
// Surface
|
||||
property color colBackgroundSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.backgroundTransparency)
|
||||
property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency)
|
||||
property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency)
|
||||
property color colSurfaceContainerLow: ColorUtils.solveOverlayColor(m3colors.m3background, m3colors.m3surfaceContainerLow, 1 - root.contentTransparency)
|
||||
property color colSurfaceContainer: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerLow, m3colors.m3surfaceContainer, 1 - root.contentTransparency)
|
||||
property color colSurfaceContainerHigh: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 1 - root.contentTransparency)
|
||||
property color colSurfaceContainerHighest: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerHigh, m3colors.m3surfaceContainerHighest, 1 - root.contentTransparency)
|
||||
property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95)
|
||||
property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85)
|
||||
property color colOnSurface: m3colors.m3onSurface
|
||||
property color colOnSurfaceVariant: m3colors.m3onSurfaceVariant
|
||||
// Misc
|
||||
property color colTooltip: m3colors.m3inverseSurface
|
||||
property color colOnTooltip: m3colors.m3inverseOnSurface
|
||||
property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5)
|
||||
|
||||
@@ -78,10 +78,7 @@ Singleton {
|
||||
JsonAdapter {
|
||||
id: configOptionsJsonAdapter
|
||||
|
||||
property list<string> enabledPanels: [
|
||||
"iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"
|
||||
]
|
||||
property string panelFamily: "ii" // "ii", "w"
|
||||
property string panelFamily: "ii" // "ii", "waffle"
|
||||
|
||||
property JsonObject policies: JsonObject {
|
||||
property int ai: 1 // 0: No | 1: Yes | 2: Local
|
||||
@@ -189,7 +186,17 @@ Singleton {
|
||||
property bool useSineCookie: false
|
||||
}
|
||||
property JsonObject digital: JsonObject {
|
||||
property bool adaptiveAlignment: true
|
||||
property bool showDate: true
|
||||
property bool animateChange: true
|
||||
property bool vertical: false
|
||||
property JsonObject font: JsonObject {
|
||||
property string family: "Google Sans Flex"
|
||||
property real weight: 350
|
||||
property real width: 100
|
||||
property real size: 90
|
||||
property real roundness: 0
|
||||
}
|
||||
}
|
||||
property JsonObject quote: JsonObject {
|
||||
property bool enable: false
|
||||
@@ -566,6 +573,7 @@ Singleton {
|
||||
}
|
||||
|
||||
property JsonObject updates: JsonObject {
|
||||
property bool enableCheck: true
|
||||
property int checkInterval: 120 // minutes
|
||||
property int adviseUpdateThreshold: 75 // packages
|
||||
property int stronglyAdviseUpdateThreshold: 200 // packages
|
||||
|
||||
@@ -59,7 +59,7 @@ Singleton {
|
||||
property string hyprlandInstanceSignature: ""
|
||||
|
||||
property JsonObject ai: JsonObject {
|
||||
property string model
|
||||
property string model: "gemini-2.5-flash"
|
||||
property real temperature: 0.5
|
||||
}
|
||||
|
||||
|
||||
@@ -135,4 +135,38 @@ Singleton {
|
||||
var c = Qt.color(color);
|
||||
return c.hslLightness < 0.5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps a value to the inclusive range [0, 1].
|
||||
*
|
||||
* @param {number} x - The value to clamp.
|
||||
* @returns {number} The clamped value in the range [0, 1].
|
||||
*/
|
||||
function clamp01(x) {
|
||||
return Math.min(1, Math.max(0, x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Solves for the solid overlay color that, when composited over a base color
|
||||
* with a given opacity, yields the target color.
|
||||
*
|
||||
* The compositing equation is:
|
||||
* result = overlay * overlayOpacity + base * (1 - overlayOpacity)
|
||||
*
|
||||
* This function algebraically inverts that equation per channel.
|
||||
*
|
||||
* @param {Qt.rgba} baseColor - The base (background) color.
|
||||
* @param {Qt.rgba} targetColor - The resulting color after compositing.
|
||||
* @param {number} overlayOpacity - The overlay opacity (0-1).
|
||||
* @returns {Qt.rgba} The solved overlay color
|
||||
*/
|
||||
function solveOverlayColor(baseColor, targetColor, overlayOpacity) {
|
||||
let invA = 1.0 - overlayOpacity;
|
||||
|
||||
let r = (targetColor.r - baseColor.r * invA) / overlayOpacity;
|
||||
let g = (targetColor.g - baseColor.g * invA) / overlayOpacity;
|
||||
let b = (targetColor.b - baseColor.b * invA) / overlayOpacity;
|
||||
|
||||
return Qt.rgba(clamp01(r), clamp01(g), clamp01(b), overlayOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ Scope {
|
||||
WlSessionLock {
|
||||
id: lock
|
||||
locked: GlobalStates.screenLocked
|
||||
|
||||
surface: root.sessionLockSurface
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
pragma Singleton
|
||||
import qs.modules.common
|
||||
import qs.modules.common.utils
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt.labs.synchronizer
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
enum Action {
|
||||
Copy,
|
||||
Edit,
|
||||
Search,
|
||||
CharRecognition,
|
||||
Record,
|
||||
RecordWithSound
|
||||
}
|
||||
|
||||
property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl
|
||||
property string fileUploadApiEndpoint: "https://uguu.se/upload"
|
||||
|
||||
function getCommand(x, y, width, height, screenshotPath, action, saveDir = "") {
|
||||
// Set command for action
|
||||
const rx = Math.round(x);
|
||||
const ry = Math.round(y);
|
||||
const rw = Math.round(width);
|
||||
const rh = Math.round(height);
|
||||
const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(screenshotPath)} `
|
||||
+ `-crop ${rw}x${rh}+${rx}+${ry}`
|
||||
const cropToStdout = `${cropBase} -`
|
||||
const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'`
|
||||
const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'`
|
||||
const slurpRegion = `${rx},${ry} ${rw}x${rh}`
|
||||
const uploadAndGetUrl = (filePath) => {
|
||||
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
||||
}
|
||||
const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`;
|
||||
switch (action) {
|
||||
case ScreenshotAction.Action.Copy:
|
||||
if (saveDir === "") {
|
||||
// not saving the screenshot, just copy to clipboard
|
||||
return ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
}
|
||||
return [
|
||||
"bash", "-c",
|
||||
`mkdir -p '${StringUtils.shellSingleQuoteEscape(saveDir)}' && \
|
||||
saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \
|
||||
savePath="${saveDir}/$saveFileName" && \
|
||||
${cropToStdout} | tee >(wl-copy) > "$savePath" && \
|
||||
${cleanup}`
|
||||
]
|
||||
|
||||
break;
|
||||
case ScreenshotAction.Action.Edit:
|
||||
return ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`]
|
||||
break;
|
||||
case ScreenshotAction.Action.Search:
|
||||
return ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(screenshotPath)})" && ${cleanup}`]
|
||||
break;
|
||||
case ScreenshotAction.Action.CharRecognition:
|
||||
return ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
case ScreenshotAction.Action.Record:
|
||||
return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`]
|
||||
break;
|
||||
case ScreenshotAction.Action.RecordWithSound:
|
||||
return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`]
|
||||
break;
|
||||
default:
|
||||
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
Process {
|
||||
id: screenshotProc
|
||||
running: true
|
||||
property string screenshotDir: Directories.screenshotTemp
|
||||
required property ShellScreen screen
|
||||
property string screenshotPath: `${screenshotDir}/image-${screen.name}`
|
||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(screen.name)}' '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'`]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.services
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
spacing: 10
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
|
||||
property string text: ""
|
||||
property string buttonIcon: ""
|
||||
property alias value: slider.value
|
||||
property alias stopIndicatorValues: slider.stopIndicatorValues
|
||||
property bool usePercentTooltip: true
|
||||
property real from: slider.from
|
||||
property real to: slider.to
|
||||
property real textWidth: 120
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
spacing: 10
|
||||
|
||||
OptionalMaterialSymbol {
|
||||
id: iconWidget
|
||||
icon: root.buttonIcon
|
||||
iconSize: Appearance.font.pixelSize.larger
|
||||
}
|
||||
StyledText {
|
||||
id: labelWidget
|
||||
Layout.preferredWidth: root.textWidth
|
||||
text: root.text
|
||||
color: Appearance.colors.colOnSecondaryContainer
|
||||
}
|
||||
}
|
||||
|
||||
StyledSlider {
|
||||
id: slider
|
||||
configuration: StyledSlider.Configuration.XS
|
||||
usePercentTooltip: root.usePercentTooltip
|
||||
value: root.value
|
||||
from: root.from
|
||||
to: root.to
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
Canvas {
|
||||
id: root
|
||||
property color color: "#ffffff"
|
||||
property int dashLength: 6
|
||||
property int gapLength: 4
|
||||
property int borderWidth: 1
|
||||
|
||||
onDashLengthChanged: requestPaint()
|
||||
onGapLengthChanged: requestPaint()
|
||||
onWidthChanged: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
ctx.save();
|
||||
ctx.strokeStyle = root.color;
|
||||
ctx.lineWidth = root.borderWidth;
|
||||
if (root.gapLength > 0) {
|
||||
ctx.setLineDash([root.dashLength, root.gapLength]); // Set dash pattern
|
||||
}
|
||||
ctx.strokeRect(root.borderWidth / 2, root.borderWidth / 2, width - root.borderWidth, height - root.borderWidth); // Draw it
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,16 @@ MouseArea {
|
||||
property bool automaticallyReset: true
|
||||
readonly property real dragDiffX: _dragDiffX
|
||||
readonly property real dragDiffY: _dragDiffY
|
||||
property real startX: 0
|
||||
property real startY: 0
|
||||
property real regionTopLeftX: Math.min(startX, startX + _dragDiffX)
|
||||
property real regionTopLeftY: Math.min(startY, startY + _dragDiffY)
|
||||
property real regionWidth: Math.abs(_dragDiffX)
|
||||
property real regionHeight: Math.abs(_dragDiffY)
|
||||
|
||||
signal dragPressed(diffX: real, diffY: real)
|
||||
signal dragReleased(diffX: real, diffY: real)
|
||||
|
||||
property real startX: 0
|
||||
property real startY: 0
|
||||
property bool dragging: false
|
||||
property real _dragDiffX: 0
|
||||
property real _dragDiffY: 0
|
||||
|
||||
@@ -121,7 +121,7 @@ MouseArea { // Notification group area
|
||||
id: background
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
color: popup ? ColorUtils.applyAlpha(Appearance.colors.colLayer2, 1 - Appearance.backgroundTransparency) : Appearance.colors.colLayer2
|
||||
color: popup ? Appearance.colors.colBackgroundSurfaceContainer : Appearance.colors.colLayer2
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.leftMargin: root.xOffset
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ Item {
|
||||
|
||||
Rectangle { // The dialog
|
||||
id: dialog
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.fill: parent
|
||||
anchors.margins: dialogMargin
|
||||
|
||||
@@ -192,7 +192,7 @@ ComboBox {
|
||||
id: popupBackground
|
||||
anchors.fill: parent
|
||||
radius: Appearance.rounding.normal
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ Slider {
|
||||
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
|
||||
property real handleMargins: 4
|
||||
property real trackDotSize: 3
|
||||
property string tooltipContent: `${Math.round(value * 100)}%`
|
||||
property bool usePercentTooltip: true
|
||||
property string tooltipContent: usePercentTooltip ? `${Math.round(((value - from) / (to - from)) * 100)}%` : `${Math.round(value)}`
|
||||
property bool wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect
|
||||
property bool animateWave: true
|
||||
property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
|
||||
|
||||
@@ -12,19 +12,30 @@ Item {
|
||||
required property var tabButtonList
|
||||
|
||||
function incrementCurrentIndex() {
|
||||
tabBar.incrementCurrentIndex()
|
||||
tabBar.incrementCurrentIndex();
|
||||
}
|
||||
function decrementCurrentIndex() {
|
||||
tabBar.decrementCurrentIndex()
|
||||
tabBar.decrementCurrentIndex();
|
||||
}
|
||||
function setCurrentIndex(index) {
|
||||
tabBar.setCurrentIndex(index)
|
||||
tabBar.setCurrentIndex(index);
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: 40
|
||||
|
||||
property Component delegate: ToolbarTabButton {
|
||||
required property int index
|
||||
required property var modelData
|
||||
current: index == root.currentIndex
|
||||
text: modelData.name
|
||||
materialSymbol: modelData.icon
|
||||
onClicked: {
|
||||
root.setCurrentIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: contentItem
|
||||
z: 1
|
||||
@@ -33,16 +44,7 @@ Item {
|
||||
|
||||
Repeater {
|
||||
model: root.tabButtonList
|
||||
delegate: ToolbarTabButton {
|
||||
required property int index
|
||||
required property var modelData
|
||||
current: index == root.currentIndex
|
||||
text: modelData.name
|
||||
materialSymbol: modelData.icon
|
||||
onClicked: {
|
||||
root.setCurrentIndex(index)
|
||||
}
|
||||
}
|
||||
delegate: root.delegate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,23 +78,23 @@ Item {
|
||||
z: 2
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onWheel: (event) => {
|
||||
onWheel: event => {
|
||||
if (event.angleDelta.y < 0) {
|
||||
root.incrementCurrentIndex();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
root.decrementCurrentIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TabBar doesn't allow tabs to be of different sizes. Literally unusable.
|
||||
// TabBar doesn't allow tabs to be of different sizes. That's what I thought...
|
||||
// We use it only for the logic and draw stuff manually
|
||||
TabBar {
|
||||
id: tabBar
|
||||
z: -1
|
||||
background: null
|
||||
Repeater { // This is to fool the TabBar that it has tabs so it does the indices properly
|
||||
Repeater {
|
||||
// This is to fool the TabBar that it has tabs so it does the indices properly
|
||||
model: root.tabButtonList.length
|
||||
delegate: TabButton {
|
||||
background: null
|
||||
|
||||
Submodule dots/.config/quickshell/ii/modules/common/widgets/shapes updated: 8aa62a41bd...e31ec4cb4e
+2
-1
@@ -59,7 +59,8 @@ AbstractWidget {
|
||||
function onReadyChanged() { refreshPlacementIfNeeded() }
|
||||
}
|
||||
function refreshPlacementIfNeeded() {
|
||||
if (!Config.ready || (root.placementStrategy === "free" && root.needsColText)) return;
|
||||
if (!Config.ready) return;
|
||||
if (root.placementStrategy === "free" && !root.needsColText) return;
|
||||
leastBusyRegionProc.wallpaperPath = root.wallpaperPath;
|
||||
leastBusyRegionProc.running = false;
|
||||
leastBusyRegionProc.running = true;
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
font {
|
||||
family: Appearance.font.family.expressive
|
||||
pixelSize: 20
|
||||
weight: 350
|
||||
// Set empty to prevent conflicts, not meaningless
|
||||
styleName: ""
|
||||
variableAxes: ({})
|
||||
}
|
||||
style: Text.Raised
|
||||
styleColor: Appearance.colors.colShadow
|
||||
animateChange: Config.options.background.widgets.clock.digital.animateChange
|
||||
}
|
||||
@@ -26,7 +26,7 @@ AbstractBackgroundWidget {
|
||||
visibleWhenLocked: true
|
||||
|
||||
property var textHorizontalAlignment: {
|
||||
if (root.forceCenter)
|
||||
if (!Config.options.background.widgets.clock.digital.adaptiveAlignment || root.forceCenter || Config.options.background.widgets.clock.digital.vertical)
|
||||
return Text.AlignHCenter;
|
||||
if (root.x < root.scaledScreenWidth / 3)
|
||||
return Text.AlignLeft;
|
||||
@@ -63,32 +63,9 @@ AbstractBackgroundWidget {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
shown: root.clockStyle === "digital" && (root.shouldShow)
|
||||
fade: false
|
||||
sourceComponent: ColumnLayout {
|
||||
id: clockColumn
|
||||
spacing: 6
|
||||
|
||||
ClockText {
|
||||
font.pixelSize: 90
|
||||
text: DateTime.time
|
||||
}
|
||||
ClockText {
|
||||
Layout.topMargin: -5
|
||||
text: DateTime.longDate
|
||||
}
|
||||
StyledText {
|
||||
// Somehow gets fucked up if made a ClockText???
|
||||
visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.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.widgets.clock.quote.text
|
||||
}
|
||||
sourceComponent: DigitalClock {
|
||||
colText: root.colText
|
||||
textHorizontalAlignment: root.textHorizontalAlignment
|
||||
}
|
||||
}
|
||||
StatusRow {
|
||||
@@ -154,19 +131,6 @@ AbstractBackgroundWidget {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -190,6 +154,7 @@ AbstractBackgroundWidget {
|
||||
ClockText {
|
||||
id: statusTextWidget
|
||||
color: statusTextRow.textColor
|
||||
horizontalAlignment: root.textHorizontalAlignment
|
||||
anchors.verticalCenter: statusTextRow.verticalCenter
|
||||
font {
|
||||
pixelSize: Appearance.font.pixelSize.large
|
||||
|
||||
@@ -66,16 +66,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Config
|
||||
function onReadyChanged() {
|
||||
categoryFileView.path = Directories.generatedWallpaperCategoryPath
|
||||
}
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: categoryFileView
|
||||
path: ""
|
||||
path: Config.ready ? Directories.generatedWallpaperCategoryPath : ""
|
||||
watchChanges: true
|
||||
onFileChanged: reload()
|
||||
onLoaded: {
|
||||
@@ -85,7 +78,7 @@ Item {
|
||||
|
||||
property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie
|
||||
StyledDropShadow {
|
||||
target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||
target: root.useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: Config.options.background.widgets.clock.cookie.constantlyRotate
|
||||
@@ -100,7 +93,7 @@ Item {
|
||||
id: sineCookieLoader
|
||||
z: 0
|
||||
visible: false // The DropShadow already draws it
|
||||
active: useSineCookie
|
||||
active: root.useSineCookie
|
||||
sourceComponent: SineCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.widgets.clock.cookie.sides
|
||||
@@ -111,7 +104,7 @@ Item {
|
||||
id: roundedPolygonCookieLoader
|
||||
z: 0
|
||||
visible: false // The DropShadow already draws it
|
||||
active: !useSineCookie
|
||||
active: !root.useSineCookie
|
||||
sourceComponent: MaterialCookie {
|
||||
implicitSize: root.implicitSize
|
||||
sides: Config.options.background.widgets.clock.cookie.sides
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
id: clockColumn
|
||||
spacing: 4
|
||||
|
||||
property bool isVertical: Config.options.background.widgets.clock.digital.vertical
|
||||
property color colText: Appearance.colors.colOnSecondaryContainer
|
||||
property var textHorizontalAlignment: Text.AlignHCenter
|
||||
|
||||
// Time
|
||||
ClockText {
|
||||
id: timeTextTop
|
||||
text: clockColumn.isVertical ? DateTime.time.split(":")[0].padStart(2, "0") : DateTime.time
|
||||
color: clockColumn.colText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font {
|
||||
pixelSize: Config.options.background.widgets.clock.digital.font.size
|
||||
weight: Config.options.background.widgets.clock.digital.font.weight
|
||||
family: Config.options.background.widgets.clock.digital.font.family
|
||||
variableAxes: ({
|
||||
"wdth": Config.options.background.widgets.clock.digital.font.width,
|
||||
"ROND": Config.options.background.widgets.clock.digital.font.roundness
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.topMargin: -40
|
||||
Layout.fillWidth: true
|
||||
active: clockColumn.isVertical
|
||||
visible: active
|
||||
sourceComponent: ClockText {
|
||||
id: timeTextBottom
|
||||
text: DateTime.time.split(":")[1].split(" ")[0].padStart(2, "0")
|
||||
color: clockColumn.colText
|
||||
horizontalAlignment: clockColumn.textHorizontalAlignment
|
||||
font {
|
||||
pixelSize: timeTextTop.font.pixelSize
|
||||
weight: timeTextTop.font.weight
|
||||
family: timeTextTop.font.family
|
||||
variableAxes: timeTextTop.font.variableAxes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Date
|
||||
ClockText {
|
||||
visible: Config.options.background.widgets.clock.digital.showDate
|
||||
Layout.topMargin: -20
|
||||
Layout.fillWidth: true
|
||||
text: DateTime.longDate
|
||||
color: clockColumn.colText
|
||||
horizontalAlignment: clockColumn.textHorizontalAlignment
|
||||
}
|
||||
|
||||
// Quote
|
||||
ClockText {
|
||||
visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
text: Config.options.background.widgets.clock.quote.text
|
||||
animateChange: false
|
||||
color: clockColumn.colText
|
||||
horizontalAlignment: clockColumn.textHorizontalAlignment
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
@@ -60,6 +62,7 @@ Scope {
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
// Positioning
|
||||
anchors {
|
||||
top: !Config.options.bar.bottom
|
||||
bottom: Config.options.bar.bottom
|
||||
@@ -72,6 +75,14 @@ Scope {
|
||||
bottom: (Config.options.interactions.deadPixelWorkaround.enable && barRoot.anchors.bottom) * -1
|
||||
}
|
||||
|
||||
// Include in focus grab
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addPersistent(barRoot);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removePersistent(barRoot);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hoverRegion
|
||||
hoverEnabled: true
|
||||
|
||||
@@ -80,19 +80,21 @@ Item { // Bar content region
|
||||
RowLayout {
|
||||
id: leftSectionRowLayout
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
spacing: 0
|
||||
|
||||
LeftSidebarButton { // Left sidebar button
|
||||
id: leftSidebarButton
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: Appearance.rounding.screenRounding
|
||||
colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)
|
||||
}
|
||||
|
||||
ActiveWindow {
|
||||
visible: root.useShortenedForm === 0
|
||||
Layout.leftMargin: 10 + (leftSidebarButton.visible ? 0 : Appearance.rounding.screenRounding)
|
||||
Layout.rightMargin: Appearance.rounding.screenRounding
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
visible: root.useShortenedForm === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,11 @@ RippleButton {
|
||||
|
||||
property bool showPing: false
|
||||
|
||||
property bool aiChatEnabled: Config.options.policies.ai !== 0
|
||||
property bool translatorEnabled: Config.options.sidebar.translator.enable
|
||||
property bool animeEnabled: Config.options.policies.weeb !== 0
|
||||
visible: aiChatEnabled || translatorEnabled || animeEnabled
|
||||
|
||||
property real buttonPadding: 5
|
||||
implicitWidth: distroIcon.width + buttonPadding * 2
|
||||
implicitHeight: distroIcon.height + buttonPadding * 2
|
||||
|
||||
@@ -18,9 +18,10 @@ Item {
|
||||
property bool borderless: Config.options.bar.borderless
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen)
|
||||
readonly property Toplevel activeWindow: ToplevelManager.activeToplevel
|
||||
readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1
|
||||
|
||||
readonly property int workspacesShown: Config.options.bar.workspaces.shown
|
||||
readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown)
|
||||
readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / root.workspacesShown)
|
||||
property list<bool> workspaceOccupied: []
|
||||
property int widgetPadding: 4
|
||||
property int workspaceButtonWidth: 26
|
||||
@@ -29,7 +30,7 @@ Item {
|
||||
property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55
|
||||
property real workspaceIconOpacityShrinked: 1
|
||||
property real workspaceIconMarginShrinked: -4
|
||||
property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown
|
||||
property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown
|
||||
|
||||
property bool showNumbers: false
|
||||
Timer {
|
||||
@@ -122,8 +123,8 @@ Item {
|
||||
implicitWidth: workspaceButtonWidth
|
||||
implicitHeight: workspaceButtonWidth
|
||||
radius: (width / 2)
|
||||
property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index))
|
||||
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2))
|
||||
property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index))
|
||||
property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+2))
|
||||
property var radiusPrev: previousOccupied ? 0 : (width / 2)
|
||||
property var radiusNext: rightOccupied ? 0 : (width / 2)
|
||||
|
||||
@@ -133,7 +134,7 @@ Item {
|
||||
bottomRightRadius: radiusNext
|
||||
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4)
|
||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0
|
||||
opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+1)) ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
@@ -225,7 +226,7 @@ Item {
|
||||
}
|
||||
text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue
|
||||
elide: Text.ElideRight
|
||||
color: (monitor?.activeWorkspace?.id == button.workspaceValue) ?
|
||||
color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ?
|
||||
Appearance.m3colors.m3onPrimary :
|
||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||
Appearance.colors.colOnLayer1Inactive)
|
||||
@@ -245,7 +246,7 @@ Item {
|
||||
width: workspaceButtonWidth * 0.18
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: (monitor?.activeWorkspace?.id == button.workspaceValue) ?
|
||||
color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ?
|
||||
Appearance.m3colors.m3onPrimary :
|
||||
(workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer :
|
||||
Appearance.colors.colOnLayer1Inactive)
|
||||
|
||||
@@ -100,5 +100,16 @@ StyledPopup {
|
||||
value: Weather.data.sunset
|
||||
}
|
||||
}
|
||||
|
||||
// Footer: last refresh
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Translation.tr("Last refresh: %1").arg(Weather.data.lastRefresh)
|
||||
font {
|
||||
weight: Font.Medium
|
||||
pixelSize: Appearance.font.pixelSize.smaller
|
||||
}
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,13 +54,16 @@ Scope { // Scope
|
||||
item: cheatsheetBackground
|
||||
}
|
||||
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [cheatsheetRoot]
|
||||
active: cheatsheetRoot.visible
|
||||
onCleared: () => {
|
||||
if (!active)
|
||||
cheatsheetRoot.hide();
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addDismissable(cheatsheetRoot);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removeDismissable(cheatsheetRoot);
|
||||
}
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
cheatsheetRoot.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ Item {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
}
|
||||
clip: true
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Appearance.sizes.elevationMargin
|
||||
|
||||
@@ -6,36 +6,61 @@ import qs.modules.common.functions
|
||||
import qs.modules.common.panels.lock
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
LockScreen {
|
||||
id: root
|
||||
|
||||
// Monitor name -> workspace id to restore on unlock (set when locking)
|
||||
property var savedWorkspaces: ({})
|
||||
|
||||
Timer {
|
||||
id: restoreTimer
|
||||
interval: 150
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
var batch = ""
|
||||
for (var j = 0; j < Quickshell.screens.length; ++j) {
|
||||
var monName = Quickshell.screens[j].name
|
||||
var wsId = root.savedWorkspaces[monName]
|
||||
if (wsId !== undefined) {
|
||||
batch += "dispatch focusmonitor " + monName + "; dispatch workspace " + wsId + "; "
|
||||
}
|
||||
}
|
||||
if (batch.length > 0) {
|
||||
Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lockSurface: LockSurface {
|
||||
context: root.context
|
||||
}
|
||||
|
||||
// Push everything down
|
||||
property var windowData: []
|
||||
function saveWindowPositionAndTile() {
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]);
|
||||
root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id));
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`);
|
||||
Hyprland.dispatch(`settiled address:${w.address}`);
|
||||
Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`);
|
||||
});
|
||||
}
|
||||
function restoreWindowPositionAndTile() {
|
||||
root.windowData.forEach(w => {
|
||||
Hyprland.dispatch(`setfloating address:${w.address}`);
|
||||
Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`);
|
||||
Hyprland.dispatch(`pseudo address:${w.address}`);
|
||||
});
|
||||
Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]);
|
||||
// Single batch for lock and unlock so we don't race multiple hyprctl calls
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onScreenLockedChanged() {
|
||||
if (GlobalStates.screenLocked) {
|
||||
// Lock: save workspace per monitor and move all to temp workspace in one batch
|
||||
var next = {}
|
||||
var batch = "keyword animation workspaces,1,7,menu_decel,slidevert; "
|
||||
for (var i = 0; i < Quickshell.screens.length; ++i) {
|
||||
var mon = Quickshell.screens[i].name
|
||||
var mData = HyprlandData.monitors.find(m => m.name === mon)
|
||||
var ws = (mData?.activeWorkspace?.id ?? 1)
|
||||
next[mon] = ws
|
||||
batch += "dispatch focusmonitor " + mon + "; dispatch workspace " + (2147483647 - ws) + "; "
|
||||
}
|
||||
root.savedWorkspaces = next
|
||||
Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"])
|
||||
} else {
|
||||
restoreTimer.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push everything down (visual only; workspace switch is in Connections above)
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: Scope {
|
||||
@@ -44,15 +69,6 @@ LockScreen {
|
||||
property string targetMonitorName: modelData.name
|
||||
property int verticalMovementDistance: modelData.height
|
||||
property int horizontalSqueeze: modelData.width * 0.2
|
||||
onShouldPushChanged: {
|
||||
if (shouldPush) {
|
||||
root.saveWindowPositionAndTile();
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]);
|
||||
} else {
|
||||
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]);
|
||||
root.restoreWindowPositionAndTile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ MouseArea {
|
||||
// Style
|
||||
clip: true
|
||||
font.pixelSize: Appearance.font.pixelSize.small
|
||||
selectedTextColor: materialShapeChars ? "transparent" : Appearance.colors.colOnSecondaryContainer
|
||||
selectionColor: materialShapeChars ? "transparent" : Appearance.colors.colSecondaryContainer
|
||||
|
||||
// Password
|
||||
enabled: !root.context.unlockInProgress
|
||||
@@ -195,6 +197,9 @@ MouseArea {
|
||||
}
|
||||
sourceComponent: PasswordChars {
|
||||
length: root.context.currentText.length
|
||||
selectionStart: passwordBox.selectionStart
|
||||
selectionEnd: passwordBox.selectionEnd
|
||||
cursorPosition: passwordBox.cursorPosition
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,7 +220,7 @@ MouseArea {
|
||||
iconSize: 24
|
||||
text: {
|
||||
if (root.context.targetAction === LockContext.ActionEnum.Unlock) {
|
||||
return root.ctrlHeld ? "emoji_food_beverage" : "arrow_right_alt";
|
||||
return root.ctrlHeld ? "coffee" : "arrow_right_alt";
|
||||
} else if (root.context.targetAction === LockContext.ActionEnum.Poweroff) {
|
||||
return "power_settings_new";
|
||||
} else if (root.context.targetAction === LockContext.ActionEnum.Reboot) {
|
||||
|
||||
@@ -9,29 +9,62 @@ import Quickshell
|
||||
|
||||
StyledFlickable {
|
||||
id: root
|
||||
|
||||
required property int length
|
||||
property int selectionStart
|
||||
property int selectionEnd
|
||||
property int cursorPosition
|
||||
|
||||
property color color: Appearance.colors.colPrimary
|
||||
property color selectedTextColor: Appearance.colors.colOnSecondaryContainer
|
||||
property color selectionColor: Appearance.colors.colSecondaryContainer
|
||||
|
||||
property int charSize: 20
|
||||
|
||||
contentWidth: dotsRow.implicitWidth
|
||||
contentX: (Math.max(contentWidth - width, 0))
|
||||
Behavior on contentX {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cursor
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: root.charSize * root.cursorPosition
|
||||
}
|
||||
color: root.color
|
||||
implicitWidth: 2
|
||||
implicitHeight: root.charSize
|
||||
Behavior on anchors.leftMargin {
|
||||
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: dotsRow
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
leftMargin: 4
|
||||
leftMargin: 4 - 5 // -5 to account for spacing being simulated by char item width
|
||||
}
|
||||
spacing: 10
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
model: ScriptModel { // TODO: use proper custom object model to insert new char at the correct pos
|
||||
values: Array(root.length)
|
||||
}
|
||||
delegate: Item {
|
||||
|
||||
delegate: Rectangle {
|
||||
id: charItem
|
||||
required property int index
|
||||
implicitWidth: 10
|
||||
implicitHeight: 10
|
||||
implicitWidth: root.charSize
|
||||
implicitHeight: root.charSize
|
||||
property bool selected: index >= root.selectionStart && index < root.selectionEnd
|
||||
|
||||
color: ColorUtils.transparentize(root.selectionColor, selected ? 0 : 1)
|
||||
|
||||
MaterialShape {
|
||||
id: materialShape
|
||||
anchors.centerIn: parent
|
||||
@@ -46,7 +79,7 @@ StyledFlickable {
|
||||
]
|
||||
shape: charShapes[charItem.index % charShapes.length]
|
||||
// Animate on appearance
|
||||
color: Appearance.colors.colPrimary
|
||||
color: charItem.selected ? root.selectedTextColor : root.color
|
||||
implicitSize: 0
|
||||
opacity: 0
|
||||
scale: 0.5
|
||||
|
||||
@@ -81,7 +81,7 @@ Scope {
|
||||
}
|
||||
|
||||
sourceComponent: PanelWindow {
|
||||
id: mediaControlsRoot
|
||||
id: panelWindow
|
||||
visible: true
|
||||
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
@@ -98,9 +98,9 @@ Scope {
|
||||
right: Config.options.bar.vertical && Config.options.bar.bottom
|
||||
}
|
||||
margins {
|
||||
top: Config.options.bar.vertical ? ((mediaControlsRoot.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight
|
||||
top: Config.options.bar.vertical ? ((panelWindow.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight
|
||||
bottom: Appearance.sizes.barHeight
|
||||
left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((mediaControlsRoot.screen.width / 2) - (osdWidth / 2) - widgetWidth)
|
||||
left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((panelWindow.screen.width / 2) - (osdWidth / 2) - widgetWidth)
|
||||
right: Appearance.sizes.barHeight
|
||||
}
|
||||
|
||||
@@ -108,13 +108,16 @@ Scope {
|
||||
item: playerColumnLayout
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [mediaControlsRoot]
|
||||
active: mediaControlsLoader.active
|
||||
onCleared: () => {
|
||||
if (!active) {
|
||||
GlobalStates.mediaControlsOpen = false;
|
||||
}
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addDismissable(panelWindow);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removeDismissable(panelWindow);
|
||||
}
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
GlobalStates.mediaControlsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,10 +140,13 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
Item { // No player placeholder
|
||||
Item {
|
||||
// No player placeholder
|
||||
Layout.alignment: {
|
||||
if (mediaControlsRoot.anchors.left) return Qt.AlignLeft;
|
||||
if (mediaControlsRoot.anchors.right) return Qt.AlignRight;
|
||||
if (panelWindow.anchors.left)
|
||||
return Qt.AlignLeft;
|
||||
if (panelWindow.anchors.right)
|
||||
return Qt.AlignRight;
|
||||
return Qt.AlignHCenter;
|
||||
}
|
||||
Layout.leftMargin: Appearance.sizes.hyprlandGapsOut
|
||||
@@ -153,7 +159,7 @@ Scope {
|
||||
target: placeholderBackground
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Rectangle {
|
||||
id: placeholderBackground
|
||||
anchors.centerIn: parent
|
||||
color: Appearance.colors.colLayer0
|
||||
|
||||
@@ -57,6 +57,13 @@ Scope { // Scope
|
||||
item: oskBackground
|
||||
}
|
||||
|
||||
// Make it usable with other panels
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addPersistent(oskRoot);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removePersistent(oskRoot);
|
||||
}
|
||||
|
||||
// Background
|
||||
StyledRectangularShadow {
|
||||
|
||||
@@ -4,5 +4,5 @@ import qs.modules.common
|
||||
Rectangle {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ AbstractOverlayWidget {
|
||||
fill: parent
|
||||
margins: root.resizeMargin
|
||||
}
|
||||
color: ColorUtils.transparentize(Appearance.colors.colLayer1, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1)
|
||||
color: ColorUtils.transparentize(Appearance.colors.colLayer1Base, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1)
|
||||
radius: root.radius
|
||||
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||
border.width: 1
|
||||
@@ -217,7 +217,7 @@ AbstractOverlayWidget {
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: titleBarRow.implicitWidth + root.padding * 2
|
||||
implicitHeight: titleBarRow.implicitHeight + root.padding * 2
|
||||
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1
|
||||
color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1Base
|
||||
// border.color: Appearance.colors.colOutlineVariant
|
||||
// border.width: 1
|
||||
|
||||
|
||||
@@ -14,116 +14,96 @@ import Quickshell.Hyprland
|
||||
Scope {
|
||||
id: overviewScope
|
||||
property bool dontAutoCancelSearch: false
|
||||
Variants {
|
||||
id: overviewVariants
|
||||
model: Quickshell.screens
|
||||
PanelWindow {
|
||||
id: root
|
||||
required property var modelData
|
||||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
screen: modelData
|
||||
|
||||
PanelWindow {
|
||||
id: panelWindow
|
||||
property string searchingText: ""
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
WlrLayershell.namespace: "quickshell:overview"
|
||||
WlrLayershell.layer: WlrLayer.Top
|
||||
WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (!GlobalStates.overviewOpen) {
|
||||
searchWidget.disableExpandAnimation();
|
||||
overviewScope.dontAutoCancelSearch = false;
|
||||
GlobalFocusGrab.dismiss();
|
||||
} else {
|
||||
if (!overviewScope.dontAutoCancelSearch) {
|
||||
searchWidget.cancelSearch();
|
||||
}
|
||||
GlobalFocusGrab.addDismissable(panelWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchWidget.setSearchingText(text);
|
||||
searchWidget.focusFirstItem();
|
||||
}
|
||||
|
||||
Column {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
|
||||
WlrLayershell.namespace: "quickshell:overview"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
mask: Region {
|
||||
item: GlobalStates.overviewOpen ? columnLayout : null
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
}
|
||||
spacing: -8
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [root]
|
||||
property bool canBeActive: root.monitorIsFocused
|
||||
active: false
|
||||
onCleared: () => {
|
||||
if (!active)
|
||||
GlobalStates.overviewOpen = false;
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
} else if (event.key === Qt.Key_Left) {
|
||||
if (!panelWindow.searchingText)
|
||||
Hyprland.dispatch("workspace r-1");
|
||||
} else if (event.key === Qt.Key_Right) {
|
||||
if (!panelWindow.searchingText)
|
||||
Hyprland.dispatch("workspace r+1");
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (!GlobalStates.overviewOpen) {
|
||||
searchWidget.disableExpandAnimation();
|
||||
overviewScope.dontAutoCancelSearch = false;
|
||||
} else {
|
||||
if (!overviewScope.dontAutoCancelSearch) {
|
||||
searchWidget.cancelSearch();
|
||||
}
|
||||
delayedGrabTimer.start();
|
||||
}
|
||||
SearchWidget {
|
||||
id: searchWidget
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Synchronizer on searchingText {
|
||||
property alias source: panelWindow.searchingText
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedGrabTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (!grab.canBeActive)
|
||||
return;
|
||||
grab.active = GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
|
||||
implicitWidth: columnLayout.implicitWidth
|
||||
implicitHeight: columnLayout.implicitHeight
|
||||
|
||||
function setSearchingText(text) {
|
||||
searchWidget.setSearchingText(text);
|
||||
searchWidget.focusFirstItem();
|
||||
}
|
||||
|
||||
Column {
|
||||
id: columnLayout
|
||||
visible: GlobalStates.overviewOpen
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
}
|
||||
spacing: -8
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
} else if (event.key === Qt.Key_Left) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r-1");
|
||||
} else if (event.key === Qt.Key_Right) {
|
||||
if (!root.searchingText)
|
||||
Hyprland.dispatch("workspace r+1");
|
||||
}
|
||||
}
|
||||
|
||||
SearchWidget {
|
||||
id: searchWidget
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
Synchronizer on searchingText {
|
||||
property alias source: root.searchingText
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
|
||||
sourceComponent: OverviewWidget {
|
||||
panelWindow: root
|
||||
visible: (root.searchingText == "")
|
||||
}
|
||||
Loader {
|
||||
id: overviewLoader
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
|
||||
sourceComponent: OverviewWidget {
|
||||
screen: panelWindow.screen
|
||||
visible: (panelWindow.searchingText == "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,15 +114,9 @@ Scope {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
|
||||
GlobalStates.overviewOpen = true;
|
||||
}
|
||||
|
||||
function toggleEmojis() {
|
||||
@@ -150,15 +124,9 @@ Scope {
|
||||
GlobalStates.overviewOpen = false;
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < overviewVariants.instances.length; i++) {
|
||||
let panelWindow = overviewVariants.instances[i];
|
||||
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) {
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.emojis);
|
||||
GlobalStates.overviewOpen = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
overviewScope.dontAutoCancelSearch = true;
|
||||
panelWindow.setSearchingText(Config.options.search.prefix.emojis);
|
||||
GlobalStates.overviewOpen = true;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
@@ -192,6 +160,14 @@ Scope {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewWorkspacesClose"
|
||||
description: "Closes overview on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "overviewWorkspacesToggle"
|
||||
description: "Toggles overview on press"
|
||||
|
||||
@@ -13,11 +13,13 @@ import Quickshell.Hyprland
|
||||
|
||||
Item {
|
||||
id: root
|
||||
required property var panelWindow
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
|
||||
required property var screen
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen)
|
||||
readonly property var toplevels: ToplevelManager.toplevels
|
||||
// Clamp to avoid lock-screen temp workspace (2147483647 - N) leaking into UI
|
||||
readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1))
|
||||
readonly property int workspacesShown: Config.options.overview.rows * Config.options.overview.columns
|
||||
readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown)
|
||||
readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / workspacesShown)
|
||||
property bool monitorIsFocused: (Hyprland.focusedMonitor?.name == monitor.name)
|
||||
property var windows: HyprlandData.windowList
|
||||
property var windowByAddress: HyprlandData.windowByAddress
|
||||
@@ -101,7 +103,7 @@ Item {
|
||||
required property int index
|
||||
property int colIndex: index
|
||||
property int workspaceValue: root.workspaceGroup * root.workspacesShown + getWsInCell(row.index, colIndex)
|
||||
property color defaultWorkspaceColor: ColorUtils.mix(Appearance.colors.colBackgroundSurfaceContainer, Appearance.colors.colSurfaceContainerHigh, 0.8)
|
||||
property color defaultWorkspaceColor: Appearance.colors.colSurfaceContainerLow
|
||||
property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1)
|
||||
property color hoveredBorderColor: Appearance.colors.colLayer2Hover
|
||||
property bool hoveredWhileDragging: false
|
||||
@@ -301,8 +303,8 @@ Item {
|
||||
|
||||
Rectangle { // Focused workspace indicator
|
||||
id: focusedWorkspaceIndicator
|
||||
property int rowIndex: getWsRow(monitor.activeWorkspace?.id)
|
||||
property int colIndex: getWsColumn(monitor.activeWorkspace?.id)
|
||||
property int rowIndex: getWsRow(root.effectiveActiveWorkspaceId)
|
||||
property int colIndex: getWsColumn(root.effectiveActiveWorkspaceId)
|
||||
x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex
|
||||
y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex
|
||||
z: root.windowZ
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
@@ -92,6 +89,16 @@ RowLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
if (LauncherSearch.results.length === 0) return;
|
||||
const tabbedText = LauncherSearch.results[0].name;
|
||||
LauncherSearch.query = tabbedText;
|
||||
searchInput.text = tabbedText;
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconToolbarButton {
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Qt.labs.synchronizer
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import Qt.labs.synchronizer
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
Item { // Wrapper
|
||||
id: root
|
||||
|
||||
readonly property string xdgConfigHome: Directories.config
|
||||
readonly property int typingDebounceInterval: 200
|
||||
readonly property int typingResultLimit: 15 // Should be enough to cover the whole view
|
||||
|
||||
property string searchingText: LauncherSearch.query
|
||||
property bool showResults: searchingText != ""
|
||||
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2
|
||||
implicitHeight: searchWidgetContent.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2
|
||||
|
||||
function focusFirstItem() {
|
||||
appResults.currentIndex = 0;
|
||||
@@ -178,30 +183,48 @@ Item { // Wrapper
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
id: model
|
||||
objectProp: "key"
|
||||
values: LauncherSearch.results
|
||||
onValuesChanged: {
|
||||
root.focusFirstItem();
|
||||
Timer {
|
||||
id: debounceTimer
|
||||
interval: root.typingDebounceInterval
|
||||
onTriggered: {
|
||||
resultModel.values = LauncherSearch.results ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: LauncherSearch
|
||||
function onResultsChanged() {
|
||||
resultModel.values = LauncherSearch.results.slice(0, root.typingResultLimit);
|
||||
root.focusFirstItem();
|
||||
debounceTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
model: ScriptModel {
|
||||
id: resultModel
|
||||
objectProp: "key"
|
||||
}
|
||||
|
||||
delegate: SearchItem {
|
||||
id: searchItem
|
||||
// The selectable item for each search result
|
||||
required property var modelData
|
||||
anchors.left: parent?.left
|
||||
anchors.right: parent?.right
|
||||
entry: modelData
|
||||
query: StringUtils.cleanOnePrefix(root.searchingText, [
|
||||
Config.options.search.prefix.action,
|
||||
Config.options.search.prefix.app,
|
||||
Config.options.search.prefix.clipboard,
|
||||
Config.options.search.prefix.emojis,
|
||||
Config.options.search.prefix.math,
|
||||
Config.options.search.prefix.shellCommand,
|
||||
Config.options.search.prefix.webSearch
|
||||
])
|
||||
query: StringUtils.cleanOnePrefix(root.searchingText, [Config.options.search.prefix.action, Config.options.search.prefix.app, Config.options.search.prefix.clipboard, Config.options.search.prefix.emojis, Config.options.search.prefix.math, Config.options.search.prefix.shellCommand, Config.options.search.prefix.webSearch])
|
||||
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Tab) {
|
||||
if (LauncherSearch.results.length === 0)
|
||||
return;
|
||||
const tabbedText = searchItem.modelData.name;
|
||||
LauncherSearch.query = tabbedText;
|
||||
searchBar.searchInput.text = tabbedText;
|
||||
event.accepted = true;
|
||||
root.focusSearchInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property var action
|
||||
property var selectionMode
|
||||
|
||||
property string description: switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return Translation.tr("Copy region (LMB) or annotate (RMB)");
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return Translation.tr("Search with Google Lens");
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return Translation.tr("Recognize text");
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return Translation.tr("Record region");
|
||||
}
|
||||
property string materialSymbol: switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return "content_cut";
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return "image_search";
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return "document_scanner";
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return "videocam";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
|
||||
property bool showDescription: true
|
||||
function hideDescription() {
|
||||
root.showDescription = false
|
||||
}
|
||||
Timer {
|
||||
id: descTimeout
|
||||
interval: 1000
|
||||
running: true
|
||||
onTriggered: {
|
||||
root.hideDescription()
|
||||
}
|
||||
}
|
||||
onActionChanged: {
|
||||
root.showDescription = true
|
||||
descTimeout.restart()
|
||||
}
|
||||
|
||||
property int margins: 8
|
||||
implicitWidth: content.implicitWidth + margins * 2
|
||||
implicitHeight: content.implicitHeight + margins * 2
|
||||
|
||||
Rectangle {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
|
||||
property real padding: 8
|
||||
implicitHeight: 38
|
||||
implicitWidth: root.showDescription ? contentRow.implicitWidth + padding * 2 : implicitHeight
|
||||
clip: true
|
||||
|
||||
topLeftRadius: 6
|
||||
bottomLeftRadius: implicitHeight - topLeftRadius
|
||||
bottomRightRadius: bottomLeftRadius
|
||||
topRightRadius: bottomLeftRadius
|
||||
|
||||
color: Appearance.colors.colPrimary
|
||||
|
||||
Behavior on topLeftRadius {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||
}
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: content.padding
|
||||
}
|
||||
spacing: 12
|
||||
|
||||
MaterialSymbol {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconSize: 22
|
||||
color: Appearance.colors.colOnPrimary
|
||||
animateChange: true
|
||||
text: root.materialSymbol
|
||||
}
|
||||
|
||||
FadeLoader {
|
||||
id: descriptionLoader
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
shown: root.showDescription
|
||||
sourceComponent: StyledText {
|
||||
color: Appearance.colors.colOnPrimary
|
||||
text: root.description
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,48 +23,6 @@ Toolbar {
|
||||
// Signals
|
||||
signal dismiss()
|
||||
|
||||
MaterialShape {
|
||||
Layout.fillHeight: true
|
||||
Layout.leftMargin: 2
|
||||
Layout.rightMargin: 2
|
||||
implicitSize: 36 // Intentionally smaller because this one is brighter than others
|
||||
shape: switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return MaterialShape.Shape.Cookie4Sided;
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return MaterialShape.Shape.Pentagon;
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return MaterialShape.Shape.Sunny;
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return MaterialShape.Shape.Gem;
|
||||
default:
|
||||
return MaterialShape.Shape.Cookie12Sided;
|
||||
}
|
||||
color: Appearance.colors.colPrimary
|
||||
MaterialSymbol {
|
||||
anchors.centerIn: parent
|
||||
iconSize: 22
|
||||
color: Appearance.colors.colOnPrimary
|
||||
animateChange: true
|
||||
text: switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return "content_cut";
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return "image_search";
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return "document_scanner";
|
||||
case RegionSelection.SnipAction.Record:
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return "videocam";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarTabBar {
|
||||
id: tabBar
|
||||
tabButtonList: [
|
||||
@@ -76,5 +34,4 @@ Toolbar {
|
||||
root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+29
-11
@@ -33,22 +33,40 @@ Item {
|
||||
}
|
||||
|
||||
// Selection border
|
||||
Rectangle {
|
||||
// Rectangle {
|
||||
// id: selectionBorder
|
||||
// z: 1
|
||||
// anchors {
|
||||
// left: parent.left
|
||||
// top: parent.top
|
||||
// leftMargin: root.regionX
|
||||
// topMargin: root.regionY
|
||||
// }
|
||||
// width: root.regionWidth
|
||||
// height: root.regionHeight
|
||||
// color: "transparent"
|
||||
// border.color: root.color
|
||||
// border.width: 2
|
||||
// // radius: root.standardRounding
|
||||
// radius: 0 // TODO: figure out how to make the overlay thing work with rounding
|
||||
// }
|
||||
|
||||
DashedBorder {
|
||||
id: selectionBorder
|
||||
z: 1
|
||||
z: 9
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: root.regionX
|
||||
topMargin: root.regionY
|
||||
leftMargin: Math.round(root.regionX) - borderWidth
|
||||
topMargin: Math.round(root.regionY) - borderWidth
|
||||
}
|
||||
width: root.regionWidth
|
||||
height: root.regionHeight
|
||||
color: "transparent"
|
||||
border.color: root.color
|
||||
border.width: 2
|
||||
// radius: root.standardRounding
|
||||
radius: 0 // TODO: figure out how to make the overlay thing work with rounding
|
||||
width: Math.round(root.regionWidth) + borderWidth * 2
|
||||
height: Math.round(root.regionHeight) + borderWidth * 2
|
||||
|
||||
color: root.color
|
||||
dashLength: 6
|
||||
gapLength: 3
|
||||
borderWidth: 1
|
||||
}
|
||||
|
||||
StyledText {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs.modules.common
|
||||
import qs.modules.common.utils
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
@@ -32,15 +33,9 @@ PanelWindow {
|
||||
property var action: RegionSelection.SnipAction.Copy
|
||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||
signal dismiss()
|
||||
|
||||
property string saveScreenshotDir: 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"
|
||||
property color overlayColor: "#88111111"
|
||||
property color overlayColor: ColorUtils.transparentize("#000000", 0.4)
|
||||
property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0
|
||||
property color brightSecondary: Appearance.m3colors.darkmode ? Appearance.colors.colSecondary : Appearance.colors.colOnSecondary
|
||||
property color brightTertiary: Appearance.m3colors.darkmode ? Appearance.colors.colTertiary : Qt.lighter(Appearance.colors.colPrimary)
|
||||
@@ -180,10 +175,12 @@ PanelWindow {
|
||||
property real regionX: Math.min(dragStartX, draggingX)
|
||||
property real regionY: Math.min(dragStartY, draggingY)
|
||||
|
||||
Process {
|
||||
TempScreenshotProcess {
|
||||
id: screenshotProc
|
||||
running: true
|
||||
command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`]
|
||||
screen: root.screen
|
||||
screenshotDir: root.screenshotDir
|
||||
screenshotPath: root.screenshotPath
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (root.enableContentRegions) imageDetectionProcess.running = true;
|
||||
root.preparationDone = !checkRecordingProc.running;
|
||||
@@ -229,6 +226,27 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
function getScreenshotAction() {
|
||||
switch(root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
return ScreenshotAction.Action.Copy;
|
||||
case RegionSelection.SnipAction.Edit:
|
||||
return ScreenshotAction.Action.Edit;
|
||||
case RegionSelection.SnipAction.Search:
|
||||
return ScreenshotAction.Action.Search;
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
return ScreenshotAction.Action.CharRecognition;
|
||||
case RegionSelection.SnipAction.Record:
|
||||
return ScreenshotAction.Action.Record;
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
return ScreenshotAction.Action.RecordWithSound;
|
||||
default:
|
||||
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
||||
root.dismiss();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function snip() {
|
||||
// Validity check
|
||||
if (root.regionWidth <= 0 || root.regionHeight <= 0) {
|
||||
@@ -246,62 +264,20 @@ PanelWindow {
|
||||
if (root.action === RegionSelection.SnipAction.Copy || root.action === RegionSelection.SnipAction.Edit) {
|
||||
root.action = root.mouseButton === Qt.RightButton ? RegionSelection.SnipAction.Edit : RegionSelection.SnipAction.Copy;
|
||||
}
|
||||
|
||||
// Set command for action
|
||||
const rx = Math.round(root.regionX * root.monitorScale);
|
||||
const ry = Math.round(root.regionY * root.monitorScale);
|
||||
const rw = Math.round(root.regionWidth * root.monitorScale);
|
||||
const rh = Math.round(root.regionHeight * root.monitorScale);
|
||||
const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} `
|
||||
+ `-crop ${rw}x${rh}+${rx}+${ry}`
|
||||
const cropToStdout = `${cropBase} -`
|
||||
const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||
const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`
|
||||
const slurpRegion = `${rx},${ry} ${rw}x${rh}`
|
||||
const uploadAndGetUrl = (filePath) => {
|
||||
return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'`
|
||||
}
|
||||
const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`;
|
||||
switch (root.action) {
|
||||
case RegionSelection.SnipAction.Copy:
|
||||
if (saveScreenshotDir === "") {
|
||||
// not saving the screenshot, just copy to clipboard
|
||||
snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
}
|
||||
|
||||
const savePathBase = root.saveScreenshotDir
|
||||
|
||||
snipProc.command = [
|
||||
"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} | ${annotationCommand} && ${cleanup}`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.Search:
|
||||
snipProc.command = ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(root.screenshotPath)})" && ${cleanup}`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.CharRecognition:
|
||||
snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.Record:
|
||||
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`]
|
||||
break;
|
||||
case RegionSelection.SnipAction.RecordWithSound:
|
||||
snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`]
|
||||
break;
|
||||
default:
|
||||
console.warn("[Region Selector] Unknown snip action, skipping snip.");
|
||||
root.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
const screenshotDir = Config.options.screenSnip.savePath !== "" ? //
|
||||
Config.options.screenSnip.savePath : "";
|
||||
var screenshotAction = root.getScreenshotAction();
|
||||
const command = ScreenshotAction.getCommand(
|
||||
root.regionX * root.monitorScale, //
|
||||
root.regionY * root.monitorScale, //
|
||||
root.regionWidth * root.monitorScale,//
|
||||
root.regionHeight * root.monitorScale, //
|
||||
root.screenshotPath, //
|
||||
screenshotAction, //
|
||||
screenshotDir
|
||||
)
|
||||
snipProc.command = command;
|
||||
|
||||
// Image post-processing
|
||||
snipProc.startDetached();
|
||||
@@ -399,6 +375,14 @@ PanelWindow {
|
||||
}
|
||||
}
|
||||
|
||||
CursorGuide {
|
||||
z: 9999
|
||||
x: root.dragging ? root.regionX + root.regionWidth : mouseArea.mouseX
|
||||
y: root.dragging ? root.regionY + root.regionHeight : mouseArea.mouseY
|
||||
action: root.action
|
||||
selectionMode: root.selectionMode
|
||||
}
|
||||
|
||||
// Window regions
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
@@ -471,7 +455,7 @@ PanelWindow {
|
||||
// Controls
|
||||
Row {
|
||||
id: regionSelectionControls
|
||||
z: 9999
|
||||
z: 10
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
|
||||
@@ -25,6 +25,10 @@ Rectangle {
|
||||
border.width: targeted ? 4 : 2
|
||||
radius: 4
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||
|
||||
@@ -45,9 +45,7 @@ Scope {
|
||||
WlrLayershell.namespace: "quickshell:session"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
// This is a big surface so we needa carefully choose the transparency,
|
||||
// or we'll get a large scary rgb blob
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.04 : 0.12)
|
||||
color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.05 : 0.12)
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
@@ -240,7 +238,7 @@ Scope {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
anchors {
|
||||
top: contentColumn.bottom
|
||||
topMargin: 10
|
||||
@@ -249,19 +247,22 @@ Scope {
|
||||
spacing: 10
|
||||
|
||||
Loader {
|
||||
active: SessionWarnings.packageManagerRunning
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: SessionWarnings.downloadRunning
|
||||
visible: active
|
||||
sourceComponent: DescriptionLabel {
|
||||
text: Translation.tr("Your package manager is running")
|
||||
text: Translation.tr("There might be a download in progress. Check your Downloads folder.")
|
||||
textColor: Appearance.m3colors.m3onErrorContainer
|
||||
color: Appearance.m3colors.m3errorContainer
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: SessionWarnings.downloadRunning
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
active: SessionWarnings.packageManagerRunning
|
||||
visible: active
|
||||
sourceComponent: DescriptionLabel {
|
||||
text: Translation.tr("There might be a download in progress")
|
||||
text: Translation.tr("Your package manager is running")
|
||||
textColor: Appearance.m3colors.m3onErrorContainer
|
||||
color: Appearance.m3colors.m3errorContainer
|
||||
}
|
||||
|
||||
@@ -314,7 +314,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
implicitWidth: statusRowLayout.implicitWidth + 10 * 2
|
||||
implicitHeight: Math.max(statusRowLayout.implicitHeight, 38)
|
||||
radius: Appearance.rounding.normal - root.padding
|
||||
color: Appearance.colors.colLayer2
|
||||
color: messageListView.atYBeginning ? Appearance.colors.colLayer2 : Appearance.colors.colLayer2Base
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
RowLayout {
|
||||
id: statusRowLayout
|
||||
anchors.centerIn: parent
|
||||
|
||||
@@ -67,6 +67,7 @@ Scope { // Scope
|
||||
|
||||
onDetachChanged: {
|
||||
if (root.detach) {
|
||||
GlobalFocusGrab.removeDismissable(sidebarLoader.item) // Remove sidebar from the focus grab system
|
||||
sidebarContent.parent = null; // Detach content from sidebar
|
||||
sidebarLoader.active = false; // Unload sidebar
|
||||
detachedSidebarLoader.active = true; // Load detached window
|
||||
@@ -84,11 +85,11 @@ Scope { // Scope
|
||||
active: true
|
||||
|
||||
sourceComponent: PanelWindow { // Window
|
||||
id: sidebarRoot
|
||||
id: panelWindow
|
||||
visible: GlobalStates.sidebarLeftOpen
|
||||
|
||||
property bool extend: false
|
||||
property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
|
||||
property real sidebarWidth: panelWindow.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth
|
||||
property var contentParent: sidebarLeftBackground
|
||||
|
||||
function hide() {
|
||||
@@ -113,15 +114,17 @@ Scope { // Scope
|
||||
item: sidebarLeftBackground
|
||||
}
|
||||
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [ sidebarRoot ]
|
||||
active: sidebarRoot.visible && !root.pin
|
||||
onActiveChanged: { // Focus the selected tab
|
||||
if (active) sidebarLeftBackground.children[0].focusActiveItem()
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
GlobalFocusGrab.addDismissable(panelWindow);
|
||||
} else {
|
||||
GlobalFocusGrab.removeDismissable(panelWindow);
|
||||
}
|
||||
onCleared: () => {
|
||||
if (!active) sidebarRoot.hide()
|
||||
}
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
panelWindow.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +139,7 @@ Scope { // Scope
|
||||
anchors.left: parent.left
|
||||
anchors.topMargin: Appearance.sizes.hyprlandGapsOut
|
||||
anchors.leftMargin: Appearance.sizes.hyprlandGapsOut
|
||||
width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||
width: panelWindow.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin
|
||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
color: Appearance.colors.colLayer0
|
||||
border.width: 1
|
||||
@@ -149,11 +152,11 @@ Scope { // Scope
|
||||
|
||||
Keys.onPressed: (event) => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sidebarRoot.hide();
|
||||
panelWindow.hide();
|
||||
}
|
||||
if (event.modifiers === Qt.ControlModifier) {
|
||||
if (event.key === Qt.Key_O) {
|
||||
sidebarRoot.extend = !sidebarRoot.extend;
|
||||
panelWindow.extend = !panelWindow.extend;
|
||||
} else if (event.key === Qt.Key_D) {
|
||||
root.toggleDetach();
|
||||
} else if (event.key === Qt.Key_P) {
|
||||
|
||||
@@ -48,6 +48,7 @@ Item {
|
||||
spacing: sidebarPadding
|
||||
|
||||
Toolbar {
|
||||
visible: tabButtonList.length > 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
enableShadow: false
|
||||
ToolbarTabBar {
|
||||
@@ -83,9 +84,10 @@ Item {
|
||||
}
|
||||
|
||||
contentChildren: [
|
||||
...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []),
|
||||
...(root.aiChatEnabled ? [aiChat.createObject()] : []),
|
||||
...(root.translatorEnabled ? [translator.createObject()] : []),
|
||||
...(root.animeEnabled ? [anime.createObject()] : [])
|
||||
...((root.tabButtonList.length === 0 || (!root.aiChatEnabled && !root.translatorEnabled && root.animeCloset)) ? [placeholder.createObject()] : []),
|
||||
...(root.animeEnabled ? [anime.createObject()] : []),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -102,6 +104,15 @@ Item {
|
||||
id: anime
|
||||
Anime {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: placeholder
|
||||
Item {
|
||||
StyledText {
|
||||
anchors.centerIn: parent
|
||||
text: root.animeCloset ? Translation.tr("Nothing") : Translation.tr("Enjoy your empty sidebar...")
|
||||
color: Appearance.colors.colSubtext
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ Button {
|
||||
opacity: root.showActions ? 1 : 0
|
||||
visible: opacity > 0
|
||||
radius: Appearance.rounding.small
|
||||
color: Appearance.colors.colSurfaceContainer
|
||||
color: Appearance.m3colors.m3surfaceContainer
|
||||
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
|
||||
implicitWidth: contextMenuColumnLayout.implicitWidth
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ Rectangle {
|
||||
|
||||
anchors.fill: parent
|
||||
// implicitHeight: tabStack.implicitHeight
|
||||
spacing: 10
|
||||
spacing: 20
|
||||
|
||||
// Navigation rail
|
||||
Item {
|
||||
@@ -146,7 +146,7 @@ Rectangle {
|
||||
Layout.fillWidth: false
|
||||
Layout.leftMargin: 10
|
||||
Layout.topMargin: 10
|
||||
width: tabBar.width
|
||||
implicitWidth: tabBar.implicitWidth
|
||||
// Navigation rail buttons
|
||||
NavigationRailTabArray {
|
||||
id: tabBar
|
||||
|
||||
@@ -12,18 +12,17 @@ Scope {
|
||||
property int sidebarWidth: Appearance.sizes.sidebarWidth
|
||||
|
||||
PanelWindow {
|
||||
id: sidebarRoot
|
||||
id: panelWindow
|
||||
visible: GlobalStates.sidebarRightOpen
|
||||
|
||||
function hide() {
|
||||
GlobalStates.sidebarRightOpen = false
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
}
|
||||
|
||||
exclusiveZone: 0
|
||||
implicitWidth: sidebarWidth
|
||||
WlrLayershell.namespace: "quickshell:sidebarRight"
|
||||
// Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab
|
||||
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
||||
WlrLayershell.keyboardFocus: GlobalStates.sidebarRightOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
@@ -32,12 +31,17 @@ Scope {
|
||||
bottom: true
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: grab
|
||||
windows: [ sidebarRoot ]
|
||||
active: GlobalStates.sidebarRightOpen
|
||||
onCleared: () => {
|
||||
if (!active) sidebarRoot.hide()
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
GlobalFocusGrab.addDismissable(panelWindow);
|
||||
} else {
|
||||
GlobalFocusGrab.removeDismissable(panelWindow);
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
panelWindow.hide();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,16 +57,14 @@ Scope {
|
||||
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
|
||||
|
||||
focus: GlobalStates.sidebarRightOpen
|
||||
Keys.onPressed: (event) => {
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
sidebarRoot.hide();
|
||||
panelWindow.hide();
|
||||
}
|
||||
}
|
||||
|
||||
sourceComponent: SidebarRightContent {}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
@@ -105,5 +107,4 @@ Scope {
|
||||
GlobalStates.sidebarRightOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -47,6 +47,7 @@ DialogListItem {
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
text: root.device?.name || Translation.tr("Unknown device")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
StyledText {
|
||||
visible: (root.device?.connected || root.device?.paired) ?? false
|
||||
|
||||
@@ -139,7 +139,7 @@ Item {
|
||||
anchors.margins: root.dialogMargins
|
||||
implicitHeight: dialogColumnLayout.implicitHeight
|
||||
|
||||
color: Appearance.colors.colSurfaceContainerHigh
|
||||
color: Appearance.m3colors.m3surfaceContainerHigh
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
function addTask() {
|
||||
|
||||
@@ -40,6 +40,7 @@ DialogListItem {
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
elide: Text.ElideRight
|
||||
text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
MaterialSymbol {
|
||||
visible: (root.wifiNetwork?.isSecure || root.wifiNetwork?.active) ?? false
|
||||
|
||||
@@ -66,6 +66,7 @@ Scope {
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
// Positioning
|
||||
anchors {
|
||||
left: !Config.options.bar.bottom
|
||||
right: Config.options.bar.bottom
|
||||
@@ -73,6 +74,14 @@ Scope {
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// Include in focus grab
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addPersistent(barRoot);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removePersistent(barRoot);
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: hoverRegion
|
||||
hoverEnabled: true
|
||||
|
||||
@@ -73,7 +73,7 @@ MouseArea {
|
||||
target: Wallpapers
|
||||
function onThumbnailGenerated(directory) {
|
||||
if (thumbnailImage.status !== Image.Error) return;
|
||||
if (FileUtils.parentDirectory(thumbnailImage.sourcePath) !== directory) return;
|
||||
if (FileUtils.parentDirectory(thumbnailImage.sourcePath) !== FileUtils.trimFileProtocol(directory)) return;
|
||||
thumbnailImage.source = "";
|
||||
thumbnailImage.source = thumbnailImage.thumbnailPath;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ Scope {
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.namespace: "quickshell:wallpaperSelector"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors.top: true
|
||||
@@ -39,12 +40,16 @@ Scope {
|
||||
implicitHeight: Appearance.sizes.wallpaperSelectorHeight
|
||||
implicitWidth: Appearance.sizes.wallpaperSelectorWidth
|
||||
|
||||
HyprlandFocusGrab { // Click outside to close
|
||||
id: grab
|
||||
windows: [ panelWindow ]
|
||||
active: wallpaperSelectorLoader.active
|
||||
onCleared: () => {
|
||||
if (!active) GlobalStates.wallpaperSelectorOpen = false;
|
||||
Component.onCompleted: {
|
||||
GlobalFocusGrab.addDismissable(panelWindow);
|
||||
}
|
||||
Component.onDestruction: {
|
||||
GlobalFocusGrab.removeDismissable(panelWindow);
|
||||
}
|
||||
Connections {
|
||||
target: GlobalFocusGrab
|
||||
function onDismissed() {
|
||||
GlobalStates.wallpaperSelectorOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +53,9 @@ ContentPage {
|
||||
}
|
||||
|
||||
ContentSection {
|
||||
id: settingsClock
|
||||
icon: "clock_loader_40"
|
||||
title: Translation.tr("Widget: Clock")
|
||||
id: settingsClock
|
||||
|
||||
function stylePresent(styleName) {
|
||||
if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) {
|
||||
@@ -120,61 +120,161 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
visible: !Config.options.background.widgets.clock.showOnlyWhenLocked
|
||||
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"
|
||||
ConfigRow {
|
||||
ContentSubsection {
|
||||
visible: !Config.options.background.widgets.clock.showOnlyWhenLocked
|
||||
title: Translation.tr("Clock style")
|
||||
Layout.fillWidth: true
|
||||
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 {
|
||||
title: Translation.tr("Clock style (locked)")
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.background.widgets.clock.styleLocked
|
||||
onSelected: newValue => {
|
||||
Config.options.background.widgets.clock.styleLocked = newValue;
|
||||
}
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("Digital"),
|
||||
icon: "timer_10",
|
||||
value: "digital"
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Cookie"),
|
||||
icon: "cookie",
|
||||
value: "cookie"
|
||||
ContentSubsection {
|
||||
title: Translation.tr("Clock style (locked)")
|
||||
Layout.fillWidth: false
|
||||
ConfigSelectionArray {
|
||||
currentValue: Config.options.background.widgets.clock.styleLocked
|
||||
onSelected: newValue => {
|
||||
Config.options.background.widgets.clock.styleLocked = newValue;
|
||||
}
|
||||
]
|
||||
options: [
|
||||
{
|
||||
displayName: Translation.tr("Digital"),
|
||||
icon: "timer_10",
|
||||
value: "digital"
|
||||
},
|
||||
{
|
||||
displayName: Translation.tr("Cookie"),
|
||||
icon: "cookie",
|
||||
value: "cookie"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContentSubsection {
|
||||
visible: settingsClock.digitalPresent
|
||||
title: Translation.tr("Digital clock settings")
|
||||
tooltip: Translation.tr("Font width and roundness settings are only available for some fonts like Google Sans Flex")
|
||||
|
||||
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;
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
ConfigSwitch {
|
||||
buttonIcon: "vertical_distribute"
|
||||
text: Translation.tr("Vertical")
|
||||
checked: Config.options.background.widgets.clock.digital.vertical
|
||||
onCheckedChanged: {
|
||||
Config.options.background.widgets.clock.digital.vertical = checked;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigRow {
|
||||
uniform: true
|
||||
|
||||
ConfigSwitch {
|
||||
buttonIcon: "date_range"
|
||||
text: Translation.tr("Show date")
|
||||
checked: Config.options.background.widgets.clock.digital.showDate
|
||||
onCheckedChanged: {
|
||||
Config.options.background.widgets.clock.digital.showDate = checked;
|
||||
}
|
||||
}
|
||||
ConfigSwitch {
|
||||
buttonIcon: "activity_zone"
|
||||
text: Translation.tr("Use adaptive alignment")
|
||||
checked: Config.options.background.widgets.clock.digital.adaptiveAlignment
|
||||
onCheckedChanged: {
|
||||
Config.options.background.widgets.clock.digital.adaptiveAlignment = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("Aligns the date and quote to left, center or right depending on its position on the screen.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTextArea {
|
||||
Layout.fillWidth: true
|
||||
placeholderText: Translation.tr("Font family")
|
||||
text: Config.options.background.widgets.clock.digital.font.family
|
||||
wrapMode: TextEdit.Wrap
|
||||
onTextChanged: {
|
||||
Config.options.background.widgets.clock.digital.font.family = text;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
text: Translation.tr("Font weight")
|
||||
value: Config.options.background.widgets.clock.digital.font.weight
|
||||
usePercentTooltip: false
|
||||
buttonIcon: "format_bold"
|
||||
from: 1
|
||||
to: 1000
|
||||
stopIndicatorValues: [350]
|
||||
onValueChanged: {
|
||||
Config.options.background.widgets.clock.digital.font.weight = value;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
text: Translation.tr("Font size")
|
||||
value: Config.options.background.widgets.clock.digital.font.size
|
||||
usePercentTooltip: false
|
||||
buttonIcon: "format_size"
|
||||
from: 50
|
||||
to: 700
|
||||
stopIndicatorValues: [90]
|
||||
onValueChanged: {
|
||||
Config.options.background.widgets.clock.digital.font.size = value;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigSlider {
|
||||
text: Translation.tr("Font width")
|
||||
value: Config.options.background.widgets.clock.digital.font.width
|
||||
usePercentTooltip: false
|
||||
buttonIcon: "fit_width"
|
||||
from: 25
|
||||
to: 125
|
||||
stopIndicatorValues: [100]
|
||||
onValueChanged: {
|
||||
Config.options.background.widgets.clock.digital.font.width = value;
|
||||
}
|
||||
}
|
||||
ConfigSlider {
|
||||
text: Translation.tr("Font roundness")
|
||||
value: Config.options.background.widgets.clock.digital.font.roundness
|
||||
usePercentTooltip: false
|
||||
buttonIcon: "line_curve"
|
||||
from: 0
|
||||
to: 100
|
||||
onValueChanged: {
|
||||
Config.options.background.widgets.clock.digital.font.roundness = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ ContentPage {
|
||||
key: "Ctrl"
|
||||
}
|
||||
KeyboardKey {
|
||||
key: ""
|
||||
key: Config.options.cheatsheet.superKey ?? ""
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
@@ -225,9 +225,6 @@ ContentPage {
|
||||
onCheckedChanged: {
|
||||
Config.options.appearance.transparency.enable = checked;
|
||||
}
|
||||
StyledToolTip {
|
||||
text: Translation.tr("Might look ass. Unsupported.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -203,6 +203,32 @@ ContentPage {
|
||||
}
|
||||
}
|
||||
|
||||
// There's no update indicator in ii for now so we shouldn't show this yet
|
||||
// ContentSection {
|
||||
// icon: "deployed_code_update"
|
||||
// title: Translation.tr("System updates (Arch only)")
|
||||
|
||||
// ConfigSwitch {
|
||||
// text: Translation.tr("Enable update checks")
|
||||
// checked: Config.options.updates.enableCheck
|
||||
// onCheckedChanged: {
|
||||
// Config.options.updates.enableCheck = checked;
|
||||
// }
|
||||
// }
|
||||
|
||||
// ConfigSpinBox {
|
||||
// icon: "av_timer"
|
||||
// text: Translation.tr("Check interval (mins)")
|
||||
// value: Config.options.updates.checkInterval
|
||||
// from: 60
|
||||
// to: 1440
|
||||
// stepSize: 60
|
||||
// onValueChanged: {
|
||||
// Config.options.updates.checkInterval = value;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
ContentSection {
|
||||
icon: "weather_mix"
|
||||
title: Translation.tr("Weather")
|
||||
|
||||
+1
@@ -41,6 +41,7 @@ ExpandableChoiceButton {
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: root.device?.name || Translation.tr("Unknown device")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
WText { // Status
|
||||
id: statusText
|
||||
|
||||
@@ -61,6 +61,7 @@ ExpandableChoiceButton {
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
WText { // Status
|
||||
id: statusText
|
||||
|
||||
@@ -13,7 +13,7 @@ Scope {
|
||||
|
||||
LazyLoader {
|
||||
id: barLoader
|
||||
active: GlobalStates.barOpen && !GlobalStates.screenLocked
|
||||
active: GlobalStates.barOpen
|
||||
component: Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: PanelWindow { // Bar window
|
||||
|
||||
@@ -12,7 +12,20 @@ AppButton {
|
||||
|
||||
required property var appEntry
|
||||
readonly property bool isSeparator: appEntry.appId === "SEPARATOR"
|
||||
readonly property var desktopEntry: DesktopEntries.heuristicLookup(appEntry.appId)
|
||||
property var desktopEntry: DesktopEntries.heuristicLookup(appEntry.appId)
|
||||
|
||||
Timer {
|
||||
// Retry looking up the desktop entry if it failed (e.g. database not loaded yet)
|
||||
property int retryCount: 5
|
||||
interval: 1000
|
||||
running: !root.isSeparator && root.desktopEntry === null && retryCount > 0
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
retryCount--;
|
||||
root.desktopEntry = DesktopEntries.heuristicLookup(root.appEntry.appId);
|
||||
}
|
||||
}
|
||||
|
||||
property bool active: root.appEntry.toplevels.some(t => t.activated)
|
||||
property bool hasWindows: appEntry.toplevels.length > 0
|
||||
|
||||
|
||||
@@ -36,15 +36,34 @@ LockScreen {
|
||||
Image {
|
||||
id: bg
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready) {
|
||||
print("Lock wallpaper loaded");
|
||||
print(lockSurfaceItem.height);
|
||||
y = -lockSurfaceItem.height;
|
||||
openAnim.restart();
|
||||
}
|
||||
}
|
||||
sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height)
|
||||
source: Config.options.background.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: bg
|
||||
property: "y"
|
||||
to: 0
|
||||
duration: 350
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
GaussianBlur {
|
||||
z: 1
|
||||
anchors.fill: parent
|
||||
anchors.fill: bg
|
||||
source: bg
|
||||
radius: 100
|
||||
samples: radius * 2 + 1
|
||||
@@ -67,7 +86,7 @@ LockScreen {
|
||||
Interactables {
|
||||
id: interactables
|
||||
z: 2
|
||||
anchors.fill: parent
|
||||
anchors.fill: bg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +102,31 @@ LockScreen {
|
||||
// }
|
||||
|
||||
function switchToFocusedView() {
|
||||
root.passwordView = true;
|
||||
switchToPasswordViewAnim.restart();
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: switchToPasswordViewAnim
|
||||
PropertyAnimation {
|
||||
target: unfocusedContent
|
||||
property: "y"
|
||||
from: 0
|
||||
to: -height * 1.1
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.passwordView = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: unfocusedContent
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: !root.passwordView
|
||||
ClockTextGroup {
|
||||
anchors {
|
||||
|
||||
@@ -17,8 +17,9 @@ Singleton {
|
||||
property string iconsPath: `${Directories.assetsPath}/icons/fluent`
|
||||
property bool dark: Appearance.m3colors.darkmode
|
||||
|
||||
property real backgroundTransparency: 0.16
|
||||
property real panelBackgroundTransparency: 0.14
|
||||
readonly property bool transparencyEnabled: Config.options.appearance.transparency.enable
|
||||
property real backgroundTransparency: transparencyEnabled ? 0.16 : 0
|
||||
property real panelBackgroundTransparency: transparencyEnabled ? 0.14 : 0
|
||||
property real panelLayerTransparency: root.dark ? 0.9 : 0.7
|
||||
property real contentTransparency: root.dark ? 0.87 : 0.5
|
||||
function applyBackgroundTransparency(col) {
|
||||
@@ -27,23 +28,22 @@ Singleton {
|
||||
function applyContentTransparency(col) {
|
||||
return ColorUtils.applyAlpha(col, 1 - root.contentTransparency)
|
||||
}
|
||||
lightColors: QtObject { // TODO: figure out transparency
|
||||
lightColors: QtObject {
|
||||
id: lightColors
|
||||
property color bgPanelFooter: "#EEEEEE"
|
||||
property color bgPanelBody: "#F2F2F2"
|
||||
property color bgPanelSeparator: "#E0E0E0"
|
||||
property color bg0: "#EEEEEE"
|
||||
property color bg0Border: '#adadad'
|
||||
property color bg1: "#F7F7F7"
|
||||
property color bg0Border: '#BEBEBE'
|
||||
property color bg1Base: "#F7F7F7"
|
||||
property color bg1: "#F7F7F7"
|
||||
property color bg1Hover: "#F7F7F7"
|
||||
property color bg1Active: '#EFEFEF'
|
||||
property color bg1Border: '#d7d7d7'
|
||||
property color bg1Border: '#E9E9E9'
|
||||
property color bg2: "#FBFBFB"
|
||||
property color bg2Base: "#FBFBFB"
|
||||
property color bg2Hover: '#ffffff'
|
||||
property color bg2Active: '#eeeeee'
|
||||
property color bg2Border: '#cdcdcd'
|
||||
property color bg2Border: '#E0E0E0'
|
||||
property color subfg: "#5C5C5C"
|
||||
property color fg: "#000000"
|
||||
property color fg1: "#626262"
|
||||
@@ -58,21 +58,20 @@ Singleton {
|
||||
}
|
||||
darkColors: QtObject {
|
||||
id: darkColors
|
||||
property color bgPanelFooter: "#1C1C1C"
|
||||
property color bgPanelBody: '#616161'
|
||||
property color bgPanelBody: '#242424'
|
||||
property color bgPanelSeparator: "#191919"
|
||||
property color bg0: "#1C1C1C"
|
||||
property color bg0Border: "#404040"
|
||||
property color bg1Base: "#2C2C2C"
|
||||
property color bg1: '#9f9f9f'
|
||||
property color bg1Hover: "#b3b3b3"
|
||||
property color bg1Active: '#727272'
|
||||
property color bg1Base: '#2C2C2C'
|
||||
property color bg1: '#2C2C2C'
|
||||
property color bg1Hover: "#292929"
|
||||
property color bg1Active: '#252525'
|
||||
property color bg1Border: '#bebebe'
|
||||
property color bg2Base: "#313131"
|
||||
property color bg2: '#8a8a8a'
|
||||
property color bg2Hover: '#b1b1b1'
|
||||
property color bg2Active: '#919191'
|
||||
property color bg2Border: '#bdbdbd'
|
||||
property color bg2: '#313131'
|
||||
property color bg2Hover: '#363636'
|
||||
property color bg2Active: '#2B2B2B'
|
||||
property color bg2Border: '#404040'
|
||||
property color subfg: "#CED1D7"
|
||||
property color fg: "#FFFFFF"
|
||||
property color fg1: "#D1D1D1"
|
||||
@@ -87,38 +86,47 @@ Singleton {
|
||||
}
|
||||
colors: QtObject {
|
||||
id: colors
|
||||
// Special
|
||||
property color shadow: ColorUtils.transparentize('#161616', 0.62)
|
||||
property color ambientShadow: ColorUtils.transparentize("#000000", 0.75)
|
||||
property color bgPanelFooterBase: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelBackgroundTransparency)
|
||||
property color bgPanelFooter: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelLayerTransparency)
|
||||
property color bgPanelBody: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody, root.panelLayerTransparency)
|
||||
property color bgPanelSeparator: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, root.backgroundTransparency)
|
||||
property color bg0Opaque: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bg0: ColorUtils.transparentize(bg0Opaque, root.backgroundTransparency)
|
||||
property color bgPanelFooterBase: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bgPanelFooterBackground: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency)
|
||||
property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBackground, root.panelLayerTransparency)
|
||||
property color bgPanelBodyBase: root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody
|
||||
property color bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBackground,bgPanelBodyBase, 1 - root.panelLayerTransparency)
|
||||
property color bgPanelSeparator: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, 1 - root.panelBackgroundTransparency)
|
||||
// Layer 0
|
||||
property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bg0: ColorUtils.transparentize(bg0Base, root.backgroundTransparency)
|
||||
property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency)
|
||||
property color bg1Base: root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base
|
||||
property color bg1: ColorUtils.transparentize(root.dark ? root.darkColors.bg1 : root.lightColors.bg1, root.contentTransparency)
|
||||
property color bg1Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, root.contentTransparency)
|
||||
property color bg1Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, root.contentTransparency)
|
||||
property color bg1Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, root.contentTransparency)
|
||||
property color bg2Base: root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base
|
||||
property color bg2: ColorUtils.transparentize(root.dark ? root.darkColors.bg2 : root.lightColors.bg2, root.contentTransparency)
|
||||
property color bg2Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, root.contentTransparency)
|
||||
property color bg2Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, root.contentTransparency)
|
||||
property color bg2Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, root.contentTransparency)
|
||||
// Layer 1
|
||||
property color bg1Base: root.dark ? root.darkColors.bg1 : root.lightColors.bg1
|
||||
property color bg1: ColorUtils.solveOverlayColor(bg0Base, bg1Base, 1 - root.contentTransparency)
|
||||
property color bg1Hover: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, 1 - root.contentTransparency)
|
||||
property color bg1Active: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, 1 - root.contentTransparency)
|
||||
property color bg1Border: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, 1 - root.contentTransparency)
|
||||
// Layer 2
|
||||
property color bg2Base: root.dark ? root.darkColors.bg2 : root.lightColors.bg2
|
||||
property color bg2: ColorUtils.solveOverlayColor(bgPanelBodyBase, bg2Base, 1 - root.contentTransparency)
|
||||
property color bg2Hover: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, 1 - root.contentTransparency)
|
||||
property color bg2Active: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, 1 - root.contentTransparency)
|
||||
property color bg2Border: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, 1 - root.contentTransparency)
|
||||
// Foreground / Text
|
||||
property color subfg: root.dark ? root.darkColors.subfg : root.lightColors.subfg
|
||||
property color fg: root.dark ? root.darkColors.fg : root.lightColors.fg
|
||||
property color fg1: root.dark ? root.darkColors.fg1 : root.lightColors.fg1
|
||||
property color inactiveIcon: root.dark ? root.darkColors.inactiveIcon : root.lightColors.inactiveIcon
|
||||
property color link: root.dark ? root.darkColors.link : root.lightColors.link
|
||||
// Controls
|
||||
property color controlBgInactive: root.dark ? root.darkColors.controlBgInactive : root.lightColors.controlBgInactive
|
||||
property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg
|
||||
property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover
|
||||
property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg
|
||||
property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg
|
||||
property color link: root.dark ? root.darkColors.link : root.lightColors.link
|
||||
property color danger: "#C42B1C"
|
||||
property color dangerActive: "#B62D1F"
|
||||
property color warning: "#FF9900"
|
||||
// Accent
|
||||
property color accent: Appearance.colors.colPrimary
|
||||
property color accentHover: Appearance.colors.colPrimaryHover
|
||||
property color accentActive: Appearance.colors.colPrimaryActive
|
||||
|
||||
@@ -39,9 +39,10 @@ Button {
|
||||
}
|
||||
}
|
||||
property color fgColor: {
|
||||
if (!root.enabled) return root.colForegroundDisabled
|
||||
if (root.checked) return root.colForegroundToggled
|
||||
if (root.enabled) return root.colForeground
|
||||
return root.colForegroundDisabled
|
||||
return root.colForeground
|
||||
}
|
||||
property alias horizontalAlignment: buttonText.horizontalAlignment
|
||||
font {
|
||||
|
||||
@@ -76,8 +76,9 @@ Menu {
|
||||
contentItem: Item {
|
||||
implicitWidth: menuListView.implicitWidth
|
||||
implicitHeight: menuListView.implicitHeight
|
||||
ListView {
|
||||
WListView {
|
||||
id: menuListView
|
||||
interactive: contentHeight > height
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@@ -87,6 +88,7 @@ Menu {
|
||||
topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins
|
||||
bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin
|
||||
}
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
implicitWidth: Array.from({
|
||||
length: count
|
||||
|
||||
@@ -6,6 +6,7 @@ import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
MenuItem {
|
||||
@@ -14,11 +15,11 @@ MenuItem {
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBackgroundToggled: Looks.colors.accent
|
||||
property color colBackgroundToggledHover: Looks.colors.accentHover
|
||||
property color colBackgroundToggledActive: Looks.colors.accentActive
|
||||
property color colBackgroundToggled: Looks.colors.bg2Hover
|
||||
property color colBackgroundToggledHover: Looks.colors.bg2Active
|
||||
property color colBackgroundToggledActive: Looks.colors.bg2Hover
|
||||
property color colForeground: Looks.colors.fg
|
||||
property color colForegroundToggled: Looks.colors.accentFg
|
||||
property color colForegroundToggled: Looks.colors.fg
|
||||
property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4)
|
||||
property color color: {
|
||||
if (!root.enabled)
|
||||
@@ -70,27 +71,57 @@ MenuItem {
|
||||
implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset
|
||||
implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: contentLayout
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 20
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: root.fgColor
|
||||
visible: root.icon.name !== "";
|
||||
icon: root.icon.name
|
||||
contentItem: Item {
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 20
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: root.fgColor
|
||||
visible: root.icon.name !== ""
|
||||
icon: root.icon.name
|
||||
}
|
||||
WText {
|
||||
id: buttonText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
WText {
|
||||
id: buttonText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: root.fgColor
|
||||
|
||||
WFadeLoader {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: -root.leftPadding + width
|
||||
}
|
||||
shown: root.checked
|
||||
sourceComponent: Rectangle {
|
||||
implicitWidth: 3
|
||||
implicitHeight: 3
|
||||
radius: width / 2
|
||||
color: Looks.colors.accent
|
||||
property bool forceZeroHeight: true
|
||||
height: forceZeroHeight ? 0 : Math.max(root.down ? 10 : 16, root.background.height - 18 * 2)
|
||||
Component.onCompleted: {
|
||||
forceZeroHeight = false;
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ Item {
|
||||
id: root
|
||||
property Item contentItem
|
||||
property real radius: Looks.radius.large
|
||||
property alias color: contentRect.color
|
||||
property alias border: borderRect
|
||||
property alias borderColor: borderRect.border.color
|
||||
property alias borderWidth: borderRect.border.width
|
||||
@@ -42,7 +43,7 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
z: 0
|
||||
|
||||
color: Looks.colors.bgPanelFooterBase
|
||||
color: Looks.colors.bgPanelFooterBackground
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
layer.enabled: true
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real padding: 9
|
||||
property alias colBackground: background.color
|
||||
property alias spacing: toolbarLayout.spacing
|
||||
property alias radius: background.radius
|
||||
default property alias data: toolbarLayout.data
|
||||
|
||||
implicitWidth: background.implicitWidth
|
||||
implicitHeight: background.implicitHeight
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
implicitHeight: 50
|
||||
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
|
||||
radius: Looks.radius.large
|
||||
color: Looks.colors.bg0Base
|
||||
|
||||
border.width: 1
|
||||
border.color: Looks.colors.bg1Border
|
||||
|
||||
RowLayout {
|
||||
id: toolbarLayout
|
||||
spacing: 4
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: root.padding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
WButton {
|
||||
implicitHeight: 32
|
||||
radius: Looks.radius.medium
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
WToolbarButton {
|
||||
id: root
|
||||
implicitWidth: height
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: root.icon.name
|
||||
implicitSize: 18
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
|
||||
TabButton {
|
||||
id: root
|
||||
|
||||
implicitWidth: 38
|
||||
implicitHeight: 32
|
||||
padding: 0
|
||||
|
||||
background: null
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: root.icon.name
|
||||
color: root.icon.color
|
||||
implicitSize: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
Rectangle {
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 4
|
||||
implicitHeight: 24
|
||||
implicitWidth: 1
|
||||
color: Looks.colors.bg0Border
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
TabBar {
|
||||
id: root
|
||||
implicitHeight: 32
|
||||
|
||||
background: Rectangle {
|
||||
radius: Looks.radius.medium
|
||||
color: Looks.colors.bgPanelFooter
|
||||
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, 0.7)
|
||||
border.width: 1
|
||||
|
||||
// Indicator
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
leftMargin: root.currentIndex * (root.width / root.count)
|
||||
Behavior on leftMargin {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
}
|
||||
radius: Looks.radius.medium
|
||||
color: Looks.colors.bg2Base
|
||||
border.color: Looks.colors.bg0Border
|
||||
border.width: 1
|
||||
width: root.width / root.count
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 1
|
||||
}
|
||||
implicitWidth: pressDetector.containsPress ? 16 : 12
|
||||
implicitHeight: 3
|
||||
radius: height / 2
|
||||
color: Looks.colors.accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pressDetector
|
||||
z: 9999
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,7 @@ BodyRectangle {
|
||||
id: dayButton
|
||||
required property var model
|
||||
checked: model.today
|
||||
enabled: hovered || calendarView.scrolling || checked || model.month === calendarView.focusedMonth
|
||||
enabled: hovered || checked || model.month === calendarView.focusedMonth
|
||||
implicitWidth: calendarView.buttonSize
|
||||
implicitHeight: calendarView.buttonSize
|
||||
radius: height / 2
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BodyRectangle {
|
||||
FooterRectangle {
|
||||
id: root
|
||||
anchors.fill: parent
|
||||
implicitHeight: 230
|
||||
|
||||
+8
-5
@@ -15,6 +15,9 @@ MouseArea {
|
||||
required property var notification
|
||||
property bool expanded: notification.actions.length > 0
|
||||
property string groupExpandControlMessage: ""
|
||||
|
||||
readonly property bool isPopup: notification?.popup ?? false
|
||||
|
||||
signal groupExpandToggle
|
||||
hoverEnabled: true
|
||||
|
||||
@@ -56,13 +59,13 @@ MouseArea {
|
||||
Rectangle {
|
||||
id: contentItem
|
||||
width: parent.width
|
||||
color: Looks.colors.bgPanelBody
|
||||
radius: Looks.radius.medium
|
||||
color: root.isPopup ? Looks.colors.bg0 : Looks.colors.bgPanelBody
|
||||
radius: root.isPopup ? Looks.radius.large : Looks.radius.medium
|
||||
property real padding: 12
|
||||
implicitHeight: notificationContent.implicitHeight + padding * 2
|
||||
implicitWidth: notificationContent.implicitWidth + padding * 2
|
||||
border.width: 1
|
||||
border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
|
||||
border.color: root.isPopup ? Looks.colors.bg2Border : Looks.colors.bgPanelSeparator
|
||||
|
||||
Behavior on x {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
@@ -157,9 +160,9 @@ MouseArea {
|
||||
|
||||
NotificationHeaderButton {
|
||||
Layout.rightMargin: 4
|
||||
opacity: root.containsMouse ? 1 : 0
|
||||
opacity: (root.containsMouse || root.isPopup) ? 1 : 0
|
||||
icon.name: "dismiss"
|
||||
implicitSize: 12
|
||||
implicitSize: 14
|
||||
onClicked: root.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.notificationCenter
|
||||
|
||||
Scope {
|
||||
id: notificationPopup
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
visible: (Notifications.popupList.length > 0) && !GlobalStates.screenLocked
|
||||
screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null
|
||||
|
||||
WlrLayershell.namespace: "quickshell:notificationPopup"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
exclusiveZone: 0
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
right: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
mask: Region {
|
||||
item: listview.contentItem
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
implicitWidth: listview.implicitWidth
|
||||
|
||||
WListView {
|
||||
id: listview
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
left: parent.left
|
||||
}
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
topMargin: 16
|
||||
bottomMargin: 16
|
||||
|
||||
height: Math.min(contentItem.height + topMargin + bottomMargin, parent.height)
|
||||
width: parent.width - Appearance.sizes.elevationMargin * 2
|
||||
|
||||
implicitWidth: 396
|
||||
spacing:12
|
||||
|
||||
model: ScriptModel {
|
||||
values: Notifications.popupList
|
||||
}
|
||||
delegate: WSingleNotification {
|
||||
required property var modelData
|
||||
notification: modelData
|
||||
width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,7 @@ Rectangle {
|
||||
}
|
||||
BodyRectangle {
|
||||
implicitHeight: 80
|
||||
color: Looks.colors.bgPanelFooterBase
|
||||
color: Looks.colors.bgPanelFooterBackground
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 24
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property int regionX
|
||||
required property int regionY
|
||||
required property int regionWidth
|
||||
required property int regionHeight
|
||||
|
||||
property bool dashed: true
|
||||
property color borderColor: "#ffffff"
|
||||
property color overlayColor: ColorUtils.transparentize("#000000", 1)
|
||||
Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4)
|
||||
Behavior on overlayColor {
|
||||
ColorAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay to darken screen
|
||||
// Base dark overlay around region
|
||||
Rectangle {
|
||||
id: darkenOverlay
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: root.regionX - darkenOverlay.border.width
|
||||
topMargin: root.regionY - darkenOverlay.border.width
|
||||
}
|
||||
width: root.regionWidth + darkenOverlay.border.width * 2
|
||||
height: root.regionHeight + darkenOverlay.border.width * 2
|
||||
color: "transparent"
|
||||
border.color: root.overlayColor
|
||||
border.width: Math.max(root.width, root.height)
|
||||
}
|
||||
|
||||
// Selection border
|
||||
DashedBorder {
|
||||
id: border
|
||||
z: 2
|
||||
visible: root.regionWidth > 0 && root.regionHeight > 0
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
leftMargin: Math.round(root.regionX - borderWidth)
|
||||
topMargin: Math.round(root.regionY - borderWidth)
|
||||
}
|
||||
width: Math.round(root.regionWidth + borderWidth * 2)
|
||||
height: Math.round(root.regionHeight + borderWidth * 2)
|
||||
color: root.borderColor
|
||||
dashLength: 4
|
||||
gapLength: root.dashed ? 3 : 0
|
||||
borderWidth: 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.synchronizer
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.utils
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
|
||||
enum MediaType {
|
||||
Image,
|
||||
Video
|
||||
}
|
||||
enum ImageAction {
|
||||
Copy,
|
||||
Menu,
|
||||
CharRecognition,
|
||||
Search
|
||||
}
|
||||
enum VideoAction {
|
||||
Record,
|
||||
RecordWithSound
|
||||
}
|
||||
enum SelectionMode {
|
||||
Rect,
|
||||
Window
|
||||
}
|
||||
|
||||
function close() {
|
||||
root.closed();
|
||||
}
|
||||
|
||||
property var mediaType: WRegionSelectionPanel.MediaType.Image
|
||||
property var imageAction: WRegionSelectionPanel.ImageAction.Copy
|
||||
property var selectionMode: WRegionSelectionPanel.SelectionMode.Rect
|
||||
|
||||
visible: false
|
||||
color: "transparent"
|
||||
WlrLayershell.namespace: "quickshell:regionSelector"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
anchors {
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
bottom: true
|
||||
}
|
||||
|
||||
// Hyprland stuff
|
||||
readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(screen)
|
||||
readonly property real monitorScale: hyprlandMonitor.scale
|
||||
readonly property var windows: [...HyprlandData.windowList].sort((a, b) => {
|
||||
// Sort floating=true windows before others
|
||||
if (a.floating === b.floating)
|
||||
return 0;
|
||||
return a.floating ? -1 : 1;
|
||||
})
|
||||
|
||||
property string screenshotDir: Directories.screenshotTemp
|
||||
property string screenshotPath: `${root.screenshotDir}/image-${screen.name}`
|
||||
TempScreenshotProcess {
|
||||
id: screenshotProc
|
||||
running: true
|
||||
screen: root.screen
|
||||
screenshotDir: root.screenshotDir
|
||||
screenshotPath: root.screenshotPath
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.preparationDone = true;
|
||||
}
|
||||
}
|
||||
property bool preparationDone: false
|
||||
onPreparationDoneChanged: {
|
||||
if (!preparationDone)
|
||||
return;
|
||||
root.visible = true;
|
||||
}
|
||||
|
||||
function getScreenshotAction() {
|
||||
switch (root.mediaType) {
|
||||
case WRegionSelectionPanel.MediaType.Image:
|
||||
switch (root.imageAction) {
|
||||
case WRegionSelectionPanel.ImageAction.Copy:
|
||||
return ScreenshotAction.Action.Copy;
|
||||
case WRegionSelectionPanel.ImageAction.Menu:
|
||||
return ScreenshotAction.Action.Edit;
|
||||
case WRegionSelectionPanel.ImageAction.CharRecognition:
|
||||
return ScreenshotAction.Action.CharRecognition;
|
||||
case WRegionSelectionPanel.ImageAction.Search:
|
||||
return ScreenshotAction.Action.Search;
|
||||
default:
|
||||
return ScreenshotAction.Action.Copy;
|
||||
}
|
||||
break;
|
||||
case WRegionSelectionPanel.MediaType.Video:
|
||||
switch (root.videoAction) {
|
||||
case WRegionSelectionPanel.VideoAction.Record:
|
||||
return ScreenshotAction.Action.Record;
|
||||
case WRegionSelectionPanel.VideoAction.RecordWithSound:
|
||||
return ScreenshotAction.Action.RecordWithSound;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: snipProc
|
||||
}
|
||||
|
||||
ScreencopyView {
|
||||
id: screencopyView
|
||||
anchors.fill: parent
|
||||
live: false
|
||||
captureSource: root.screen
|
||||
|
||||
focus: root.visible
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
root.close();
|
||||
} else if (event.key === Qt.Key_E && event.modifiers & Qt.ControlModifier) {
|
||||
if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Copy;
|
||||
} else {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DragManager {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
cursorShape: Qt.CrossCursor
|
||||
|
||||
property bool isWindowSelection: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window
|
||||
property var hoveredWindow: root.windows.find(w => {
|
||||
const inCurrentWorkspace = w.workspace.id === HyprlandData.activeWorkspace.id;
|
||||
const withinXRange = w.at[0] <= dragArea.mouseX && dragArea.mouseX <= w.at[0] + w.size[0];
|
||||
const withinYRange = w.at[1] <= dragArea.mouseY && dragArea.mouseY <= w.at[1] + w.size[1];
|
||||
return inCurrentWorkspace && withinXRange && withinYRange;
|
||||
})
|
||||
property int winPadding: 1
|
||||
property int selectionX: isWindowSelection ? ((hoveredWindow?.at[0] ?? 0) - winPadding) : regionTopLeftX
|
||||
property int selectionY: isWindowSelection ? ((hoveredWindow?.at[1] ?? 0) - winPadding) : regionTopLeftY
|
||||
property int selectionWidth: isWindowSelection ? ((hoveredWindow?.size[0] ?? 0) + winPadding * 2) : regionWidth
|
||||
property int selectionHeight: isWindowSelection ? ((hoveredWindow?.size[1] ?? 0) + winPadding * 2) : regionHeight
|
||||
|
||||
onDragReleased: (diffX, diffY) => {
|
||||
if (selectionWidth === 0 || selectionHeight === 0) {
|
||||
return;
|
||||
}
|
||||
const screenshotDir = Config.options.screenSnip.savePath !== "" ? Config.options.screenSnip.savePath : "";
|
||||
const screenshotAction = root.getScreenshotAction();
|
||||
const command = ScreenshotAction.getCommand(dragArea.selectionX * root.monitorScale //
|
||||
, dragArea.selectionY * root.monitorScale //
|
||||
, dragArea.selectionWidth * root.monitorScale//
|
||||
, dragArea.selectionHeight * root.monitorScale //
|
||||
, root.screenshotPath //
|
||||
, screenshotAction //
|
||||
, screenshotDir); // yo wtf is this formatting qmlls do be funnie
|
||||
snipProc.command = command;
|
||||
|
||||
// Image post-processing
|
||||
snipProc.startDetached();
|
||||
root.close();
|
||||
}
|
||||
|
||||
WRectangularSelection {
|
||||
id: rectangularSelection
|
||||
anchors.fill: parent
|
||||
regionX: dragArea.selectionX
|
||||
regionY: dragArea.selectionY
|
||||
regionWidth: dragArea.selectionWidth
|
||||
regionHeight: dragArea.selectionHeight
|
||||
dashed: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect
|
||||
}
|
||||
|
||||
RegionSelectionOptionsToolbar {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component RegionSelectionOptionsToolbar: WToolbar {
|
||||
// Image/video
|
||||
WToolbarTabBar {
|
||||
currentIndex: switch (root.mediaType) {
|
||||
case WRegionSelectionPanel.MediaType.Image:
|
||||
return 0;
|
||||
case WRegionSelectionPanel.MediaType.Video:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
WToolbarIconTabButton {
|
||||
icon.name: "camera"
|
||||
icon.color: Looks.colors.fg
|
||||
}
|
||||
WToolbarIconTabButton {
|
||||
icon.name: "video"
|
||||
icon.color: Looks.colors.fg
|
||||
}
|
||||
onCurrentIndexChanged: {
|
||||
switch (currentIndex) {
|
||||
case 0:
|
||||
root.mediaType = WRegionSelectionPanel.MediaType.Image;
|
||||
break;
|
||||
case 1:
|
||||
root.mediaType = WRegionSelectionPanel.MediaType.Video;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: Translation.tr("Snip")
|
||||
}
|
||||
}
|
||||
|
||||
// Selection type
|
||||
WToolbarButton {
|
||||
id: selectionTypeBtn
|
||||
implicitWidth: selectionTypeBtnRow.implicitWidth + 11 * 2
|
||||
leftPadding: 11
|
||||
rightPadding: 11
|
||||
onClicked: {
|
||||
selectionTypeMenu.visible = !selectionTypeMenu.visible;
|
||||
}
|
||||
contentItem: Row {
|
||||
id: selectionTypeBtnRow
|
||||
spacing: 4
|
||||
FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: switch (root.selectionMode) {
|
||||
case WRegionSelectionPanel.SelectionMode.Rect:
|
||||
return "crop";
|
||||
case WRegionSelectionPanel.SelectionMode.Window:
|
||||
return "calendar-add";
|
||||
default:
|
||||
return "crop";
|
||||
}
|
||||
implicitSize: 18
|
||||
}
|
||||
FluentIcon {
|
||||
anchors {
|
||||
top: parent.top
|
||||
topMargin: (parent.height - height) / 2 + (selectionTypeBtn.down ? 2 : 0)
|
||||
Behavior on topMargin {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
}
|
||||
icon: "chevron-down"
|
||||
implicitSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
WMenu {
|
||||
id: selectionTypeMenu
|
||||
onClosed: screencopyView.focus = true
|
||||
x: -margins
|
||||
y: -margins - (selectionTypeBtn.parent.height - selectionTypeBtn.height) - 16
|
||||
topMargin: -6
|
||||
height: implicitHeight + sourceEdgeMargin
|
||||
|
||||
color: Looks.colors.bg1Base
|
||||
|
||||
Action {
|
||||
icon.name: "crop"
|
||||
text: Translation.tr("Rectangle")
|
||||
checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect
|
||||
onTriggered: {
|
||||
root.selectionMode = WRegionSelectionPanel.SelectionMode.Rect;
|
||||
}
|
||||
}
|
||||
Action {
|
||||
icon.name: "calendar-add"
|
||||
text: Translation.tr("Window")
|
||||
checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window
|
||||
onTriggered: {
|
||||
root.selectionMode = WRegionSelectionPanel.SelectionMode.Window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
text: Translation.tr("Snipping area")
|
||||
}
|
||||
}
|
||||
|
||||
// Markup
|
||||
WToolbarIconButton {
|
||||
icon.name: "image-edit"
|
||||
enabled: root.mediaType === WRegionSelectionPanel.MediaType.Image
|
||||
checked: root.imageAction === WRegionSelectionPanel.ImageAction.Menu
|
||||
onClicked: {
|
||||
if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Copy;
|
||||
} else {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Menu;
|
||||
}
|
||||
}
|
||||
WToolTip {
|
||||
text: Translation.tr("Quick markup (Ctrl+E)")
|
||||
}
|
||||
}
|
||||
|
||||
WToolbarSeparator {}
|
||||
|
||||
// Tools
|
||||
WToolbarIconButton {
|
||||
icon.name: "search-visual"
|
||||
checked: root.imageAction === WRegionSelectionPanel.ImageAction.Search
|
||||
onClicked: {
|
||||
if (root.imageAction === WRegionSelectionPanel.ImageAction.Search && root.mediaType === WRegionSelectionPanel.MediaType.Image) {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Copy;
|
||||
} else {
|
||||
root.mediaType = WRegionSelectionPanel.MediaType.Image;
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Search;
|
||||
}
|
||||
}
|
||||
WToolTip {
|
||||
text: Translation.tr("Image search")
|
||||
}
|
||||
}
|
||||
WToolbarIconButton {
|
||||
icon.name: "eyedropper"
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["bash", "-c", "sleep 0.2; hyprpicker -a"]);
|
||||
root.closed();
|
||||
}
|
||||
WToolTip {
|
||||
text: Translation.tr("Color picker")
|
||||
}
|
||||
}
|
||||
WToolbarIconButton {
|
||||
icon.name: "scan-text"
|
||||
checked: root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition
|
||||
onClicked: {
|
||||
if (root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition && root.mediaType === WRegionSelectionPanel.MediaType.Image) {
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.Copy;
|
||||
} else {
|
||||
root.mediaType = WRegionSelectionPanel.MediaType.Image;
|
||||
root.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition;
|
||||
}
|
||||
}
|
||||
WToolTip {
|
||||
text: Translation.tr("Text extractor")
|
||||
}
|
||||
}
|
||||
|
||||
WToolbarSeparator {}
|
||||
|
||||
WToolbarIconButton {
|
||||
icon.name: "dismiss"
|
||||
onClicked: root.close()
|
||||
WToolTip {
|
||||
text: Translation.tr("Close (Esc)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.services
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Hyprland
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
function dismiss() {
|
||||
GlobalStates.regionSelectorOpen = false;
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: regionSelectorLoader
|
||||
active: GlobalStates.regionSelectorOpen
|
||||
|
||||
sourceComponent: WRegionSelectionPanel {
|
||||
onClosed: root.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
function screenshot() {
|
||||
GlobalStates.regionSelectorOpen = true;
|
||||
}
|
||||
|
||||
function ocr() {
|
||||
GlobalStates.regionSelectorOpen = true;
|
||||
regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image;
|
||||
regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition;
|
||||
}
|
||||
|
||||
function record() {
|
||||
GlobalStates.regionSelectorOpen = true;
|
||||
regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video;
|
||||
regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.Record;
|
||||
}
|
||||
|
||||
function recordWithSound() {
|
||||
GlobalStates.regionSelectorOpen = true;
|
||||
regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video;
|
||||
regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.RecordWithSound;
|
||||
}
|
||||
|
||||
function search() {
|
||||
GlobalStates.regionSelectorOpen = true;
|
||||
regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image;
|
||||
regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.Search;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "region"
|
||||
|
||||
function screenshot() {
|
||||
root.screenshot();
|
||||
}
|
||||
function ocr() {
|
||||
root.ocr();
|
||||
}
|
||||
function record() {
|
||||
root.record();
|
||||
}
|
||||
function recordWithSound() {
|
||||
root.recordWithSound();
|
||||
}
|
||||
function search() {
|
||||
root.search();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "regionScreenshot"
|
||||
description: "Takes a screenshot of the selected region"
|
||||
onPressed: root.screenshot()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionSearch"
|
||||
description: "Searches the selected region"
|
||||
onPressed: root.search()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionOcr"
|
||||
description: "Recognizes text in the selected region"
|
||||
onPressed: root.ocr()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionRecord"
|
||||
description: "Records the selected region"
|
||||
onPressed: root.record()
|
||||
}
|
||||
GlobalShortcut {
|
||||
name: "regionRecordWithSound"
|
||||
description: "Records the selected region with sound"
|
||||
onPressed: root.recordWithSound()
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,7 @@ WBarAttachedPanelContent {
|
||||
id: searchBar
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: 832 // TODO: Make sizes naturally inferred
|
||||
horizontalPadding: root.searching ? 24 : 32
|
||||
horizontalPadding: 32
|
||||
// verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel
|
||||
Synchronizer on searching {
|
||||
property alias target: root.searching
|
||||
|
||||
+4
-1
@@ -44,7 +44,7 @@ WChoiceButton {
|
||||
Layout.fillHeight: true
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 11
|
||||
implicitHeight: root.firstEntry ? 62 : 36
|
||||
implicitHeight: Math.max(root.firstEntry ? 62 : 36, entryContentRow.implicitHeight + 8 * 2)
|
||||
implicitWidth: entryContentRow.implicitWidth + leftPadding + rightPadding
|
||||
topRightRadius: 0
|
||||
bottomRightRadius: 0
|
||||
@@ -54,6 +54,7 @@ WChoiceButton {
|
||||
id: entryContentRow
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
spacing: 8
|
||||
@@ -102,6 +103,7 @@ WChoiceButton {
|
||||
text: root.entry.name
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
maximumLineCount: 2
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
WText {
|
||||
@@ -109,6 +111,7 @@ WChoiceButton {
|
||||
visible: root.firstEntry
|
||||
text: root.entry.type
|
||||
color: Looks.colors.accentUnfocused
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ RowLayout {
|
||||
id: root
|
||||
|
||||
property int maxResultsPerCategory: 4
|
||||
property int resultLimit: 20
|
||||
property StartMenuContext context
|
||||
property int currentIndex: context.currentIndex
|
||||
onCurrentIndexChanged: {
|
||||
@@ -99,21 +100,33 @@ RowLayout {
|
||||
|
||||
// Collect max 4 per category
|
||||
var categorizedResults = [];
|
||||
categories.forEach(category => {
|
||||
let categoriesArray = Array.from(categories);
|
||||
let totalCount = 0;
|
||||
for (let c = 0; c < categoriesArray.length; c++) {
|
||||
let category = categoriesArray[c];
|
||||
let count = 0;
|
||||
for (let i = 0; i < allResults.length; i++) {
|
||||
if (allResults[i].type === category) {
|
||||
if (totalCount >= root.resultLimit) {
|
||||
break;
|
||||
}
|
||||
const entry = allResults[i];
|
||||
const tweakedEntry = searchResultComp.createObject(null, Object.assign({}, entry));
|
||||
tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type
|
||||
tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type;
|
||||
|
||||
categorizedResults.push(tweakedEntry); // Section header
|
||||
count++;
|
||||
totalCount++;
|
||||
if (count >= root.maxResultsPerCategory) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (totalCount >= root.resultLimit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// print(JSON.stringify(categorizedResults, null, 2));
|
||||
return categorizedResults;
|
||||
}
|
||||
|
||||
@@ -28,13 +28,13 @@ GridLayout {
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Creativity"),
|
||||
categories: ["AudioVideo", "Graphics"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Other"),
|
||||
categories: ["Game"]
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("System"),
|
||||
categories: ["Settings", "System"]
|
||||
})
|
||||
}), aggAppCatComp.createObject(null, {
|
||||
name: Translation.tr("Other"),
|
||||
categories: ["Game"]
|
||||
}),
|
||||
]
|
||||
|
||||
Repeater {
|
||||
|
||||
@@ -16,29 +16,43 @@ Rectangle {
|
||||
id: root
|
||||
|
||||
color: ColorUtils.transparentize(Looks.colors.bg1Base, 0.5)
|
||||
property bool draggingWindow: false
|
||||
property real openProgress: 0
|
||||
property Item hoveredWorkspace: null
|
||||
signal closed
|
||||
|
||||
Component.onCompleted: {
|
||||
openAnim.start();
|
||||
}
|
||||
function close() {
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: root
|
||||
property: "openProgress"
|
||||
to: 1
|
||||
duration: 200
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
PropertyAnimation {
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
target: root
|
||||
property: "openProgress"
|
||||
to: 0
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
|
||||
PropertyAnimation {
|
||||
target: root
|
||||
property: "openProgress"
|
||||
to: 0
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.closed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Windows
|
||||
@@ -81,8 +95,18 @@ Rectangle {
|
||||
return resultLayout;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
z: 0
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Windows
|
||||
WListView {
|
||||
id: windowListView
|
||||
z: root.openProgress == 1 ? 2 : 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
@@ -96,9 +120,8 @@ Rectangle {
|
||||
rightMargin: root.padding
|
||||
height: Math.min(contentHeight + topMargin + bottomMargin, root.height - (wsBorder.height + 16))
|
||||
|
||||
interactive: height < contentHeight
|
||||
|
||||
clip: true
|
||||
interactive: (height < contentHeight) && !root.draggingWindow
|
||||
clip: root.openProgress > 0.99 && !root.draggingWindow
|
||||
|
||||
model: ScriptModel {
|
||||
values: root.arrangedToplevels
|
||||
@@ -107,7 +130,7 @@ Rectangle {
|
||||
id: clientRow
|
||||
required property var modelData
|
||||
spacing: root.spacing
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.horizontalCenter: parent?.horizontalCenter ?? undefined
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
@@ -117,24 +140,61 @@ Rectangle {
|
||||
id: clientGridArea
|
||||
required property int index
|
||||
required property var modelData
|
||||
implicitWidth: windowItem.implicitWidth
|
||||
implicitHeight: windowItem.implicitHeight
|
||||
implicitWidth: windowItem.openedSize.width
|
||||
implicitHeight: windowItem.openedSize.height + windowItem.titleBarImplicitHeight
|
||||
|
||||
TaskViewWindow {
|
||||
id: windowItem
|
||||
z: 9999
|
||||
drag {
|
||||
target: this
|
||||
z: Drag.active ? 2 : 1
|
||||
opacity: openAnim.running ? root.openProgress : 1
|
||||
|
||||
property int mappedX: {
|
||||
// print("AAAWAWAAWAWWA: ", -(clientRow.x + clientGridArea.x + root.padding));
|
||||
var rootPosToThis = -(clientRow.x + clientGridArea.x + root.padding);
|
||||
return rootPosToThis + hyprlandClient.at[0];
|
||||
}
|
||||
property int mappedY: {
|
||||
// print("AAAWAWAAWAWWA YYYY YUIUSDFOIU: ", clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight)
|
||||
var rootPosToThis = -(clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight);
|
||||
return rootPosToThis + hyprlandClient.at[1];
|
||||
}
|
||||
property int openedX: 0
|
||||
property int openedY: 0
|
||||
// property int openedX: Drag.active ? (dragHandler.xAxis.activeValue) : 0
|
||||
// property int openedY: Drag.active ? (dragHandler.yAxis.activeValue) : 0
|
||||
scaleSize: (root.openProgress > 0 && !closeAnim.running)
|
||||
x: mappedX + (openedX - mappedX) * root.openProgress
|
||||
y: mappedY + (openedY - mappedY) * root.openProgress
|
||||
|
||||
droppable: root.hoveredWorkspace !== null
|
||||
Drag.active: dragHandler.active
|
||||
Drag.hotSpot.x: mouseX
|
||||
Drag.hotSpot.y: mouseY
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
target: null
|
||||
xAxis.onActiveValueChanged: {
|
||||
windowItem.openedX = dragHandler.xAxis.activeValue;
|
||||
}
|
||||
yAxis.onActiveValueChanged: {
|
||||
windowItem.openedY = dragHandler.yAxis.activeValue;
|
||||
}
|
||||
onActiveChanged: {
|
||||
if (drag.active) {
|
||||
parent = root;
|
||||
if (active) {
|
||||
root.draggingWindow = true;
|
||||
} else {
|
||||
parent = clientGridArea;
|
||||
x = 0;
|
||||
y = 0;
|
||||
root.draggingWindow = false;
|
||||
if (root.hoveredWorkspace !== null && root.hoveredWorkspace.workspace !== windowItem.hyprlandClient.workspace.id) {
|
||||
Hyprland.dispatch(`movetoworkspacesilent ${root.hoveredWorkspace.workspace}, address:${windowItem.hyprlandClient.address}`);
|
||||
} else {
|
||||
windowItem.openedX = 0;
|
||||
windowItem.openedY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layout.alignment: Qt.AlignTop
|
||||
maxHeight: root.maxWindowHeight
|
||||
maxWidth: root.maxWindowWidth
|
||||
@@ -148,6 +208,7 @@ Rectangle {
|
||||
// Workspaces
|
||||
Rectangle {
|
||||
id: wsBorder
|
||||
z: root.openProgress == 1 ? 1 : 2
|
||||
property real sourceEdgeMargin: -(height + 8) + root.openProgress * (height + 16)
|
||||
anchors {
|
||||
left: parent.left
|
||||
@@ -170,7 +231,7 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: wsBorder.border.width
|
||||
radius: wsBorder.radius - wsBorder.border.width
|
||||
color: Looks.colors.bgPanelFooterBase
|
||||
color: Looks.colors.bgPanelFooterBackground
|
||||
|
||||
implicitHeight: 174
|
||||
|
||||
@@ -210,9 +271,29 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
delegate: TaskViewWorkspace {
|
||||
id: workspaceItem
|
||||
required property int index
|
||||
workspace: index + 1
|
||||
newWorkspace: index == workspaceIndexModel.count - 1
|
||||
|
||||
droppable: root.hoveredWorkspace === workspaceItem
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
onEntered: drag => {
|
||||
root.hoveredWorkspace = workspaceItem;
|
||||
}
|
||||
onExited: {
|
||||
if (root.hoveredWorkspace === workspaceItem) {
|
||||
root.hoveredWorkspace = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
root.closed(); // Close immediately to avoid weird animations
|
||||
Hyprland.dispatch(`workspace ${workspaceItem.workspace}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,47 +25,60 @@ WMouseAreaButton {
|
||||
|
||||
property string iconName: AppSearch.guessIcon(hyprlandClient?.class)
|
||||
|
||||
color: containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase
|
||||
borderColor: Looks.colors.bg2Border
|
||||
color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBackground)
|
||||
borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0)
|
||||
radius: Looks.radius.xLarge
|
||||
|
||||
property size size: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight)
|
||||
property real titleBarImplicitHeight: titleBar.implicitHeight
|
||||
property bool scaleSize: true
|
||||
property size openedSize: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight);
|
||||
property size fullSize: Qt.size(hyprlandClient?.size[0] ?? maxWidth, hyprlandClient?.size[1] ?? maxHeight)
|
||||
property size size: scaleSize ? openedSize : fullSize
|
||||
implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138)
|
||||
implicitHeight: Math.round(contentItem.implicitHeight)
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
maskSource: Item {
|
||||
width: root.background.width
|
||||
height: root.background.height
|
||||
radius: root.background.radius
|
||||
Rectangle {
|
||||
radius: root.background.radius
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: root.drag.active ? root.titleBarImplicitHeight : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1
|
||||
property bool droppable: false
|
||||
scale: (root.pressedButtons & Qt.LeftButton || root.Drag.active) ? (droppable ? 0.4 : 0.95) : 1
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
id: scaleAnim
|
||||
duration: 300
|
||||
duration: 200
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
function closeWindow() {
|
||||
Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`)
|
||||
Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`);
|
||||
}
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
|
||||
onClicked: (event) => {
|
||||
onClicked: event => {
|
||||
if (event.button === Qt.LeftButton) {
|
||||
GlobalStates.overviewOpen = false
|
||||
Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`)
|
||||
GlobalStates.overviewOpen = false;
|
||||
Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`);
|
||||
GlobalStates.overviewOpen = false;
|
||||
} else if (event.button === Qt.MiddleButton) {
|
||||
root.closeWindow();
|
||||
event.accepted = true;
|
||||
} else if (event.button === Qt.RightButton) {
|
||||
if (!windowMenu.visible) windowMenu.popup();
|
||||
else windowMenu.close();
|
||||
if (!windowMenu.visible)
|
||||
windowMenu.popup();
|
||||
else
|
||||
windowMenu.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +90,8 @@ WMouseAreaButton {
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
id: titleBar
|
||||
opacity: root.drag.active ? 0 : 1
|
||||
spacing: 8
|
||||
WAppIcon {
|
||||
Layout.leftMargin: 10
|
||||
@@ -104,6 +119,14 @@ WMouseAreaButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
implicitWidth: Math.round(root.size.width)
|
||||
implicitHeight: Math.round(root.size.height)
|
||||
constraintSize: Qt.size(Math.round(root.size.width), Math.round(root.size.height))
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
|
||||
captureSource: root.toplevel ?? null
|
||||
live: true
|
||||
@@ -120,7 +143,7 @@ WMouseAreaButton {
|
||||
icon.name: isPinned ? "checkmark" : "empty"
|
||||
text: Translation.tr("Show this window on all desktops")
|
||||
onTriggered: {
|
||||
Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`)
|
||||
Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`);
|
||||
}
|
||||
}
|
||||
Action {
|
||||
|
||||
@@ -16,6 +16,7 @@ WMouseAreaButton {
|
||||
|
||||
required property int workspace
|
||||
property bool newWorkspace: false
|
||||
property bool droppable: false
|
||||
|
||||
readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace
|
||||
readonly property real screenWidth: QsWindow.window?.width ?? 0
|
||||
@@ -28,12 +29,7 @@ WMouseAreaButton {
|
||||
height: ListView.view?.height ?? 100
|
||||
implicitWidth: 244 // for now
|
||||
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = false;
|
||||
Hyprland.dispatch(`workspace ${root.workspace}`);
|
||||
}
|
||||
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg2, isActiveWorkspace ? 0 : 1)
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg2, (isActiveWorkspace || droppable) ? 0 : 1)
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
@@ -119,6 +115,14 @@ WMouseAreaButton {
|
||||
icon: "add"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: 2
|
||||
visible: root.droppable && !root.newWorkspace
|
||||
anchors.fill: parent
|
||||
color: Looks.colors.accent
|
||||
opacity: 0.2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,14 @@ Scope {
|
||||
Loader {
|
||||
id: panelLoader
|
||||
required property var modelData
|
||||
active: GlobalStates.overviewOpen
|
||||
active: false
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (GlobalStates.overviewOpen)
|
||||
panelLoader.active = true;
|
||||
}
|
||||
}
|
||||
sourceComponent: PanelWindow {
|
||||
id: root
|
||||
property string searchingText: ""
|
||||
@@ -33,7 +40,7 @@ Scope {
|
||||
|
||||
WlrLayershell.namespace: "quickshell:wTaskView"
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
// WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
@@ -44,7 +51,26 @@ Scope {
|
||||
}
|
||||
|
||||
TaskViewContent {
|
||||
id: taskViewContent
|
||||
anchors.fill: parent
|
||||
|
||||
Component.onCompleted: {
|
||||
taskViewContent.forceActiveFocus();
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
GlobalStates.overviewOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onOverviewOpenChanged() {
|
||||
if (!GlobalStates.overviewOpen)
|
||||
taskViewContent.close();
|
||||
}
|
||||
}
|
||||
onClosed: panelLoader.active = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user