Merge branch 'main' into module/wifi
C++ / fmt (pull_request) Successful in 7s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 35s
Rust / build (pull_request) Successful in 1m0s
Python / verify (pull_request) Successful in 1m44s
Rust / fmt (pull_request) Successful in 45s
Rust / clippy (pull_request) Successful in 1m7s
C++ / fmt (pull_request) Successful in 7s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 11s
JS/TS / fmt (pull_request) Successful in 13s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 35s
Rust / build (pull_request) Successful in 1m0s
Python / verify (pull_request) Successful in 1m44s
Rust / fmt (pull_request) Successful in 45s
Rust / clippy (pull_request) Successful in 1m7s
This commit is contained in:
@@ -1,88 +0,0 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
import Quickshell.Hyprland
|
||||
import ZShell
|
||||
import qs.Components
|
||||
|
||||
Scope {
|
||||
LazyLoader {
|
||||
id: root
|
||||
|
||||
property bool closing
|
||||
property bool freeze
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: win
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.keyboardFocus: root.closing ? WlrKeyboardFocus.None : WlrKeyboardFocus.Exclusive
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.namespace: "areapicker"
|
||||
color: "transparent"
|
||||
mask: root.closing ? empty : null
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
top: true
|
||||
}
|
||||
|
||||
Region {
|
||||
id: empty
|
||||
|
||||
}
|
||||
|
||||
Picker {
|
||||
loader: root
|
||||
screen: win.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function open(): void {
|
||||
root.freeze = false;
|
||||
root.closing = false;
|
||||
root.activeAsync = true;
|
||||
}
|
||||
|
||||
function openFreeze(): void {
|
||||
root.freeze = true;
|
||||
root.closing = false;
|
||||
root.activeAsync = true;
|
||||
}
|
||||
|
||||
target: "picker"
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "screenshot"
|
||||
|
||||
onPressed: {
|
||||
root.freeze = false;
|
||||
root.closing = false;
|
||||
root.activeAsync = true;
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "screenshotFreeze"
|
||||
|
||||
onPressed: {
|
||||
root.freeze = true;
|
||||
root.closing = false;
|
||||
root.activeAsync = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Services
|
||||
import ZShell.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property bool enabled: service.enabled
|
||||
readonly property int end: Config.general.color.scheduleHyprsunsetEnd
|
||||
property bool manualToggle: false
|
||||
readonly property int start: Config.general.color.scheduleHyprsunsetStart
|
||||
readonly property int temp: Config.general.color.hyprsunsetTemp
|
||||
|
||||
function checkStartup(): void {
|
||||
if (!Config.general.color.scheduleHyprsunset)
|
||||
return;
|
||||
|
||||
service.apply();
|
||||
}
|
||||
|
||||
function toggleNightLight(): void {
|
||||
service.manualToggle = true;
|
||||
service.toggle();
|
||||
}
|
||||
|
||||
HyprsunsetManager {
|
||||
id: service
|
||||
|
||||
activeAuto: Config.general.color.scheduleHyprsunset
|
||||
endTime: root.end
|
||||
startTime: root.start
|
||||
temp: root.temp
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,14 @@ import qs.Services
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property int darkEnd: Config.general.color.scheduleDarkEnd
|
||||
readonly property int darkStart: Config.general.color.scheduleDarkStart
|
||||
readonly property bool enabled: Config.general.color.scheduleDark && Config.general.color.schemeGeneration
|
||||
readonly property int darkEnd: Config.colors.scheduleDarkEnd
|
||||
readonly property int darkStart: Config.colors.scheduleDarkStart
|
||||
readonly property bool enabled: Config.colors.scheduleDark && Config.colors.schemeGen
|
||||
|
||||
function applyDarkMode() {
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "dark"]);
|
||||
|
||||
Config.general.color.mode = "dark";
|
||||
Config.colors.mode = "dark";
|
||||
|
||||
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-dark'"]);
|
||||
|
||||
@@ -30,13 +30,13 @@ Singleton {
|
||||
function applyLightMode() {
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--mode", "light"]);
|
||||
|
||||
Config.general.color.mode = "light";
|
||||
Config.colors.mode = "light";
|
||||
|
||||
Quickshell.execDetached(["gsettings", "set", "org.gnome.desktop.interface", "color-scheme", "'prefer-light'"]);
|
||||
|
||||
Quickshell.execDetached(["sh", "-c", `sed -i 's/color_scheme_path=\\(.*\\)Dark.colors/color_scheme_path=\\1Light.colors/' ${Paths.home}/.config/qt6ct/qt6ct.conf`]);
|
||||
|
||||
if (Config.general.color.neovimColors)
|
||||
if (Config.colors.neovimColors)
|
||||
Quickshell.execDetached(["sed", "-i", "'s/\\(vim.cmd.colorscheme \\).*/\\1\"onelight\"/'", "~/.config/nvim/lua/config/load-colorscheme.lua"]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import ZShell.Internal
|
||||
import ZShell.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property real _downloadSpeed: 0
|
||||
property real _downloadTotal: 0
|
||||
|
||||
property real _initialRxBytes: 0
|
||||
property real _initialTxBytes: 0
|
||||
property bool _initialized: false
|
||||
|
||||
property real _prevRxBytes: 0
|
||||
property real _prevTimestamp: 0
|
||||
property real _prevTxBytes: 0
|
||||
property real _uploadSpeed: 0
|
||||
property real _uploadTotal: 0
|
||||
|
||||
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
||||
|
||||
readonly property real downloadSpeed: _downloadSpeed
|
||||
|
||||
readonly property real downloadTotal: _downloadTotal
|
||||
readonly property int historyLength: Config.dashboard.performance.showVram ? 70 : 30
|
||||
property int refCount: 0
|
||||
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
||||
readonly property real uploadSpeed: _uploadSpeed
|
||||
readonly property real uploadTotal: _uploadTotal
|
||||
|
||||
function resetSampling(): void {
|
||||
netDevFile.reload();
|
||||
|
||||
const content = netDevFile.text();
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
const data = root.parseNetDev(content);
|
||||
const now = Date.now();
|
||||
|
||||
root._prevRxBytes = data.rx;
|
||||
root._prevTxBytes = data.tx;
|
||||
root._prevTimestamp = now;
|
||||
}
|
||||
|
||||
function formatBytes(bytes: real): var {
|
||||
// Handle negative or invalid values
|
||||
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
|
||||
return {
|
||||
value: 0,
|
||||
unit: "B/s"
|
||||
};
|
||||
}
|
||||
|
||||
if (bytes < 1024) {
|
||||
return {
|
||||
value: bytes,
|
||||
unit: "B/s"
|
||||
};
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return {
|
||||
value: bytes / 1024,
|
||||
unit: "KB/s"
|
||||
};
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return {
|
||||
value: bytes / (1024 * 1024),
|
||||
unit: "MB/s"
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
value: bytes / (1024 * 1024 * 1024),
|
||||
unit: "GB/s"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function formatBytesTotal(bytes: real): var {
|
||||
// Handle negative or invalid values
|
||||
if (bytes < 0 || isNaN(bytes) || !isFinite(bytes)) {
|
||||
return {
|
||||
value: 0,
|
||||
unit: "B"
|
||||
};
|
||||
}
|
||||
|
||||
if (bytes < 1024) {
|
||||
return {
|
||||
value: bytes,
|
||||
unit: "B"
|
||||
};
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return {
|
||||
value: bytes / 1024,
|
||||
unit: "KB"
|
||||
};
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return {
|
||||
value: bytes / (1024 * 1024),
|
||||
unit: "MB"
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
value: bytes / (1024 * 1024 * 1024),
|
||||
unit: "GB"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parseNetDev(content: string): var {
|
||||
const lines = content.split("\n");
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
|
||||
for (let i = 2; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
const parts = line.split(/\s+/);
|
||||
if (parts.length < 10)
|
||||
continue;
|
||||
|
||||
const iface = parts[0].replace(":", "");
|
||||
// Skip loopback interface
|
||||
if (iface === "lo")
|
||||
continue;
|
||||
|
||||
const rxBytes = parseFloat(parts[1]) || 0;
|
||||
const txBytes = parseFloat(parts[9]) || 0;
|
||||
|
||||
totalRx += rxBytes;
|
||||
totalTx += txBytes;
|
||||
}
|
||||
|
||||
return {
|
||||
rx: totalRx,
|
||||
tx: totalTx
|
||||
};
|
||||
}
|
||||
|
||||
CircularBuffer {
|
||||
id: _downloadBuffer
|
||||
|
||||
capacity: root.historyLength + 1
|
||||
}
|
||||
|
||||
CircularBuffer {
|
||||
id: _uploadBuffer
|
||||
|
||||
capacity: root.historyLength + 1
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: netDevFile
|
||||
|
||||
path: "/proc/net/dev"
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: Config.dashboard.resourceUpdateInterval
|
||||
repeat: true
|
||||
running: true
|
||||
triggeredOnStart: true
|
||||
|
||||
onTriggered: {
|
||||
if (root.refCount <= 0) {
|
||||
root.resetSampling();
|
||||
return;
|
||||
}
|
||||
|
||||
netDevFile.reload();
|
||||
const content = netDevFile.text();
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
const data = root.parseNetDev(content);
|
||||
const now = Date.now();
|
||||
|
||||
if (!root._initialized) {
|
||||
root._initialRxBytes = data.rx;
|
||||
root._initialTxBytes = data.tx;
|
||||
root._prevRxBytes = data.rx;
|
||||
root._prevTxBytes = data.tx;
|
||||
root._prevTimestamp = now;
|
||||
root._initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const timeDelta = (now - root._prevTimestamp) / 1000; // seconds
|
||||
if (timeDelta > 0) {
|
||||
// Calculate byte deltas
|
||||
let rxDelta = data.rx - root._prevRxBytes;
|
||||
let txDelta = data.tx - root._prevTxBytes;
|
||||
|
||||
// Handle counter overflow (when counters wrap around from max to 0)
|
||||
// This happens when counters exceed 32-bit or 64-bit limits
|
||||
if (rxDelta < 0) {
|
||||
// Counter wrapped around - assume 64-bit counter
|
||||
rxDelta += Math.pow(2, 64);
|
||||
}
|
||||
if (txDelta < 0) {
|
||||
txDelta += Math.pow(2, 64);
|
||||
}
|
||||
|
||||
// Calculate speeds
|
||||
root._downloadSpeed = rxDelta / timeDelta;
|
||||
root._uploadSpeed = txDelta / timeDelta;
|
||||
|
||||
if (root._downloadSpeed >= 0 && isFinite(root._downloadSpeed))
|
||||
_downloadBuffer.push(root._downloadSpeed);
|
||||
|
||||
if (root._uploadSpeed >= 0 && isFinite(root._uploadSpeed))
|
||||
_uploadBuffer.push(root._uploadSpeed);
|
||||
}
|
||||
|
||||
// Calculate totals with overflow handling
|
||||
let downTotal = data.rx - root._initialRxBytes;
|
||||
let upTotal = data.tx - root._initialTxBytes;
|
||||
|
||||
// Handle counter overflow for totals
|
||||
if (downTotal < 0) {
|
||||
downTotal += Math.pow(2, 64);
|
||||
}
|
||||
if (upTotal < 0) {
|
||||
upTotal += Math.pow(2, 64);
|
||||
}
|
||||
|
||||
root._downloadTotal = downTotal;
|
||||
root._uploadTotal = upTotal;
|
||||
|
||||
root._prevRxBytes = data.rx;
|
||||
root._prevTxBytes = data.tx;
|
||||
root._prevTimestamp = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import ZShell.Services
|
||||
import ZShell.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property bool enabled: service.enabled
|
||||
readonly property int end: Config.display.nightlight.scheduleEnd
|
||||
property bool manualToggle: false
|
||||
readonly property int start: Config.display.nightlight.scheduleStart
|
||||
readonly property int temp: Config.display.nightlight.temp
|
||||
|
||||
function checkStartup(): void {
|
||||
if (!Config.display.nightlight.schedule)
|
||||
return;
|
||||
|
||||
service.apply();
|
||||
}
|
||||
|
||||
function toggleNightLight(): void {
|
||||
service.manualToggle = true;
|
||||
service.toggle(true);
|
||||
}
|
||||
|
||||
NightlightManager {
|
||||
id: service
|
||||
|
||||
activeAuto: Config.display.nightlight.schedule
|
||||
endTime: root.end
|
||||
startTime: root.start
|
||||
temp: root.temp
|
||||
fadeDuration: Config.display.nightlight.fadeDuration / 1000
|
||||
useNativeNightlight: Config.display.nightlight.useNative
|
||||
}
|
||||
}
|
||||
+100
-60
@@ -41,8 +41,9 @@ Item {
|
||||
property real rsy: Math.min(sy, ey)
|
||||
readonly property real scaleRatio: Hypr.monitorFor(screen).scale
|
||||
required property ShellScreen screen
|
||||
required property ScreenState screenState
|
||||
property real sh: Math.abs(sy - ey)
|
||||
readonly property color shadowColor: Hypr.options.decoration.shadow.color
|
||||
readonly property color shadowColor: Hypr.options.decoration.shadow?.color ?? "black"
|
||||
readonly property bool shadowEnabled: Hypr.options.decoration.shadow.enabled
|
||||
readonly property var shadowOffset: Hypr.options.decoration.shadow.offset
|
||||
readonly property int shadowRange: Hypr.options.decoration.shadow.range
|
||||
@@ -97,10 +98,6 @@ Item {
|
||||
const args = Config.screenshot.mode === "auto" ? ["--rounding", `${rounding}`, "--radius", root.cornerRadius, "--shadow", root.shadowEnabled, "--shadow-blur", `${shadow_blur}`, "--shadow-color", `${r},${g},${b},${a}`, "--shadow-offset-x", root.shadowOffset[0], "--shadow-offset-y", root.shadowOffset[1]] : [];
|
||||
const postCmd = Config.screenshot.enable_pp ? ["zshell-img-tools", "--scale", root.scaleRatio, ...args, "--image"] : null;
|
||||
|
||||
overlay.visible = border.visible = false;
|
||||
handles.visible = false;
|
||||
toolbar.visible = false;
|
||||
|
||||
ZShellIo.saveItem(captureLayer, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => {
|
||||
const finalize = finalPath => {
|
||||
if (copyToClipboard) {
|
||||
@@ -126,39 +123,35 @@ Item {
|
||||
focus: true
|
||||
opacity: 0
|
||||
|
||||
Behavior on rsx {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
Anim {}
|
||||
}
|
||||
Behavior on rsy {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
Anim {}
|
||||
}
|
||||
Behavior on sh {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
Anim {}
|
||||
}
|
||||
Behavior on sw {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
Anim {}
|
||||
}
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: 300
|
||||
}
|
||||
}
|
||||
Behavior on rsx {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
ExAnim {
|
||||
}
|
||||
}
|
||||
Behavior on rsy {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
ExAnim {
|
||||
}
|
||||
}
|
||||
Behavior on sh {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
ExAnim {
|
||||
}
|
||||
}
|
||||
Behavior on sw {
|
||||
enabled: !selectArea.pressed && root.mode === "select"
|
||||
|
||||
ExAnim {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
Hypr.extras.refreshOptions();
|
||||
screencopy.active = true;
|
||||
root.forceActiveFocus();
|
||||
|
||||
opacity = 1;
|
||||
|
||||
@@ -223,13 +216,63 @@ Item {
|
||||
onReleased: root.enterEditMode()
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onClosingChanged(): void {
|
||||
if (root.loader.closing)
|
||||
closeAnim.start();
|
||||
else {
|
||||
closeAnim.stop();
|
||||
returnAnim.start();
|
||||
}
|
||||
}
|
||||
|
||||
target: root.loader
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
id: returnAnim
|
||||
|
||||
Anim {
|
||||
duration: 300
|
||||
property: "opacity"
|
||||
target: root
|
||||
to: 1
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "rsx"
|
||||
target: root
|
||||
to: Math.min(root.sx, root.ex)
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "rsy"
|
||||
target: root
|
||||
to: Math.min(root.sy, root.ey)
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "sw"
|
||||
target: root
|
||||
to: Math.abs(root.sx - root.ex)
|
||||
}
|
||||
|
||||
Anim {
|
||||
property: "sh"
|
||||
target: root
|
||||
to: Math.abs(root.sy - root.ey)
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
|
||||
PropertyAction {
|
||||
property: "closing"
|
||||
target: root.loader
|
||||
value: true
|
||||
ScriptAction {
|
||||
script: {
|
||||
const states = ShellState.allStates();
|
||||
for (const s of states)
|
||||
s.screenshot = false;
|
||||
}
|
||||
}
|
||||
|
||||
ParallelAnimation {
|
||||
@@ -240,19 +283,19 @@ Item {
|
||||
to: 0
|
||||
}
|
||||
|
||||
ExAnim {
|
||||
Anim {
|
||||
properties: "rsx,rsy"
|
||||
target: root
|
||||
to: 0
|
||||
}
|
||||
|
||||
ExAnim {
|
||||
Anim {
|
||||
property: "sw"
|
||||
target: root
|
||||
to: root.screen.width
|
||||
}
|
||||
|
||||
ExAnim {
|
||||
Anim {
|
||||
property: "sh"
|
||||
target: root
|
||||
to: root.screen.height
|
||||
@@ -271,17 +314,12 @@ Item {
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
Loader {
|
||||
ScreencopyView {
|
||||
id: screencopy
|
||||
|
||||
active: false
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: ScreencopyView {
|
||||
captureSource: root.screen
|
||||
paintCursor: false
|
||||
}
|
||||
captureSource: root.screen
|
||||
paintCursor: false
|
||||
}
|
||||
|
||||
AnnotationCanvas {
|
||||
@@ -290,6 +328,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
inkColor: root.inkColor
|
||||
layer.enabled: true
|
||||
selection: selectionRect
|
||||
tool: root.mode === "edit" && root.tool !== "move" ? root.tool : "none"
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
@@ -301,15 +340,14 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
CustomRect {
|
||||
id: overlay
|
||||
|
||||
anchors.fill: parent
|
||||
color: "white"
|
||||
layer.enabled: true
|
||||
opacity: 0.3
|
||||
radius: root.realRounding
|
||||
visible: screencopy.item?.hasContent ?? false
|
||||
visible: screencopy.hasContent
|
||||
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
@@ -338,39 +376,44 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
CustomRect {
|
||||
id: border
|
||||
|
||||
border.color: Colors.palette.m3primary
|
||||
border.width: root.realBorderWidth
|
||||
color: "transparent"
|
||||
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
|
||||
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
|
||||
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
|
||||
visible: screencopy.item?.hasContent ?? false
|
||||
visible: screencopy.hasContent
|
||||
x: selectionRect.x - root.realBorderWidth
|
||||
y: selectionRect.y - root.realBorderWidth
|
||||
|
||||
Behavior on border.color {
|
||||
Anim {
|
||||
}
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
SelectionHandles {
|
||||
id: handles
|
||||
|
||||
border: root.realBorderWidth
|
||||
selH: root.sh
|
||||
selW: root.sw
|
||||
selX: root.rsx
|
||||
selY: root.rsy
|
||||
selection: selectionRect
|
||||
tool: root.tool
|
||||
visible: root.mode === "edit" && root.tool === "move"
|
||||
|
||||
onBoundsChanged: (x, y, w, h) => {
|
||||
root.sx = x;
|
||||
root.sy = y;
|
||||
root.ex = x + w;
|
||||
root.ey = y + h;
|
||||
let nx = Math.max(0, x);
|
||||
let ny = Math.max(0, y);
|
||||
let nw = Math.min(w, root.screen.width - nx);
|
||||
let nh = Math.min(h, root.screen.height - ny);
|
||||
root.sx = nx;
|
||||
root.sy = ny;
|
||||
root.ex = nx + nw;
|
||||
root.ey = ny + nh;
|
||||
}
|
||||
onMoveBy: (dx, dy) => {
|
||||
const w = root.sw, h = root.sh;
|
||||
@@ -401,7 +444,4 @@ Item {
|
||||
onToolSelected: t => root.tool = t
|
||||
onUndoRequested: annotations.undo()
|
||||
}
|
||||
|
||||
component ExAnim: Anim {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ Item {
|
||||
|
||||
property color inkColor: "#ff3b30"
|
||||
property real inkWidth: 4
|
||||
required property Rectangle selection
|
||||
property string tool: "none"
|
||||
|
||||
signal changed
|
||||
|
||||
function clearAll() {
|
||||
committedCanvas.clear();
|
||||
previewCanvas.clear();
|
||||
canvas.clear();
|
||||
changed();
|
||||
}
|
||||
|
||||
@@ -54,90 +54,85 @@ Item {
|
||||
}
|
||||
|
||||
function undo() {
|
||||
committedCanvas.undoLastGroup();
|
||||
canvas.undoLastGroup();
|
||||
changed();
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
enabled: tool !== "none"
|
||||
|
||||
StrokeCanvas {
|
||||
id: committedCanvas
|
||||
id: canvas
|
||||
|
||||
anchors.fill: parent
|
||||
penColor: root.inkColor
|
||||
penWidth: root.inkWidth
|
||||
}
|
||||
|
||||
StrokeCanvas {
|
||||
id: previewCanvas
|
||||
|
||||
anchors.fill: parent
|
||||
penColor: root.inkColor
|
||||
penWidth: root.inkWidth
|
||||
}
|
||||
|
||||
PointHandler {
|
||||
MouseArea {
|
||||
id: drawHandler
|
||||
|
||||
property real startX
|
||||
property real startY
|
||||
property bool started: false
|
||||
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||
enabled: root.tool !== "none"
|
||||
anchors.fill: parent
|
||||
cursorShape: undefined
|
||||
enabled: root.enabled
|
||||
|
||||
onActiveChanged: {
|
||||
if (active)
|
||||
onPositionChanged: e => {
|
||||
const x = e.x;
|
||||
const y = e.y;
|
||||
|
||||
if (!pressed)
|
||||
return;
|
||||
|
||||
if (root.tool === "pen") {
|
||||
committedCanvas.endStroke();
|
||||
} else if (started) {
|
||||
const pts = root.sampleShape(root.tool, startX, startY, point.position.x, point.position.y);
|
||||
if (pts.length > 0) {
|
||||
committedCanvas.beginStroke(pts[0][0], pts[0][1], true);
|
||||
for (let i = 1; i < pts.length; i++)
|
||||
committedCanvas.appendPoint(pts[i][0], pts[i][1]);
|
||||
committedCanvas.endStroke();
|
||||
}
|
||||
previewCanvas.clear();
|
||||
}
|
||||
started = false;
|
||||
root.changed();
|
||||
}
|
||||
onPointChanged: {
|
||||
if (!active)
|
||||
canvas.appendPoint(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (point.pressedButtons & Qt.RightButton) {
|
||||
root.clearAll();
|
||||
return;
|
||||
const pts = root.sampleShape(root.tool, startX, startY, x, y);
|
||||
if (pts.length > 0) {
|
||||
canvas.beginStroke(pts[0][0], pts[0][1], true);
|
||||
for (let i = 1; i < pts.length; i++)
|
||||
canvas.appendPoint(pts[i][0], pts[i][1]);
|
||||
}
|
||||
}
|
||||
onPressed: e => {
|
||||
if (e.button & Qt.LeftButton) {
|
||||
startX = e.x;
|
||||
startY = e.y;
|
||||
canvas.beginStroke(e.x, e.y);
|
||||
} else if (e.button & Qt.RightButton) {
|
||||
root.clearAll();
|
||||
}
|
||||
}
|
||||
onPressedChanged: if (!pressed)
|
||||
canvas.endStroke()
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
cursorShape: {
|
||||
if (root.tool === "none")
|
||||
return undefined;
|
||||
|
||||
const x = point.position.x;
|
||||
const y = point.position.y;
|
||||
const withinXWidth = x > root.selection.x && x < root.selection.x + root.selection.implicitWidth;
|
||||
const withinYHeight = y > root.selection.y && y < root.selection.y + root.selection.implicitHeight;
|
||||
|
||||
if (root.tool === "pen") {
|
||||
if (!started) {
|
||||
started = true;
|
||||
committedCanvas.beginStroke(x, y);
|
||||
return;
|
||||
}
|
||||
committedCanvas.appendPoint(x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!started) {
|
||||
started = true;
|
||||
startX = point.pressPosition.x;
|
||||
startY = point.pressPosition.y;
|
||||
}
|
||||
const pts = root.sampleShape(root.tool, startX, startY, x, y);
|
||||
if (pts.length > 0) {
|
||||
previewCanvas.beginStroke(pts[0][0], pts[0][1], true);
|
||||
for (let i = 1; i < pts.length; i++)
|
||||
previewCanvas.appendPoint(pts[i][0], pts[i][1]);
|
||||
if (withinXWidth && withinYHeight) {
|
||||
if (!drawHandler.pressed)
|
||||
canvas.showHover(x, y);
|
||||
else
|
||||
canvas.showHover(drawHandler.mouseX, drawHandler.mouseY);
|
||||
return Qt.BlankCursor;
|
||||
} else {
|
||||
canvas.hideHover();
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
enabled: root.enabled
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ CustomRect {
|
||||
required property var modelData
|
||||
|
||||
color: root.tool === modelData.id ? "#3584e4" : "transparent"
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: tool.modelData.glyph
|
||||
inactiveColor: root.tool === modelData.id ? Colors.palette.m3primary : "transparent"
|
||||
inactiveOnColor: root.tool === modelData.id ? Colors.palette.m3onPrimary : Colors.palette.m3onSurface
|
||||
@@ -103,7 +103,7 @@ CustomRect {
|
||||
required property color modelData
|
||||
|
||||
fillWidth: false
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: ""
|
||||
implicitHeight: buttonSize
|
||||
implicitWidth: buttonSize
|
||||
@@ -128,7 +128,7 @@ CustomRect {
|
||||
spacing: Tokens.spacing.extraSmall
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "undo"
|
||||
inactiveColor: "transparent"
|
||||
inactiveOnColor: Colors.palette.m3onSurface
|
||||
@@ -140,7 +140,7 @@ CustomRect {
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "delete_history"
|
||||
inactiveColor: "transparent"
|
||||
inactiveOnColor: Colors.palette.m3onSurface
|
||||
@@ -152,7 +152,7 @@ CustomRect {
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "content_copy"
|
||||
inactiveColor: "transparent"
|
||||
inactiveOnColor: Colors.palette.m3onSurface
|
||||
@@ -164,7 +164,7 @@ CustomRect {
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "delete_forever"
|
||||
inactiveColor: "transparent"
|
||||
inactiveOnColor: Colors.palette.m3onSurface
|
||||
@@ -176,7 +176,7 @@ CustomRect {
|
||||
}
|
||||
|
||||
IconButton {
|
||||
font.pointSize: Tokens.font.size.large
|
||||
font: Tokens.font.icon.medium
|
||||
icon: "check"
|
||||
inactiveColor: "transparent"
|
||||
inactiveOnColor: Colors.palette.m3onSurface
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import ZShell.Internal
|
||||
import qs.Services
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property color handleBorder: "#3584e4"
|
||||
property color handleFill: "white"
|
||||
required property int border
|
||||
property color handleBorder: Colors.palette.m3primary
|
||||
property color handleFill: Colors.palette.m3onPrimary
|
||||
property real hs: 12
|
||||
property real minSize: 24
|
||||
required property real selH
|
||||
required property real selW
|
||||
required property real selX
|
||||
required property real selY
|
||||
required property Rectangle selection
|
||||
required property string tool
|
||||
|
||||
signal boundsChanged(x: real, y: real, w: real, h: real)
|
||||
signal dragFinished
|
||||
@@ -23,12 +28,16 @@ Item {
|
||||
x: selX
|
||||
y: selY
|
||||
|
||||
Component.onDestruction: Cursor.clearOverride()
|
||||
|
||||
DragHandler {
|
||||
id: moveHandler
|
||||
|
||||
property real accumX: 0
|
||||
property real accumY: 0
|
||||
|
||||
cursorShape: undefined
|
||||
dragThreshold: 0
|
||||
target: null
|
||||
|
||||
onActiveChanged: {
|
||||
@@ -45,51 +54,63 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
cursorShape: {
|
||||
if (root.tool !== "move")
|
||||
return undefined;
|
||||
|
||||
if ((moveHandler.centroid.pressedButtons & Qt.LeftButton) && moveHandler.active)
|
||||
return Qt.ClosedHandCursor;
|
||||
else
|
||||
return Qt.OpenHandCursor;
|
||||
}
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "tl"
|
||||
x: -root.hs / 2
|
||||
y: -root.hs / 2
|
||||
x: -(root.hs + root.border) / 2
|
||||
y: -(root.hs + root.border) / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "t"
|
||||
x: root.width / 2 - root.hs / 2
|
||||
y: -root.hs / 2
|
||||
y: -(root.hs + root.border) / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "tr"
|
||||
x: root.width - root.hs / 2
|
||||
y: -root.hs / 2
|
||||
x: root.width - root.hs / 2 + root.border / 2
|
||||
y: -(root.hs + root.border) / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "r"
|
||||
x: root.width - root.hs / 2
|
||||
x: root.width - root.hs / 2 + root.border / 2
|
||||
y: root.height / 2 - root.hs / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "br"
|
||||
x: root.width - root.hs / 2
|
||||
y: root.height - root.hs / 2
|
||||
x: root.width - root.hs / 2 + root.border / 2
|
||||
y: root.height - root.hs / 2 + root.border / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "b"
|
||||
x: root.width / 2 - root.hs / 2
|
||||
y: root.height - root.hs / 2
|
||||
y: root.height - root.hs / 2 + root.border / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "bl"
|
||||
x: -root.hs / 2
|
||||
y: root.height - root.hs / 2
|
||||
x: -(root.hs + root.border) / 2
|
||||
y: root.height - root.hs / 2 + root.border / 2
|
||||
}
|
||||
|
||||
Handle {
|
||||
edge: "l"
|
||||
x: -root.hs / 2
|
||||
x: -(root.hs + root.border) / 2
|
||||
y: root.height / 2 - root.hs / 2
|
||||
}
|
||||
|
||||
@@ -107,6 +128,8 @@ Item {
|
||||
z: 10
|
||||
|
||||
HoverHandler {
|
||||
id: hover
|
||||
|
||||
cursorShape: {
|
||||
switch (handle.edge) {
|
||||
case "tl":
|
||||
@@ -132,6 +155,7 @@ Item {
|
||||
property real startX
|
||||
property real startY
|
||||
|
||||
dragThreshold: 0
|
||||
grabPermissions: PointerHandler.CanTakeOverFromAnything | PointerHandler.TakeOverForbidden
|
||||
target: null
|
||||
|
||||
@@ -141,8 +165,10 @@ Item {
|
||||
startY = root.y;
|
||||
startW = root.width;
|
||||
startH = root.height;
|
||||
Cursor.setOverride(hover.cursorShape);
|
||||
} else {
|
||||
root.dragFinished();
|
||||
Cursor.clearOverride();
|
||||
}
|
||||
}
|
||||
onTranslationChanged: {
|
||||
|
||||
@@ -5,6 +5,15 @@ import Quickshell
|
||||
Singleton {
|
||||
property var _regexCache: ({})
|
||||
|
||||
function percent(value: int): string {
|
||||
// TRANSLATORS: %1 = a number
|
||||
return qsTr("%1%").arg(value);
|
||||
}
|
||||
|
||||
function percentOne(value: real): string {
|
||||
return percent(Math.round(value * 100));
|
||||
}
|
||||
|
||||
function testRegexList(filterList: list<string>, target: string): bool {
|
||||
const regexChecker = /^\^.*\$$/;
|
||||
for (const filter of filterList) {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
property var bars: new Map()
|
||||
property var screens: new Map()
|
||||
|
||||
function getForActive(): PersistentProperties {
|
||||
return screens.get(Hypr.focusedMonitor);
|
||||
}
|
||||
|
||||
function load(screen: ShellScreen, visibilities: var): void {
|
||||
screens.set(Hypr.monitorFor(screen), visibilities);
|
||||
}
|
||||
}
|
||||
+41
-3
@@ -5,7 +5,9 @@ import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
import ZShell.Models
|
||||
import ZShell.Internal
|
||||
import ZShell.Config
|
||||
import qs.Components
|
||||
import qs.Services
|
||||
import qs.Helpers
|
||||
import qs.Paths
|
||||
@@ -38,7 +40,7 @@ Searcher {
|
||||
previewPath = path;
|
||||
showPreview = true;
|
||||
|
||||
if (Config.general.color.schemeGeneration)
|
||||
if (Config.colors.schemeGen)
|
||||
previewColorsProc.running = true;
|
||||
}
|
||||
|
||||
@@ -62,8 +64,8 @@ Searcher {
|
||||
WallpaperPath.currentWallpaperPath = path;
|
||||
Quickshell.screens.forEach(n => setCrop(n.name, Qt.rect(0, 0, 1, 1), 1.0));
|
||||
Quickshell.execDetached(["zshell-cli", "wallpaper", "lockscreen", "--input-image", `${root.actualCurrent}`, "--output-path", `${Paths.state}/lockscreen_bg.png`, "--blur-amount", `${Config.lock.blurAmount}`]);
|
||||
if (Config.general.color.schemeGeneration)
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.general.color.mode}`]);
|
||||
if (Config.colors.schemeGen)
|
||||
Quickshell.execDetached(["zshell-cli", "scheme", "generate", "--image-path", `${root.actualCurrent}`, "--scheme", `${Config.colors.schemeType}`, "--mode", `${Config.colors.mode}`]);
|
||||
}
|
||||
|
||||
function stopPreview(): void {
|
||||
@@ -140,4 +142,40 @@ Searcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WallpaperService {
|
||||
id: wallpaper
|
||||
|
||||
path: root.current
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
WallpaperObject {
|
||||
readonly property var crops: root.getCrop(modelData.name)
|
||||
required property var modelData
|
||||
|
||||
cropHeight: crops?.height ?? 1.0
|
||||
cropWidth: crops?.width ?? 1.0
|
||||
cropX: crops?.x ?? 0.0
|
||||
cropY: crops?.y ?? 0.0
|
||||
screen: modelData.name
|
||||
service: wallpaper
|
||||
zoom: crops?.zoom ?? 1.0
|
||||
|
||||
Behavior on cropHeight {
|
||||
Anim {}
|
||||
}
|
||||
Behavior on cropWidth {
|
||||
Anim {}
|
||||
}
|
||||
Behavior on cropX {
|
||||
Anim {}
|
||||
}
|
||||
Behavior on cropY {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-7
@@ -3,25 +3,36 @@ pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import ZShell
|
||||
import ZShell.Internal
|
||||
import ZShell.Config
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property var cachedCities: new Map()
|
||||
property var cc
|
||||
property string city
|
||||
readonly property string description: cc?.weatherDesc ?? qsTr("No weather")
|
||||
readonly property string feelsLike: formatTemp(cc?.feelsLikeC)
|
||||
property list<var> forecast
|
||||
property list<var> hourlyForecast
|
||||
readonly property int humidity: cc?.humidity ?? 0
|
||||
readonly property string icon: cc ? Icons.getWeatherIcon(cc.weatherCode) : "cloud_alert"
|
||||
property string loc
|
||||
readonly property string sunrise: cc ? Qt.formatDateTime(new Date(cc.sunrise), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
|
||||
readonly property string sunset: cc ? Qt.formatDateTime(new Date(cc.sunset), Config.services.useTwelveHourClock ? "h:mm A" : "h:mm") : "--:--"
|
||||
readonly property string temp: formatTemp(cc?.tempC)
|
||||
readonly property real windSpeed: cc?.windSpeed ?? 0
|
||||
readonly property CircularBuffer precipitationForecast: precipitationBuffer
|
||||
|
||||
property var cc
|
||||
property string city
|
||||
property list<var> forecast
|
||||
property list<var> hourlyForecast
|
||||
readonly property int currentHourIndex: {
|
||||
const now = new Date();
|
||||
for (let i = 0; i < hourlyForecast.length; i++) {
|
||||
if (new Date(hourlyForecast[i].timestamp.replace("T", " ")) >= now)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
property string loc
|
||||
|
||||
function fetchCityFromCoords(coords: string): void {
|
||||
if (cachedCities.has(coords)) {
|
||||
@@ -112,11 +123,12 @@ Singleton {
|
||||
forecast = forecastList;
|
||||
|
||||
const hourlyList = [];
|
||||
const now = new Date();
|
||||
const startOfToday = new Date();
|
||||
startOfToday.setHours(0, 0, 0, 0);
|
||||
for (let i = 0; i < json.hourly.time.length; i++) {
|
||||
const time = new Date(json.hourly.time[i].replace("T", " "));
|
||||
|
||||
if (time < now)
|
||||
if (time < startOfToday)
|
||||
continue;
|
||||
|
||||
hourlyList.push({
|
||||
@@ -129,6 +141,10 @@ Singleton {
|
||||
});
|
||||
}
|
||||
hourlyForecast = hourlyList;
|
||||
precipitationBuffer.clear();
|
||||
|
||||
for (const hour of hourlyList)
|
||||
precipitationBuffer.push(hour.precipChance);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -258,6 +274,11 @@ Singleton {
|
||||
|
||||
onLocChanged: fetchWeatherData()
|
||||
|
||||
CircularBuffer {
|
||||
id: precipitationBuffer
|
||||
capacity: 24
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onWeatherLocationChanged(): void {
|
||||
root.reload();
|
||||
|
||||
Reference in New Issue
Block a user