forked from Shinonome/dots-hyprland
Merge branch 'end-4:main' into parallax
This commit is contained in:
@@ -140,7 +140,7 @@ misc {
|
|||||||
disable_hyprland_logo = true
|
disable_hyprland_logo = true
|
||||||
disable_splash_rendering = true
|
disable_splash_rendering = true
|
||||||
vfr = 1
|
vfr = 1
|
||||||
vrr = 1
|
vrr = 0
|
||||||
mouse_move_enables_dpms = true
|
mouse_move_enables_dpms = true
|
||||||
key_press_enables_dpms = true
|
key_press_enables_dpms = true
|
||||||
animate_manual_resizes = false
|
animate_manual_resizes = false
|
||||||
@@ -166,3 +166,6 @@ cursor {
|
|||||||
hotspot_padding = 1
|
hotspot_padding = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xwayland {
|
||||||
|
force_zero_scaling = true
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import QtQuick
|
||||||
|
|
||||||
|
// QtObject that allows stuff to be freely declared inside
|
||||||
|
QtObject {
|
||||||
|
default property list<QtObject> data
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQml
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.services
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
NestableObject {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property string key
|
||||||
|
property alias fetching: fetchProc.running
|
||||||
|
property bool set
|
||||||
|
property var value
|
||||||
|
|
||||||
|
Component.onCompleted: fetch()
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: HyprlandConfig
|
||||||
|
function onReloaded() {
|
||||||
|
root.fetch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch() {
|
||||||
|
fetchProc.command = fetchProc.baseCommand.concat([root.key]);
|
||||||
|
fetchProc.running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setValue(newValue) {
|
||||||
|
HyprlandConfig.set(root.key, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
HyprlandConfig.reset(root.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: fetchProc
|
||||||
|
property list<string> baseCommand: ["hyprctl", "getoption", "-j"]
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
if (text == "no such option")
|
||||||
|
return;
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(text);
|
||||||
|
// Note that the value is returned as "<data type>": <value>
|
||||||
|
// It's the only field that isn't always in the same key so we put it in an else
|
||||||
|
for (const key in obj) {
|
||||||
|
if (key == "option")
|
||||||
|
continue;
|
||||||
|
else if (key == "set")
|
||||||
|
root.set = obj[key];
|
||||||
|
else
|
||||||
|
root.value = obj[key];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`[HyprlandConfigOption] Failed to fetch option "${root.key}":\n - Output: ${text.trim()}\n - Error: ${e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -8,10 +8,10 @@ QuickToggleModel {
|
|||||||
name: Translation.tr("Anti-flashbang")
|
name: Translation.tr("Anti-flashbang")
|
||||||
tooltipText: Translation.tr("Anti-flashbang")
|
tooltipText: Translation.tr("Anti-flashbang")
|
||||||
icon: "flash_off"
|
icon: "flash_off"
|
||||||
toggled: Config.options.light.antiFlashbang.enable
|
toggled: HyprlandAntiFlashbangShader.enabled
|
||||||
|
|
||||||
mainAction: () => {
|
mainAction: () => {
|
||||||
Config.options.light.antiFlashbang.enable = !Config.options.light.antiFlashbang.enable;
|
HyprlandAntiFlashbangShader.toggle()
|
||||||
}
|
}
|
||||||
hasMenu: true
|
hasMenu: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import qs.modules.common.models.hyprland
|
||||||
import qs.services
|
import qs.services
|
||||||
|
|
||||||
QuickToggleModel {
|
QuickToggleModel {
|
||||||
id: root
|
id: root
|
||||||
name: Translation.tr("Game mode")
|
name: Translation.tr("Game mode")
|
||||||
toggled: toggled
|
toggled: !confOpt.value
|
||||||
icon: "gamepad"
|
icon: "gamepad"
|
||||||
|
|
||||||
mainAction: () => {
|
mainAction: () => {
|
||||||
@@ -34,13 +35,11 @@ QuickToggleModel {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Process {
|
|
||||||
id: fetchActiveState
|
HyprlandConfigOption {
|
||||||
running: true
|
id: confOpt
|
||||||
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
|
key: "animations:enabled"
|
||||||
onExited: (exitCode, exitStatus) => {
|
|
||||||
root.toggled = exitCode !== 0; // Inverted because enabled = nonzero exit
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltipText: Translation.tr("Game mode")
|
tooltipText: Translation.tr("Game mode")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setExtraWindowAndGrabFocus(window) {
|
function setExtraWindowAndGrabFocus(window) {
|
||||||
|
if (root.activeMenu && root.activeMenu !== window) {
|
||||||
|
if (typeof root.activeMenu.close === "function")
|
||||||
|
root.activeMenu.close();
|
||||||
|
root.activeMenu = null;
|
||||||
|
}
|
||||||
root.activeMenu = window;
|
root.activeMenu = window;
|
||||||
root.grabFocus();
|
root.grabFocus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
@@ -26,7 +27,11 @@ MouseArea {
|
|||||||
item.activate();
|
item.activate();
|
||||||
break;
|
break;
|
||||||
case Qt.RightButton:
|
case Qt.RightButton:
|
||||||
if (item.hasMenu) menu.open();
|
if (item.hasMenu)
|
||||||
|
if (menu.active && menu.item && typeof menu.item.close === "function")
|
||||||
|
menu.item.close();
|
||||||
|
else
|
||||||
|
menu.open();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
@@ -44,14 +49,16 @@ MouseArea {
|
|||||||
sourceComponent: SysTrayMenu {
|
sourceComponent: SysTrayMenu {
|
||||||
Component.onCompleted: this.open();
|
Component.onCompleted: this.open();
|
||||||
trayItemMenuHandle: root.item.menu
|
trayItemMenuHandle: root.item.menu
|
||||||
|
trayItemId: root.item.id
|
||||||
anchor {
|
anchor {
|
||||||
window: root.QsWindow.window
|
window: root.QsWindow.window
|
||||||
rect.x: root.x + (Config.options.bar.vertical ? 0 : QsWindow.window?.width)
|
item: root
|
||||||
rect.y: root.y + (Config.options.bar.vertical ? QsWindow.window?.height : 0)
|
gravity: Config.options.bar.vertical
|
||||||
rect.height: root.height
|
? (Config.options.bar.bottom ? Edges.Left : Edges.Right)
|
||||||
rect.width: root.width
|
: (Config.options.bar.bottom ? Edges.Top : Edges.Bottom)
|
||||||
edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right)
|
edges: Config.options.bar.vertical
|
||||||
gravity: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right)
|
? (Config.options.bar.bottom ? Edges.Left : Edges.Right)
|
||||||
|
: (Config.options.bar.bottom ? Edges.Top : Edges.Bottom)
|
||||||
}
|
}
|
||||||
onMenuOpened: (window) => root.menuOpened(window);
|
onMenuOpened: (window) => root.menuOpened(window);
|
||||||
onMenuClosed: {
|
onMenuClosed: {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import qs.services
|
import qs.services
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.widgets
|
import qs.modules.common.widgets
|
||||||
@@ -9,6 +11,7 @@ import Quickshell
|
|||||||
PopupWindow {
|
PopupWindow {
|
||||||
id: root
|
id: root
|
||||||
required property QsMenuHandle trayItemMenuHandle
|
required property QsMenuHandle trayItemMenuHandle
|
||||||
|
property string trayItemId: ""
|
||||||
property real popupBackgroundMargin: 0
|
property real popupBackgroundMargin: 0
|
||||||
|
|
||||||
signal menuClosed
|
signal menuClosed
|
||||||
@@ -173,6 +176,48 @@ PopupWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RippleButton {
|
||||||
|
id: pinEntry
|
||||||
|
buttonRadius: popupBackground.radius - popupBackground.padding
|
||||||
|
horizontalPadding: 12
|
||||||
|
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2
|
||||||
|
implicitHeight: 36
|
||||||
|
Layout.topMargin: 0
|
||||||
|
Layout.bottomMargin: 0
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
visible: root.trayItemId !== undefined && root.trayItemId.length > 0 && stackView.depth === 1
|
||||||
|
releaseAction: () => TrayService.togglePin(root.trayItemId);
|
||||||
|
|
||||||
|
contentItem: RowLayout {
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: pinEntry.horizontalPadding
|
||||||
|
rightMargin: pinEntry.horizontalPadding
|
||||||
|
}
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
iconSize: 18
|
||||||
|
text: "push_pin"
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: TrayService.isPinned(root.trayItemId) ? Translation.tr("Unpin") : Translation.tr("Pin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
implicitHeight: 1
|
||||||
|
color: Appearance.colors.colSubtext
|
||||||
|
Layout.topMargin: 4
|
||||||
|
Layout.bottomMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: menuEntriesRepeater
|
id: menuEntriesRepeater
|
||||||
|
|||||||
+17
-23
@@ -14,11 +14,14 @@ Item {
|
|||||||
required property color overlayColor
|
required property color overlayColor
|
||||||
property bool showAimLines: Config.options.regionSelector.rect.showAimLines
|
property bool showAimLines: Config.options.regionSelector.rect.showAimLines
|
||||||
|
|
||||||
|
property bool breathingBorderOnly: false
|
||||||
|
|
||||||
// Overlay to darken screen
|
// Overlay to darken screen
|
||||||
// Base dark overlay around region
|
// Base dark overlay around region
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: darkenOverlay
|
id: darkenOverlay
|
||||||
z: 1
|
z: 1
|
||||||
|
visible: !root.breathingBorderOnly
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
top: parent.top
|
top: parent.top
|
||||||
@@ -32,25 +35,6 @@ Item {
|
|||||||
border.width: Math.max(root.width, root.height)
|
border.width: Math.max(root.width, root.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selection border
|
|
||||||
// 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 {
|
DashedBorder {
|
||||||
id: selectionBorder
|
id: selectionBorder
|
||||||
z: 9
|
z: 9
|
||||||
@@ -64,13 +48,23 @@ Item {
|
|||||||
height: Math.round(root.regionHeight) + borderWidth * 2
|
height: Math.round(root.regionHeight) + borderWidth * 2
|
||||||
|
|
||||||
color: root.color
|
color: root.color
|
||||||
dashLength: 6
|
dashLength: 8
|
||||||
gapLength: 3
|
gapLength: 4
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
|
|
||||||
|
// Breathing
|
||||||
|
opacity: 0.9
|
||||||
|
SequentialAnimation on opacity {
|
||||||
|
running: root.breathingBorderOnly
|
||||||
|
loops: Animation.Infinite
|
||||||
|
NumberAnimation { from: 0.9; to: 0.3; duration: 1200; easing.type: Easing.InOutQuad }
|
||||||
|
NumberAnimation { from: 0.3; to: 0.9; duration: 1200; easing.type: Easing.InOutQuad }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
z: 2
|
z: 2
|
||||||
|
visible: !root.breathingBorderOnly
|
||||||
anchors {
|
anchors {
|
||||||
top: selectionBorder.bottom
|
top: selectionBorder.bottom
|
||||||
right: selectionBorder.right
|
right: selectionBorder.right
|
||||||
@@ -82,7 +76,7 @@ Item {
|
|||||||
|
|
||||||
// Coord lines
|
// Coord lines
|
||||||
Rectangle { // Vertical
|
Rectangle { // Vertical
|
||||||
visible: root.showAimLines
|
visible: root.showAimLines && !root.breathingBorderOnly
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
z: 2
|
z: 2
|
||||||
x: root.mouseX
|
x: root.mouseX
|
||||||
@@ -94,7 +88,7 @@ Item {
|
|||||||
color: root.color
|
color: root.color
|
||||||
}
|
}
|
||||||
Rectangle { // Horizontal
|
Rectangle { // Horizontal
|
||||||
visible: root.showAimLines
|
visible: root.showAimLines && !root.breathingBorderOnly
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
z: 2
|
z: 2
|
||||||
y: root.mouseY
|
y: root.mouseY
|
||||||
|
|||||||
@@ -27,13 +27,17 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modes
|
||||||
// TODO: Ask: sidebar AI
|
// TODO: Ask: sidebar AI
|
||||||
enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound }
|
enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound }
|
||||||
enum SelectionMode { RectCorners, Circle }
|
enum SelectionMode { RectCorners, Circle }
|
||||||
|
enum Phase { Select, Post }
|
||||||
property var action: RegionSelection.SnipAction.Copy
|
property var action: RegionSelection.SnipAction.Copy
|
||||||
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
property var selectionMode: RegionSelection.SelectionMode.RectCorners
|
||||||
|
property var phase: RegionSelection.Phase.Select
|
||||||
signal dismiss()
|
signal dismiss()
|
||||||
|
|
||||||
|
// Styles
|
||||||
property string screenshotDir: Directories.screenshotTemp
|
property string screenshotDir: Directories.screenshotTemp
|
||||||
property color overlayColor: ColorUtils.transparentize("#000000", 0.4)
|
property color overlayColor: ColorUtils.transparentize("#000000", 0.4)
|
||||||
property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0
|
property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0
|
||||||
@@ -46,6 +50,10 @@ PanelWindow {
|
|||||||
property color imageBorderColor: brightTertiary
|
property color imageBorderColor: brightTertiary
|
||||||
property color imageFillColor: ColorUtils.transparentize(imageBorderColor, 0.85)
|
property color imageFillColor: ColorUtils.transparentize(imageBorderColor, 0.85)
|
||||||
property color onBorderColor: "#ff000000"
|
property color onBorderColor: "#ff000000"
|
||||||
|
property real targetRegionOpacity: Config.options.regionSelector.targetRegions.opacity
|
||||||
|
property bool contentRegionOpacity: Config.options.regionSelector.targetRegions.contentRegionOpacity
|
||||||
|
|
||||||
|
// Vars for indicators
|
||||||
readonly property var windows: [...HyprlandData.windowList].sort((a, b) => {
|
readonly property var windows: [...HyprlandData.windowList].sort((a, b) => {
|
||||||
// Sort floating=true windows before others
|
// Sort floating=true windows before others
|
||||||
if (a.floating === b.floating) return 0;
|
if (a.floating === b.floating) return 0;
|
||||||
@@ -54,6 +62,7 @@ PanelWindow {
|
|||||||
readonly property var layers: HyprlandData.layers
|
readonly property var layers: HyprlandData.layers
|
||||||
readonly property real falsePositivePreventionRatio: 0.5
|
readonly property real falsePositivePreventionRatio: 0.5
|
||||||
|
|
||||||
|
// Screen & interaction vars
|
||||||
readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(screen)
|
readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(screen)
|
||||||
readonly property real monitorScale: hyprlandMonitor.scale
|
readonly property real monitorScale: hyprlandMonitor.scale
|
||||||
readonly property real monitorOffsetX: hyprlandMonitor.x
|
readonly property real monitorOffsetX: hyprlandMonitor.x
|
||||||
@@ -105,13 +114,13 @@ PanelWindow {
|
|||||||
return offsetAdjustedLayers;
|
return offsetAdjustedLayers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Config
|
||||||
property bool isCircleSelection: (root.selectionMode === RegionSelection.SelectionMode.Circle)
|
property bool isCircleSelection: (root.selectionMode === RegionSelection.SelectionMode.Circle)
|
||||||
property bool enableWindowRegions: Config.options.regionSelector.targetRegions.windows && !isCircleSelection
|
property bool enableWindowRegions: Config.options.regionSelector.targetRegions.windows && !isCircleSelection
|
||||||
property bool enableLayerRegions: Config.options.regionSelector.targetRegions.layers && !isCircleSelection
|
property bool enableLayerRegions: Config.options.regionSelector.targetRegions.layers && !isCircleSelection
|
||||||
property bool enableContentRegions: Config.options.regionSelector.targetRegions.content
|
property bool enableContentRegions: Config.options.regionSelector.targetRegions.content
|
||||||
property real targetRegionOpacity: Config.options.regionSelector.targetRegions.opacity
|
|
||||||
property bool contentRegionOpacity: Config.options.regionSelector.targetRegions.contentRegionOpacity
|
|
||||||
|
|
||||||
|
// Target
|
||||||
property real targetedRegionX: -1
|
property real targetedRegionX: -1
|
||||||
property real targetedRegionY: -1
|
property real targetedRegionY: -1
|
||||||
property real targetedRegionWidth: 0
|
property real targetedRegionWidth: 0
|
||||||
@@ -175,6 +184,7 @@ PanelWindow {
|
|||||||
property real regionX: Math.min(dragStartX, draggingX)
|
property real regionX: Math.min(dragStartX, draggingX)
|
||||||
property real regionY: Math.min(dragStartY, draggingY)
|
property real regionY: Math.min(dragStartY, draggingY)
|
||||||
|
|
||||||
|
// Screenshot stuff
|
||||||
TempScreenshotProcess {
|
TempScreenshotProcess {
|
||||||
id: screenshotProc
|
id: screenshotProc
|
||||||
running: true
|
running: true
|
||||||
@@ -247,6 +257,7 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execution after selection
|
||||||
function snip() {
|
function snip() {
|
||||||
// Validity check
|
// Validity check
|
||||||
if (root.regionWidth <= 0 || root.regionHeight <= 0) {
|
if (root.regionWidth <= 0 || root.regionHeight <= 0) {
|
||||||
@@ -277,21 +288,27 @@ PanelWindow {
|
|||||||
screenshotAction, //
|
screenshotAction, //
|
||||||
screenshotDir
|
screenshotDir
|
||||||
)
|
)
|
||||||
snipProc.command = command;
|
Quickshell.execDetached(command);
|
||||||
|
if (root.action == RegionSelection.SnipAction.Record || root.action == RegionSelection.SnipAction.RecordWithSound) {
|
||||||
// Image post-processing
|
root.phase = RegionSelection.Phase.Post
|
||||||
snipProc.startDetached();
|
} else {
|
||||||
root.dismiss();
|
root.dismiss();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
// Only clickable in Selection phase
|
||||||
id: snipProc
|
mask: Region {
|
||||||
|
item: switch(root.phase) {
|
||||||
|
case RegionSelection.Phase.Select: return mouseArea;
|
||||||
|
case RegionSelection.Phase.Post: return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreencopyView {
|
ScreencopyView { // For freezing
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
live: false
|
live: false
|
||||||
captureSource: root.screen
|
captureSource: root.screen
|
||||||
|
visible: root.phase === RegionSelection.Phase.Select
|
||||||
|
|
||||||
focus: root.visible
|
focus: root.visible
|
||||||
Keys.onPressed: (event) => { // Esc to close
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
@@ -299,220 +316,242 @@ PanelWindow {
|
|||||||
root.dismiss();
|
root.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mouseArea
|
id: mouseArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.CrossCursor
|
cursorShape: Qt.CrossCursor
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
onPressed: (mouse) => {
|
onPressed: (mouse) => {
|
||||||
root.dragStartX = mouse.x;
|
root.dragStartX = mouse.x;
|
||||||
root.dragStartY = mouse.y;
|
root.dragStartY = mouse.y;
|
||||||
root.draggingX = mouse.x;
|
root.draggingX = mouse.x;
|
||||||
root.draggingY = mouse.y;
|
root.draggingY = mouse.y;
|
||||||
root.dragging = true;
|
root.dragging = true;
|
||||||
root.mouseButton = mouse.button;
|
root.mouseButton = mouse.button;
|
||||||
}
|
|
||||||
onReleased: (mouse) => {
|
|
||||||
// Detect if it was a click -> Try to select targeted region
|
|
||||||
if (root.draggingX === root.dragStartX && root.draggingY === root.dragStartY) {
|
|
||||||
if (root.targetedRegionValid()) {
|
|
||||||
root.setRegionToTargeted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Circle dragging?
|
|
||||||
else if (root.selectionMode === RegionSelection.SelectionMode.Circle) {
|
|
||||||
const padding = Config.options.regionSelector.circle.padding + Config.options.regionSelector.circle.strokeWidth / 2;
|
|
||||||
const dragPoints = (root.points.length > 0) ? root.points : [{ x: mouseArea.mouseX, y: mouseArea.mouseY }];
|
|
||||||
const maxX = Math.max(...dragPoints.map(p => p.x));
|
|
||||||
const minX = Math.min(...dragPoints.map(p => p.x));
|
|
||||||
const maxY = Math.max(...dragPoints.map(p => p.y));
|
|
||||||
const minY = Math.min(...dragPoints.map(p => p.y));
|
|
||||||
root.regionX = minX - padding;
|
|
||||||
root.regionY = minY - padding;
|
|
||||||
root.regionWidth = maxX - minX + padding * 2;
|
|
||||||
root.regionHeight = maxY - minY + padding * 2;
|
|
||||||
}
|
|
||||||
root.snip();
|
|
||||||
}
|
|
||||||
onPositionChanged: (mouse) => {
|
|
||||||
root.updateTargetedRegion(mouse.x, mouse.y);
|
|
||||||
if (!root.dragging) return;
|
|
||||||
root.draggingX = mouse.x;
|
|
||||||
root.draggingY = mouse.y;
|
|
||||||
root.dragDiffX = mouse.x - root.dragStartX;
|
|
||||||
root.dragDiffY = mouse.y - root.dragStartY;
|
|
||||||
root.points.push({ x: mouse.x, y: mouse.y });
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
z: 2
|
|
||||||
anchors.fill: parent
|
|
||||||
active: root.selectionMode === RegionSelection.SelectionMode.RectCorners
|
|
||||||
sourceComponent: RectCornersSelectionDetails {
|
|
||||||
regionX: root.regionX
|
|
||||||
regionY: root.regionY
|
|
||||||
regionWidth: root.regionWidth
|
|
||||||
regionHeight: root.regionHeight
|
|
||||||
mouseX: mouseArea.mouseX
|
|
||||||
mouseY: mouseArea.mouseY
|
|
||||||
color: root.selectionBorderColor
|
|
||||||
overlayColor: root.overlayColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loader {
|
|
||||||
z: 2
|
|
||||||
anchors.fill: parent
|
|
||||||
active: root.selectionMode === RegionSelection.SelectionMode.Circle
|
|
||||||
sourceComponent: CircleSelectionDetails {
|
|
||||||
color: root.selectionBorderColor
|
|
||||||
overlayColor: root.overlayColor
|
|
||||||
points: root.points
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
values: root.enableWindowRegions ? root.windowRegions : []
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 2
|
|
||||||
required property var modelData
|
|
||||||
clientDimensions: modelData
|
|
||||||
showIcon: true
|
|
||||||
targeted: !root.draggedAway &&
|
|
||||||
(root.targetedRegionX === modelData.at[0]
|
|
||||||
&& root.targetedRegionY === modelData.at[1]
|
|
||||||
&& root.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& root.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: root.draggedAway ? 0 : root.targetRegionOpacity
|
|
||||||
borderColor: root.windowBorderColor
|
|
||||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
|
||||||
text: `${modelData.class}`
|
|
||||||
radius: Appearance.rounding.windowRounding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layer regions
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.enableLayerRegions ? root.layerRegions : []
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 3
|
|
||||||
required property var modelData
|
|
||||||
clientDimensions: modelData
|
|
||||||
targeted: !root.draggedAway &&
|
|
||||||
(root.targetedRegionX === modelData.at[0]
|
|
||||||
&& root.targetedRegionY === modelData.at[1]
|
|
||||||
&& root.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& root.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: root.draggedAway ? 0 : root.targetRegionOpacity
|
|
||||||
borderColor: root.windowBorderColor
|
|
||||||
fillColor: targeted ? root.windowFillColor : "transparent"
|
|
||||||
text: `${modelData.namespace}`
|
|
||||||
radius: Appearance.rounding.windowRounding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content regions
|
|
||||||
Repeater {
|
|
||||||
model: ScriptModel {
|
|
||||||
values: root.enableContentRegions ? root.imageRegions : []
|
|
||||||
}
|
|
||||||
delegate: TargetRegion {
|
|
||||||
z: 4
|
|
||||||
required property var modelData
|
|
||||||
clientDimensions: modelData
|
|
||||||
targeted: !root.draggedAway &&
|
|
||||||
(root.targetedRegionX === modelData.at[0]
|
|
||||||
&& root.targetedRegionY === modelData.at[1]
|
|
||||||
&& root.targetedRegionWidth === modelData.size[0]
|
|
||||||
&& root.targetedRegionHeight === modelData.size[1])
|
|
||||||
|
|
||||||
opacity: root.draggedAway ? 0 : root.contentRegionOpacity
|
|
||||||
borderColor: root.imageBorderColor
|
|
||||||
fillColor: targeted ? root.imageFillColor : "transparent"
|
|
||||||
text: Translation.tr("Content region")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Controls
|
|
||||||
Row {
|
|
||||||
id: regionSelectionControls
|
|
||||||
z: 10
|
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
|
||||||
bottom: parent.bottom
|
|
||||||
bottomMargin: -height
|
|
||||||
}
|
|
||||||
opacity: 0
|
|
||||||
Connections {
|
|
||||||
target: root
|
|
||||||
function onVisibleChanged() {
|
|
||||||
if (!visible) return;
|
|
||||||
regionSelectionControls.anchors.bottomMargin = 8;
|
|
||||||
regionSelectionControls.opacity = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on opacity {
|
|
||||||
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
Behavior on anchors.bottomMargin {
|
|
||||||
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
|
||||||
}
|
|
||||||
spacing: 6
|
|
||||||
|
|
||||||
OptionsToolbar {
|
|
||||||
Synchronizer on action {
|
|
||||||
property alias source: root.action
|
|
||||||
}
|
|
||||||
Synchronizer on selectionMode {
|
|
||||||
property alias source: root.selectionMode
|
|
||||||
}
|
|
||||||
onDismiss: root.dismiss();
|
|
||||||
}
|
|
||||||
Item {
|
|
||||||
anchors {
|
|
||||||
verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
implicitWidth: closeFab.implicitWidth
|
|
||||||
implicitHeight: closeFab.implicitHeight
|
|
||||||
StyledRectangularShadow {
|
|
||||||
target: closeFab
|
|
||||||
radius: closeFab.buttonRadius
|
|
||||||
}
|
|
||||||
FloatingActionButton {
|
|
||||||
id: closeFab
|
|
||||||
baseSize: 48
|
|
||||||
iconText: "close"
|
|
||||||
onClicked: root.dismiss();
|
|
||||||
StyledToolTip {
|
|
||||||
text: Translation.tr("Close")
|
|
||||||
}
|
|
||||||
colBackground: Appearance.colors.colTertiaryContainer
|
|
||||||
colBackgroundHover: Appearance.colors.colTertiaryContainerHover
|
|
||||||
colRipple: Appearance.colors.colTertiaryContainerActive
|
|
||||||
colOnBackground: Appearance.colors.colOnTertiaryContainer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
onReleased: (mouse) => {
|
||||||
|
// Detect if it was a click -> Try to select targeted region
|
||||||
|
if (root.draggingX === root.dragStartX && root.draggingY === root.dragStartY) {
|
||||||
|
if (root.targetedRegionValid()) {
|
||||||
|
root.setRegionToTargeted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Circle dragging?
|
||||||
|
else if (root.selectionMode === RegionSelection.SelectionMode.Circle) {
|
||||||
|
const padding = Config.options.regionSelector.circle.padding + Config.options.regionSelector.circle.strokeWidth / 2;
|
||||||
|
const dragPoints = (root.points.length > 0) ? root.points : [{ x: mouseArea.mouseX, y: mouseArea.mouseY }];
|
||||||
|
const maxX = Math.max(...dragPoints.map(p => p.x));
|
||||||
|
const minX = Math.min(...dragPoints.map(p => p.x));
|
||||||
|
const maxY = Math.max(...dragPoints.map(p => p.y));
|
||||||
|
const minY = Math.min(...dragPoints.map(p => p.y));
|
||||||
|
root.regionX = minX - padding;
|
||||||
|
root.regionY = minY - padding;
|
||||||
|
root.regionWidth = maxX - minX + padding * 2;
|
||||||
|
root.regionHeight = maxY - minY + padding * 2;
|
||||||
|
}
|
||||||
|
root.snip();
|
||||||
|
}
|
||||||
|
onPositionChanged: (mouse) => {
|
||||||
|
root.updateTargetedRegion(mouse.x, mouse.y);
|
||||||
|
if (!root.dragging) return;
|
||||||
|
root.draggingX = mouse.x;
|
||||||
|
root.draggingY = mouse.y;
|
||||||
|
root.dragDiffX = mouse.x - root.dragStartX;
|
||||||
|
root.dragDiffY = mouse.y - root.dragStartY;
|
||||||
|
root.points.push({ x: mouse.x, y: mouse.y });
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
z: 2
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.selectionMode === RegionSelection.SelectionMode.RectCorners
|
||||||
|
sourceComponent: RectCornersSelectionDetails {
|
||||||
|
regionX: root.regionX
|
||||||
|
regionY: root.regionY
|
||||||
|
regionWidth: root.regionWidth
|
||||||
|
regionHeight: root.regionHeight
|
||||||
|
mouseX: mouseArea.mouseX
|
||||||
|
mouseY: mouseArea.mouseY
|
||||||
|
color: root.selectionBorderColor
|
||||||
|
overlayColor: root.overlayColor
|
||||||
|
breathingBorderOnly: root.phase === RegionSelection.Phase.Post
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
z: 2
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.selectionMode === RegionSelection.SelectionMode.Circle
|
||||||
|
sourceComponent: CircleSelectionDetails {
|
||||||
|
color: root.selectionBorderColor
|
||||||
|
overlayColor: root.overlayColor
|
||||||
|
points: root.points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thing to the bottom-right with an icon
|
||||||
|
CursorGuide {
|
||||||
|
z: 9999
|
||||||
|
visible: root.phase === RegionSelection.Phase.Select
|
||||||
|
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 {
|
||||||
|
values: {
|
||||||
|
if (root.phase === RegionSelection.Phase.Select && root.enableWindowRegions) {
|
||||||
|
return root.windowRegions
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: TargetRegion {
|
||||||
|
z: 2
|
||||||
|
required property var modelData
|
||||||
|
clientDimensions: modelData
|
||||||
|
showIcon: true
|
||||||
|
targeted: !root.draggedAway && //
|
||||||
|
(root.targetedRegionX === modelData.at[0] //
|
||||||
|
&& root.targetedRegionY === modelData.at[1] //
|
||||||
|
&& root.targetedRegionWidth === modelData.size[0] //
|
||||||
|
&& root.targetedRegionHeight === modelData.size[1])
|
||||||
|
|
||||||
|
opacity: root.draggedAway ? 0 : root.targetRegionOpacity
|
||||||
|
borderColor: root.windowBorderColor
|
||||||
|
fillColor: targeted ? root.windowFillColor : "transparent"
|
||||||
|
text: `${modelData.class}`
|
||||||
|
radius: Appearance.rounding.windowRounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layer regions
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: {
|
||||||
|
if (root.phase === RegionSelection.Phase.Select && root.enableLayerRegions) {
|
||||||
|
return root.layerRegions
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: TargetRegion {
|
||||||
|
z: 3
|
||||||
|
required property var modelData
|
||||||
|
clientDimensions: modelData
|
||||||
|
targeted: !root.draggedAway &&
|
||||||
|
(root.targetedRegionX === modelData.at[0]
|
||||||
|
&& root.targetedRegionY === modelData.at[1]
|
||||||
|
&& root.targetedRegionWidth === modelData.size[0]
|
||||||
|
&& root.targetedRegionHeight === modelData.size[1])
|
||||||
|
|
||||||
|
opacity: root.draggedAway ? 0 : root.targetRegionOpacity
|
||||||
|
borderColor: root.windowBorderColor
|
||||||
|
fillColor: targeted ? root.windowFillColor : "transparent"
|
||||||
|
text: `${modelData.namespace}`
|
||||||
|
radius: Appearance.rounding.windowRounding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content regions
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: {
|
||||||
|
if (root.phase === RegionSelection.Phase.Select && root.enableContentRegions) {
|
||||||
|
return root.imageRegions
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delegate: TargetRegion {
|
||||||
|
z: 4
|
||||||
|
required property var modelData
|
||||||
|
clientDimensions: modelData
|
||||||
|
targeted: !root.draggedAway &&
|
||||||
|
(root.targetedRegionX === modelData.at[0]
|
||||||
|
&& root.targetedRegionY === modelData.at[1]
|
||||||
|
&& root.targetedRegionWidth === modelData.size[0]
|
||||||
|
&& root.targetedRegionHeight === modelData.size[1])
|
||||||
|
|
||||||
|
opacity: root.draggedAway ? 0 : root.contentRegionOpacity
|
||||||
|
borderColor: root.imageBorderColor
|
||||||
|
fillColor: targeted ? root.imageFillColor : "transparent"
|
||||||
|
text: Translation.tr("Content region")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
Row {
|
||||||
|
id: regionSelectionControls
|
||||||
|
z: 10
|
||||||
|
visible: root.phase === RegionSelection.Phase.Select
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: -height
|
||||||
|
}
|
||||||
|
opacity: 0
|
||||||
|
Connections {
|
||||||
|
target: root
|
||||||
|
function onVisibleChanged() {
|
||||||
|
if (!visible) return;
|
||||||
|
regionSelectionControls.anchors.bottomMargin = 8;
|
||||||
|
regionSelectionControls.opacity = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
Behavior on anchors.bottomMargin {
|
||||||
|
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
OptionsToolbar {
|
||||||
|
Synchronizer on action {
|
||||||
|
property alias source: root.action
|
||||||
|
}
|
||||||
|
Synchronizer on selectionMode {
|
||||||
|
property alias source: root.selectionMode
|
||||||
|
}
|
||||||
|
onDismiss: root.dismiss();
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
implicitWidth: closeFab.implicitWidth
|
||||||
|
implicitHeight: closeFab.implicitHeight
|
||||||
|
StyledRectangularShadow {
|
||||||
|
target: closeFab
|
||||||
|
radius: closeFab.buttonRadius
|
||||||
|
}
|
||||||
|
FloatingActionButton {
|
||||||
|
id: closeFab
|
||||||
|
baseSize: 48
|
||||||
|
iconText: "close"
|
||||||
|
onClicked: root.dismiss();
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("Close")
|
||||||
|
}
|
||||||
|
colBackground: Appearance.colors.colTertiaryContainer
|
||||||
|
colBackgroundHover: Appearance.colors.colTertiaryContainerHover
|
||||||
|
colRipple: Appearance.colors.colTertiaryContainerActive
|
||||||
|
colOnBackground: Appearance.colors.colOnTertiaryContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,12 +65,16 @@ Scope {
|
|||||||
function record() {
|
function record() {
|
||||||
root.action = RegionSelection.SnipAction.Record
|
root.action = RegionSelection.SnipAction.Record
|
||||||
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||||
|
// If already open then re-trigger to stop recording
|
||||||
|
if (GlobalStates.regionSelectorOpen) GlobalStates.regionSelectorOpen = false
|
||||||
GlobalStates.regionSelectorOpen = true
|
GlobalStates.regionSelectorOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function recordWithSound() {
|
function recordWithSound() {
|
||||||
root.action = RegionSelection.SnipAction.RecordWithSound
|
root.action = RegionSelection.SnipAction.RecordWithSound
|
||||||
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
root.selectionMode = RegionSelection.SelectionMode.RectCorners
|
||||||
|
// If already open then re-trigger to stop recording
|
||||||
|
if (GlobalStates.regionSelectorOpen) GlobalStates.regionSelectorOpen = false
|
||||||
GlobalStates.regionSelectorOpen = true
|
GlobalStates.regionSelectorOpen = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import qs.modules.common
|
|
||||||
import qs.modules.common.widgets
|
|
||||||
import qs.modules.common.functions
|
|
||||||
import qs.services
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell.Hyprland
|
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
|
||||||
RippleButton {
|
RippleButton {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ WindowDialog {
|
|||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
buttonIcon: "lightbulb"
|
buttonIcon: "check"
|
||||||
text: Translation.tr("Enable now")
|
text: Translation.tr("Enable now")
|
||||||
checked: Hyprsunset.active
|
checked: Hyprsunset.active
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
@@ -102,14 +102,32 @@ WindowDialog {
|
|||||||
right: parent.right
|
right: parent.right
|
||||||
}
|
}
|
||||||
iconSize: Appearance.font.pixelSize.larger
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
buttonIcon: "flash_off"
|
buttonIcon: "filter"
|
||||||
text: Translation.tr("Enable")
|
text: Translation.tr("Content adjustment")
|
||||||
|
checked: HyprlandAntiFlashbangShader.enabled
|
||||||
|
onCheckedChanged: {
|
||||||
|
if (checked) HyprlandAntiFlashbangShader.enable()
|
||||||
|
else HyprlandAntiFlashbangShader.disable()
|
||||||
|
}
|
||||||
|
StyledToolTip {
|
||||||
|
text: Translation.tr("<b>Dims screen content</b> as needed.<br><br>Pros: Immediately responsive<br>Cons: Expensive and can hurt color accuracy<br><br><i>Uses a Hyprland screen shader</i>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigSwitch {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
iconSize: Appearance.font.pixelSize.larger
|
||||||
|
buttonIcon: "light_mode"
|
||||||
|
text: Translation.tr("Brightness adjustment")
|
||||||
checked: Config.options.light.antiFlashbang.enable
|
checked: Config.options.light.antiFlashbang.enable
|
||||||
onCheckedChanged: {
|
onCheckedChanged: {
|
||||||
Config.options.light.antiFlashbang.enable = checked;
|
Config.options.light.antiFlashbang.enable = checked;
|
||||||
}
|
}
|
||||||
StyledToolTip {
|
StyledToolTip {
|
||||||
text: Translation.tr("Example use case: eroge on one workspace, dark Discord window on another")
|
text: Translation.tr("Adapts the <b>display (physical screen) brightness</b><br><br>Pros: Less expensive, retains colors<br>Cons: Not immediately responsive<br><br><i>Adjusts display brightness after each Hyprland IPC event</i>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
def edit_hyprland_config(file_path, set_args, reset_args):
|
def edit_hyprland_config(file_path, set_args, reset_args):
|
||||||
try:
|
try:
|
||||||
@@ -54,8 +55,19 @@ def edit_hyprland_config(file_path, set_args, reset_args):
|
|||||||
new_lines[-1] += '\n'
|
new_lines[-1] += '\n'
|
||||||
new_lines.append(f"{key} = {value}\n")
|
new_lines.append(f"{key} = {value}\n")
|
||||||
|
|
||||||
with open(file_path, 'w') as file:
|
dir_name = os.path.dirname(os.path.abspath(file_path))
|
||||||
file.writelines(new_lines)
|
temp_path = None
|
||||||
|
try:
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', dir=dir_name, delete=False) as temp_file:
|
||||||
|
temp_file.writelines(new_lines)
|
||||||
|
temp_path = temp_file.name
|
||||||
|
os.chmod(temp_path, os.stat(file_path).st_mode)
|
||||||
|
os.replace(temp_path, file_path)
|
||||||
|
except Exception as e:
|
||||||
|
if temp_path and os.path.exists(temp_path):
|
||||||
|
os.remove(temp_path)
|
||||||
|
print(f"Error saving file: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
for key in reset_set:
|
for key in reset_set:
|
||||||
print(f"Removed '{key}' from '{file_path}'")
|
print(f"Removed '{key}' from '{file_path}'")
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
import qs.modules.common.models.hyprland
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property string shaderPath: Quickshell.shellPath("services/hyprlandAntiFlashbangShader/anti-flashbang.glsl")
|
||||||
|
property bool enabled: confOpt.value == shaderPath
|
||||||
|
|
||||||
|
function enable() {
|
||||||
|
HyprlandConfig.setMany({
|
||||||
|
"decoration:screen_shader": root.shaderPath,
|
||||||
|
"debug:damage_tracking": 1, // Turn off dmg tracking to prevent weird flashes. 1 = monitor only
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function disable() {
|
||||||
|
HyprlandConfig.resetMany([
|
||||||
|
"decoration:screen_shader",
|
||||||
|
"debug:damage_tracking"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
if (root.enabled) disable()
|
||||||
|
else enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
HyprlandConfigOption {
|
||||||
|
id: confOpt
|
||||||
|
key: "decoration:screen_shader"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Hyprland
|
||||||
|
|
||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.common.functions
|
import qs.modules.common.functions
|
||||||
@@ -14,6 +14,8 @@ import qs.modules.common.functions
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
signal reloaded()
|
||||||
|
|
||||||
readonly property string configuratorScriptPath: Quickshell.shellPath("scripts/hyprland/hyprconfigurator.py")
|
readonly property string configuratorScriptPath: Quickshell.shellPath("scripts/hyprland/hyprconfigurator.py")
|
||||||
readonly property string shellOverridesPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/hyprland/shellOverrides/main.conf`)
|
readonly property string shellOverridesPath: FileUtils.trimFileProtocol(`${Directories.config}/hypr/hyprland/shellOverrides/main.conf`)
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ Singleton {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetMany(keys: var) {
|
function resetMany(keys: list<string>) {
|
||||||
let args = ""
|
let args = ""
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
args += `--reset "${keys[i]}" `
|
args += `--reset "${keys[i]}" `
|
||||||
@@ -48,4 +50,14 @@ Singleton {
|
|||||||
`${root.configuratorScriptPath} --file ${root.shellOverridesPath} ${args}` //
|
`${root.configuratorScriptPath} --file ${root.shellOverridesPath} ${args}` //
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Hyprland
|
||||||
|
|
||||||
|
function onRawEvent(event) {
|
||||||
|
if (event.name == "configreloaded") {
|
||||||
|
root.reloaded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ Singleton {
|
|||||||
function unpin(itemId) {
|
function unpin(itemId) {
|
||||||
Config.options.tray.pinnedItems = Config.options.tray.pinnedItems.filter(id => id !== itemId);
|
Config.options.tray.pinnedItems = Config.options.tray.pinnedItems.filter(id => id !== itemId);
|
||||||
}
|
}
|
||||||
|
function isPinned(itemId) {
|
||||||
|
for (var i = 0; i < root.pinnedItems.length; i++) {
|
||||||
|
if (root.pinnedItems[i].id === itemId)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function togglePin(itemId) {
|
function togglePin(itemId) {
|
||||||
var pins = Config.options.tray.pinnedItems;
|
var pins = Config.options.tray.pinnedItems;
|
||||||
if (pins.includes(itemId)) {
|
if (pins.includes(itemId)) {
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#version 300 es
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
in vec2 v_texcoord;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
float overlayOpacityForBrightness(float x) {
|
||||||
|
// Note: range 0 to 1
|
||||||
|
|
||||||
|
// Will a fancy curve help?... I'll have to experiment more at night
|
||||||
|
// float y = pow(x, 2.0) * 0.75;
|
||||||
|
// float y = (1.0 - exp(-x))*1.15;
|
||||||
|
// float y = (1.0 - exp(-pow((x-0.15), 0.6)))*1.18;
|
||||||
|
float y = x*0.75;
|
||||||
|
|
||||||
|
return min(max(y, 0.001), 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// 1. Get the current pixel color
|
||||||
|
vec4 pixColor = texture(tex, v_texcoord);
|
||||||
|
|
||||||
|
// 2. Calculate average screen brightness
|
||||||
|
vec3 totalRGB = vec3(0.0);
|
||||||
|
float samples = 0.0;
|
||||||
|
|
||||||
|
// We use a nested loop to create a 10x10 grid (100 samples)
|
||||||
|
// This is dense enough to catch small icons/text but light enough to run fast.
|
||||||
|
for(float x = 0.05; x < 1.0; x += 0.1) {
|
||||||
|
for(float y = 0.05; y < 1.0; y += 0.1) {
|
||||||
|
totalRGB += texture(tex, vec2(x, y)).rgb;
|
||||||
|
samples++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 avgColor = totalRGB / samples;
|
||||||
|
float globalBrightness = dot(avgColor, vec3(0.2126, 0.7152, 0.0722));
|
||||||
|
|
||||||
|
// 3. Get the specific opacity for this brightness level
|
||||||
|
float opacity = overlayOpacityForBrightness(globalBrightness);
|
||||||
|
|
||||||
|
// 4. Apply the "black overlay" effect
|
||||||
|
vec3 outColor = mix(pixColor.rgb, vec3(0.0), opacity);
|
||||||
|
|
||||||
|
fragColor = vec4(outColor, pixColor.a);
|
||||||
|
}
|
||||||
@@ -604,5 +604,7 @@
|
|||||||
"Recognize music": "Recognize music",
|
"Recognize music": "Recognize music",
|
||||||
"Stroke width": "Stroke width",
|
"Stroke width": "Stroke width",
|
||||||
"Use varying shapes for password characters": "Use varying shapes for password characters",
|
"Use varying shapes for password characters": "Use varying shapes for password characters",
|
||||||
"Battery full": "Battery full"
|
"Battery full": "Battery full",
|
||||||
|
"Pin": "Pin",
|
||||||
|
"Unpin": "Unpin"
|
||||||
}
|
}
|
||||||
@@ -718,5 +718,7 @@
|
|||||||
"No applications": "没有应用",
|
"No applications": "没有应用",
|
||||||
"Creativity": "创意",
|
"Creativity": "创意",
|
||||||
"Move left": "左移",
|
"Move left": "左移",
|
||||||
"Pin to Start": "固定到“开始”屏幕"
|
"Pin to Start": "固定到“开始”屏幕",
|
||||||
|
"Pin": "固定",
|
||||||
|
"Unpin": "取消固定"
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
_commit='6e17efab83d3a5ad5d6e59bc08d26095c6660502'
|
_commit='7511545ee20664e3b8b8d3322c0ffe7567c56f7a'
|
||||||
# Useful links:
|
# Useful links:
|
||||||
# https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master
|
# https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master
|
||||||
# https://aur.archlinux.org/packages/quickshell-git
|
# https://aur.archlinux.org/packages/quickshell-git
|
||||||
@@ -16,16 +16,16 @@ url='https://git.outfoxxed.me/quickshell/quickshell'
|
|||||||
options=(!strip)
|
options=(!strip)
|
||||||
license=('LGPL-3.0-only')
|
license=('LGPL-3.0-only')
|
||||||
depends=(
|
depends=(
|
||||||
|
'cpptrace'
|
||||||
|
'jemalloc'
|
||||||
|
'mesa'
|
||||||
'qt6-declarative'
|
'qt6-declarative'
|
||||||
'qt6-base'
|
'qt6-base'
|
||||||
'jemalloc'
|
|
||||||
'qt6-svg'
|
'qt6-svg'
|
||||||
|
'libdrm'
|
||||||
'libpipewire'
|
'libpipewire'
|
||||||
'libxcb'
|
'libxcb'
|
||||||
'wayland'
|
'wayland'
|
||||||
'libdrm'
|
|
||||||
'mesa'
|
|
||||||
'google-breakpad'
|
|
||||||
# NOTE: Below are custom dependencies of illogical-impulse
|
# NOTE: Below are custom dependencies of illogical-impulse
|
||||||
qt6-5compat
|
qt6-5compat
|
||||||
qt6-avif-image-plugin
|
qt6-avif-image-plugin
|
||||||
@@ -44,15 +44,15 @@ depends=(
|
|||||||
syntax-highlighting
|
syntax-highlighting
|
||||||
)
|
)
|
||||||
makedepends=(
|
makedepends=(
|
||||||
'spirv-tools'
|
|
||||||
'qt6-shadertools'
|
|
||||||
'wayland'
|
|
||||||
'wayland-protocols'
|
|
||||||
'cli11'
|
'cli11'
|
||||||
'ninja'
|
|
||||||
'cmake'
|
'cmake'
|
||||||
'git'
|
'git'
|
||||||
|
'ninja'
|
||||||
|
'qt6-shadertools'
|
||||||
|
'spirv-tools'
|
||||||
'vulkan-headers'
|
'vulkan-headers'
|
||||||
|
'wayland'
|
||||||
|
'wayland-protocols'
|
||||||
)
|
)
|
||||||
provides=("$_pkgname")
|
provides=("$_pkgname")
|
||||||
conflicts=("$_pkgname")
|
conflicts=("$_pkgname")
|
||||||
|
|||||||
Reference in New Issue
Block a user