forked from Shinonome/dots-hyprland
implement crosshair
This commit is contained in:
@@ -10,6 +10,7 @@ pragma ComponentBehavior: Bound
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
property bool barOpen: true
|
property bool barOpen: true
|
||||||
|
property bool crosshairOpen: false
|
||||||
property bool sidebarLeftOpen: false
|
property bool sidebarLeftOpen: false
|
||||||
property bool sidebarRightOpen: false
|
property bool sidebarRightOpen: false
|
||||||
property bool mediaControlsOpen: false
|
property bool mediaControlsOpen: false
|
||||||
|
|||||||
@@ -215,6 +215,11 @@ Singleton {
|
|||||||
property bool autoKillTrays: false
|
property bool autoKillTrays: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject crosshair: JsonObject {
|
||||||
|
// Valorant crosshair format. Use https://www.vcrdb.net/builder
|
||||||
|
property string code: "0;P;d;1;0l;10;0o;2;1b;0"
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject dock: JsonObject {
|
property JsonObject dock: JsonObject {
|
||||||
property bool enable: false
|
property bool enable: false
|
||||||
property bool monochromeIcons: true
|
property bool monochromeIcons: true
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.services
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
|
Scope {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: crosshairLoader
|
||||||
|
active: GlobalStates.crosshairOpen
|
||||||
|
sourceComponent: PanelWindow {
|
||||||
|
id: crosshairWindow
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
WlrLayershell.namespace: "quickshell:crosshair"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
visible: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
mask: Region { // Crosshair should not block mouse input
|
||||||
|
item: null
|
||||||
|
}
|
||||||
|
|
||||||
|
implicitWidth: crosshairContent.implicitWidth
|
||||||
|
implicitHeight: crosshairContent.implicitHeight
|
||||||
|
|
||||||
|
CrosshairContent {
|
||||||
|
id: crosshairContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "sidebarRight"
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "crosshairToggle"
|
||||||
|
description: "Toggles crosshair on press"
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// Keys to props
|
||||||
|
// f, 0f, 1f, m are irrelevant as they're firing error stuff
|
||||||
|
// 0 is irrelevant because it's some profile stuff
|
||||||
|
property var propertyMap: ({
|
||||||
|
"c": "color",
|
||||||
|
"u": "colorCode",
|
||||||
|
"h": "outline",
|
||||||
|
"o": "outlineOpacity",
|
||||||
|
"t": "outlineThickness",
|
||||||
|
"d": "centerDot",
|
||||||
|
"a": "centerDotOpacity",
|
||||||
|
"z": "centerDotSize",
|
||||||
|
"0a": "innerLineOpacity",
|
||||||
|
"0l": "innerLineLength",
|
||||||
|
"0v": "innerLineVerticalLength",
|
||||||
|
"0g": "innerLineUnbindAxesLengths",
|
||||||
|
"0t": "innerLineThickness",
|
||||||
|
"0o": "innerLineOffset",
|
||||||
|
"1b": "outerLines",
|
||||||
|
"1a": "outerLineOpacity",
|
||||||
|
"1l": "outerLineLength",
|
||||||
|
"1v": "outerLineVerticalLength",
|
||||||
|
"1g": "outerLineUnbindAxesLengths",
|
||||||
|
"1t": "outerLineThickness",
|
||||||
|
"1o": "outerLineOffset",
|
||||||
|
})
|
||||||
|
property var colorMap: ({
|
||||||
|
0: "#FFFFFF",
|
||||||
|
1: "#00FF00",
|
||||||
|
2: "#7FFF00",
|
||||||
|
3: "#DFFF00",
|
||||||
|
4: "#FFFF00",
|
||||||
|
5: "#00FFFF",
|
||||||
|
6: "#FF00FF",
|
||||||
|
7: "#FF0000"
|
||||||
|
})
|
||||||
|
|
||||||
|
// Raw props
|
||||||
|
property int color: 0
|
||||||
|
property string colorCode: "#FFFFFF"
|
||||||
|
property bool outline: true
|
||||||
|
property real outlineOpacity: 0.5
|
||||||
|
property int outlineThickness: 1
|
||||||
|
property bool centerDot: false
|
||||||
|
property real centerDotOpacity: 1
|
||||||
|
property int centerDotSize: 2
|
||||||
|
property bool innerLines: true
|
||||||
|
property real innerLineOpacity: 0.8
|
||||||
|
property int innerLineLength: 6
|
||||||
|
property int innerLineVerticalLength: innerLineLength
|
||||||
|
property bool innerLineUnbindAxesLengths: false
|
||||||
|
property int innerLineThickness: 2
|
||||||
|
property int innerLineOffset: 3
|
||||||
|
property bool outerLines: true
|
||||||
|
property real outerLineOpacity: 0.35
|
||||||
|
property int outerLineLength: 2
|
||||||
|
property int outerLineVerticalLength: outerLineLength
|
||||||
|
property bool outerLineUnbindAxesLengths: false
|
||||||
|
property int outerLineThickness: 2
|
||||||
|
property int outerLineOffset: 10
|
||||||
|
property string defaultCode: "c;0;u;FFFFFF;h;1;o;0.5;t;1;d;0;a;1;z;2;0a;0.8;0l;6;0v;6;0g;0;0t;2;0o;3;1b;1;1a;0.35;1l;2;1v;2;1g;0;1t;2;1o;10"
|
||||||
|
|
||||||
|
function loadFromCode(code: string): void {
|
||||||
|
let args = code.split(";");
|
||||||
|
for (let i = 0; i < args.length; i+= 2) {
|
||||||
|
let key = args[i];
|
||||||
|
let value = args[i+1];
|
||||||
|
let targetKey = root.propertyMap[key];
|
||||||
|
let targetType = typeof root[targetKey];
|
||||||
|
|
||||||
|
if (targetKey === undefined) continue;
|
||||||
|
|
||||||
|
if (targetType === "number") {
|
||||||
|
value = parseFloat(value);
|
||||||
|
} else if (targetType === "boolean") {
|
||||||
|
value = (value === "1");
|
||||||
|
}
|
||||||
|
if (targetKey === "colorCode") {
|
||||||
|
value = "#" + value.slice(0, 6);
|
||||||
|
}
|
||||||
|
root[targetKey] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!root.innerLineUnbindAxesLengths) {
|
||||||
|
root.innerLineVerticalLength = root.innerLineLength;
|
||||||
|
}
|
||||||
|
if (!root.outerLineUnbindAxesLengths) {
|
||||||
|
root.outerLineVerticalLength = root.outerLineLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update values from code
|
||||||
|
property var code: Config.options.crosshair.code
|
||||||
|
Component.onCompleted: reloadFromCode();
|
||||||
|
onCodeChanged: reloadFromCode();
|
||||||
|
function reloadFromCode() {
|
||||||
|
root.loadFromCode(root.defaultCode);
|
||||||
|
root.loadFromCode(root.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregated props
|
||||||
|
property color crosshairColor: {
|
||||||
|
if (colorMap[color] !== undefined) return root.colorMap[color];
|
||||||
|
if (color === 8) return colorCode;
|
||||||
|
return "#FFFFFF";
|
||||||
|
}
|
||||||
|
property int borderWidth: outline ? outlineThickness : 0
|
||||||
|
property color borderColor: ColorUtils.transparentize("black", 1 - root.outlineOpacity)
|
||||||
|
property color innerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.innerLineOpacity)
|
||||||
|
property color outerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.outerLineOpacity)
|
||||||
|
property int innerLineTotalOffset: root.centerDotSize / 2 + 1 + root.innerLineOffset
|
||||||
|
property int outerLineTotalOffset: root.centerDotSize / 2 + 1 + root.outerLineOffset
|
||||||
|
property real centerDotTotalSize: root.centerDotSize + root.borderWidth * 2
|
||||||
|
property real innerLineTotalSize: (innerLineTotalOffset + root.innerLineLength + root.borderWidth) * 2
|
||||||
|
property real outerLineTotalSize: (outerLineTotalOffset + root.outerLineLength + root.borderWidth) * 2
|
||||||
|
implicitWidth: Math.max(centerDotTotalSize, innerLineTotalSize, outerLineTotalSize) + 2 // 2 for pixel correction
|
||||||
|
implicitHeight: implicitWidth
|
||||||
|
// width: implicitWidth
|
||||||
|
// height: implicitHeight
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: centerDot
|
||||||
|
visible: root.centerDot
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
color: root.crosshairColor
|
||||||
|
opacity: root.centerDotOpacity
|
||||||
|
width: centerDotTotalSize
|
||||||
|
height: width
|
||||||
|
|
||||||
|
border.width: root.borderWidth
|
||||||
|
border.color: root.borderColor
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: innerLines
|
||||||
|
model: 4
|
||||||
|
Item {
|
||||||
|
id: innerHair
|
||||||
|
z: index % 2 // Vertical lines above horizontal lines
|
||||||
|
required property int index
|
||||||
|
property int pixelCorrection: (root.innerLineThickness % 2 === 1 && index > 1) ? 1 : 0
|
||||||
|
property int hairLength: (innerHair.index % 2 === 0 ? root.innerLineLength : root.innerLineVerticalLength)
|
||||||
|
visible: root.innerLines && hairLength > 0
|
||||||
|
anchors.fill: parent
|
||||||
|
rotation: index * 90
|
||||||
|
Rectangle {
|
||||||
|
x: parent.width / 2 + root.innerLineTotalOffset - root.borderWidth + innerHair.pixelCorrection
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
|
|
||||||
|
color: root.innerLineColor
|
||||||
|
width: innerHair.hairLength + root.borderWidth * 2
|
||||||
|
height: root.innerLineThickness + root.borderWidth * 2
|
||||||
|
|
||||||
|
border.width: root.borderWidth
|
||||||
|
border.color: root.borderColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: outerLines
|
||||||
|
model: 4
|
||||||
|
Item {
|
||||||
|
id: outerHair
|
||||||
|
z: index % 2 + 2 // Vertical lines above horizontal lines, above inner lines
|
||||||
|
required property int index
|
||||||
|
property int pixelCorrection: (root.outerLineThickness % 2 === 1 && index > 1) ? 1 : 0
|
||||||
|
property int hairLength: (outerHair.index % 2 === 0 ? root.outerLineLength : root.outerLineVerticalLength)
|
||||||
|
visible: root.outerLines && hairLength > 0
|
||||||
|
anchors.fill: parent
|
||||||
|
rotation: index * 90
|
||||||
|
Rectangle {
|
||||||
|
x: parent.width / 2 + root.outerLineTotalOffset - root.borderWidth + outerHair.pixelCorrection
|
||||||
|
y: parent.height / 2 - height / 2
|
||||||
|
|
||||||
|
color: root.outerLineColor
|
||||||
|
width: hairLength + root.borderWidth * 2
|
||||||
|
height: root.outerLineThickness + root.borderWidth * 2
|
||||||
|
|
||||||
|
border.width: root.borderWidth
|
||||||
|
border.color: root.borderColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -61,6 +61,37 @@ ContentPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContentSection {
|
||||||
|
icon: "point_scan"
|
||||||
|
title: Translation.tr("Crosshair")
|
||||||
|
|
||||||
|
MaterialTextArea {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: Translation.tr("Valorant crosshair code")
|
||||||
|
text: Config.options.crosshair.code
|
||||||
|
wrapMode: TextEdit.Wrap
|
||||||
|
onTextChanged: {
|
||||||
|
Config.options.crosshair.code = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
RippleButtonWithIcon {
|
||||||
|
id: editorButton
|
||||||
|
buttonRadius: Appearance.rounding.full
|
||||||
|
materialIcon: "open_in_new"
|
||||||
|
mainText: Translation.tr("Open editor")
|
||||||
|
onClicked: {
|
||||||
|
Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`);
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: "www.vcrdb.net"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ContentSection {
|
ContentSection {
|
||||||
icon: "call_to_action"
|
icon: "call_to_action"
|
||||||
title: Translation.tr("Dock")
|
title: Translation.tr("Dock")
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "./modules/common/"
|
|||||||
import "./modules/background/"
|
import "./modules/background/"
|
||||||
import "./modules/bar/"
|
import "./modules/bar/"
|
||||||
import "./modules/cheatsheet/"
|
import "./modules/cheatsheet/"
|
||||||
|
import "./modules/crosshair/"
|
||||||
import "./modules/dock/"
|
import "./modules/dock/"
|
||||||
import "./modules/lock/"
|
import "./modules/lock/"
|
||||||
import "./modules/mediaControls/"
|
import "./modules/mediaControls/"
|
||||||
@@ -36,6 +37,7 @@ ShellRoot {
|
|||||||
property bool enableBar: true
|
property bool enableBar: true
|
||||||
property bool enableBackground: true
|
property bool enableBackground: true
|
||||||
property bool enableCheatsheet: true
|
property bool enableCheatsheet: true
|
||||||
|
property bool enableCrosshair: true
|
||||||
property bool enableDock: true
|
property bool enableDock: true
|
||||||
property bool enableLock: true
|
property bool enableLock: true
|
||||||
property bool enableMediaControls: true
|
property bool enableMediaControls: true
|
||||||
@@ -64,6 +66,7 @@ ShellRoot {
|
|||||||
LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }
|
LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} }
|
||||||
LazyLoader { active: enableBackground; component: Background {} }
|
LazyLoader { active: enableBackground; component: Background {} }
|
||||||
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
LazyLoader { active: enableCheatsheet; component: Cheatsheet {} }
|
||||||
|
LazyLoader { active: enableCrosshair; component: Crosshair {} }
|
||||||
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} }
|
||||||
LazyLoader { active: enableLock; component: Lock {} }
|
LazyLoader { active: enableLock; component: Lock {} }
|
||||||
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
LazyLoader { active: enableMediaControls; component: MediaControls {} }
|
||||||
|
|||||||
Reference in New Issue
Block a user