forked from Shinonome/dots-hyprland
add overlay
This commit is contained in:
@@ -33,7 +33,7 @@ bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle #
|
|||||||
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet
|
||||||
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard
|
||||||
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls
|
||||||
bind = Super, G, global, quickshell:crosshairToggle # Toggle crosshair
|
bind = Super, G, global, quickshell:overlayToggle # Toggle overlay
|
||||||
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu
|
||||||
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar
|
||||||
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback)
|
||||||
|
|||||||
@@ -134,11 +134,11 @@ layerrule = blur, quickshell:.*
|
|||||||
layerrule = ignorealpha 0.79, quickshell:.*
|
layerrule = ignorealpha 0.79, quickshell:.*
|
||||||
layerrule = animation slide, quickshell:bar
|
layerrule = animation slide, quickshell:bar
|
||||||
layerrule = animation slide bottom, quickshell:cheatsheet
|
layerrule = animation slide bottom, quickshell:cheatsheet
|
||||||
layerrule = noanim, quickshell:crosshair
|
|
||||||
layerrule = animation slide bottom, quickshell:dock
|
layerrule = animation slide bottom, quickshell:dock
|
||||||
layerrule = animation popin 120%, quickshell:screenCorners
|
layerrule = animation popin 120%, quickshell:screenCorners
|
||||||
layerrule = noanim, quickshell:lockWindowPusher
|
layerrule = noanim, quickshell:lockWindowPusher
|
||||||
layerrule = animation fade, quickshell:notificationPopup
|
layerrule = animation fade, quickshell:notificationPopup
|
||||||
|
layerrule = noanim, quickshell:overlay
|
||||||
layerrule = noanim, quickshell:overview
|
layerrule = noanim, quickshell:overview
|
||||||
layerrule = animation slide bottom, quickshell:osk
|
layerrule = animation slide bottom, quickshell:osk
|
||||||
layerrule = noanim, quickshell:polkit
|
layerrule = noanim, quickshell:polkit
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Singleton {
|
|||||||
property bool osdBrightnessOpen: false
|
property bool osdBrightnessOpen: false
|
||||||
property bool osdVolumeOpen: false
|
property bool osdVolumeOpen: false
|
||||||
property bool oskOpen: false
|
property bool oskOpen: false
|
||||||
|
property bool overlayOpen: false
|
||||||
property bool overviewOpen: false
|
property bool overviewOpen: false
|
||||||
property bool regionSelectorOpen: false
|
property bool regionSelectorOpen: false
|
||||||
property bool screenLocked: false
|
property bool screenLocked: false
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ AbstractWidget {
|
|||||||
draggable: placementStrategy === "free"
|
draggable: placementStrategy === "free"
|
||||||
onReleased: {
|
onReleased: {
|
||||||
root.targetX = root.x;
|
root.targetX = root.x;
|
||||||
root.targetY = root.y;
|
root.targetY = root.y;
|
||||||
configEntry.x = root.targetX;
|
configEntry.x = root.targetX;
|
||||||
configEntry.y = root.targetY ;
|
configEntry.y = root.targetY;
|
||||||
}
|
}
|
||||||
|
|
||||||
property bool needsColText: false
|
property bool needsColText: false
|
||||||
|
|||||||
@@ -79,6 +79,22 @@ Singleton {
|
|||||||
property bool inhibit: false
|
property bool inhibit: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property JsonObject overlay: JsonObject {
|
||||||
|
property list<string> open: ["crosshair"]
|
||||||
|
property JsonObject crosshair: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: true
|
||||||
|
property real x: 100
|
||||||
|
property real y: 100
|
||||||
|
}
|
||||||
|
property JsonObject volumeMixer: JsonObject {
|
||||||
|
property bool pinned: false
|
||||||
|
property bool clickthrough: false
|
||||||
|
property real x: 55
|
||||||
|
property real y: 188
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
property JsonObject timer: JsonObject {
|
property JsonObject timer: JsonObject {
|
||||||
property JsonObject pomodoro: JsonObject {
|
property JsonObject pomodoro: JsonObject {
|
||||||
property bool running: false
|
property bool running: false
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract widgets for an overlay. Doesn't contain any visuals.
|
||||||
|
*/
|
||||||
|
AbstractWidget {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool pinned: false // Whether to stay visible when the overlay is dismissed
|
||||||
|
property bool clickthrough: true // When pinned, whether to allow clicks go through
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
Item {
|
MouseArea {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// uh this is stupid turns out we don't need anything here
|
// uh this is stupid turns out we don't need anything here
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
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: "crosshair"
|
|
||||||
|
|
||||||
function toggle(): void {
|
|
||||||
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GlobalShortcut {
|
|
||||||
name: "crosshairToggle"
|
|
||||||
description: "Toggles crosshair on press"
|
|
||||||
|
|
||||||
onPressed: {
|
|
||||||
GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
property Component regionComponent: Component {
|
||||||
|
Region {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: overlayLoader
|
||||||
|
active: GlobalStates.overlayOpen || OverlayContext.hasPinnedWidgets
|
||||||
|
sourceComponent: PanelWindow {
|
||||||
|
id: overlayWindow
|
||||||
|
exclusionMode: ExclusionMode.Ignore
|
||||||
|
WlrLayershell.namespace: "quickshell:overlay"
|
||||||
|
WlrLayershell.layer: WlrLayer.Overlay
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
visible: true
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
mask: Region {
|
||||||
|
item: GlobalStates.overlayOpen ? overlayContent : null
|
||||||
|
regions: OverlayContext.clickableWidgets.map((widget) => regionComponent.createObject(this, {
|
||||||
|
item: widget
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
bottom: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
|
||||||
|
OverlayContent {
|
||||||
|
id: overlayContent
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpcHandler {
|
||||||
|
target: "overlay"
|
||||||
|
|
||||||
|
function toggle(): void {
|
||||||
|
GlobalStates.overlayOpen = !GlobalStates.overlayOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcut {
|
||||||
|
name: "overlayToggle"
|
||||||
|
description: "Toggles overlay on press"
|
||||||
|
|
||||||
|
onPressed: {
|
||||||
|
GlobalStates.overlayOpen = !GlobalStates.overlayOpen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.widgets.widgetCanvas
|
||||||
|
|
||||||
|
import qs.modules.overlay.crosshair
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true
|
||||||
|
|
||||||
|
Keys.onPressed: (event) => { // Esc to close
|
||||||
|
if (event.key === Qt.Key_Escape) {
|
||||||
|
GlobalStates.overlayOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property real initScale: 1.08
|
||||||
|
scale: initScale
|
||||||
|
Component.onCompleted: {
|
||||||
|
scale = 1
|
||||||
|
}
|
||||||
|
Behavior on scale {
|
||||||
|
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: bg
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Appearance.colors.colScrim
|
||||||
|
opacity: (GlobalStates.overlayOpen && root.scale !== initScale) ? 1 : 0
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WidgetCanvas {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: GlobalStates.overlayOpen = false
|
||||||
|
|
||||||
|
OverlayTaskbar {
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: Persistent.states.overlay.open.map(identifier => {
|
||||||
|
return OverlayContext.availableWidgets.find(w => w.identifier === identifier);
|
||||||
|
})
|
||||||
|
objectProp: "identifier"
|
||||||
|
}
|
||||||
|
delegate: OverlayWidgetDelegateChooser {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
pragma Singleton
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<var> availableWidgets: [
|
||||||
|
{ identifier: "crosshair", materialSymbol: "point_scan" },
|
||||||
|
{ identifier: "volumeMixer", materialSymbol: "volume_up" }
|
||||||
|
]
|
||||||
|
|
||||||
|
readonly property bool hasPinnedWidgets: root.pinnedWidgetIdentifiers.length > 0
|
||||||
|
|
||||||
|
property list<string> pinnedWidgetIdentifiers: []
|
||||||
|
property list<var> clickableWidgets: []
|
||||||
|
|
||||||
|
function pin(identifier: string, pin = true) {
|
||||||
|
if (pin) {
|
||||||
|
if (!root.pinnedWidgetIdentifiers.includes(identifier)) {
|
||||||
|
root.pinnedWidgetIdentifiers.push(identifier)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.pinnedWidgetIdentifiers = root.pinnedWidgetIdentifiers.filter(id => id !== identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerClickableWidget(widget: var, clickable = true) {
|
||||||
|
if (clickable) {
|
||||||
|
if (!root.clickableWidgets.includes(widget)) {
|
||||||
|
root.clickableWidgets.push(widget)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root.clickableWidgets = root.clickableWidgets.filter(w => w !== widget)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.widgets.widgetCanvas
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property real padding: 8
|
||||||
|
|
||||||
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
|
implicitWidth: contentRow.implicitWidth + (padding * 2)
|
||||||
|
implicitHeight: contentRow.implicitHeight + (padding * 2)
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
radius: Appearance.rounding.large
|
||||||
|
border.color: Appearance.colors.colOutlineVariant
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: contentRow
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: root.padding
|
||||||
|
}
|
||||||
|
spacing: 6
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: 4
|
||||||
|
Repeater {
|
||||||
|
model: ScriptModel {
|
||||||
|
values: OverlayContext.availableWidgets
|
||||||
|
}
|
||||||
|
delegate: WidgetButton {
|
||||||
|
required property var modelData
|
||||||
|
identifier: modelData.identifier
|
||||||
|
materialSymbol: modelData.materialSymbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
TimeWidget {}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Separator: Rectangle {
|
||||||
|
implicitWidth: 1
|
||||||
|
color: Appearance.colors.colOutlineVariant
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.topMargin: 10
|
||||||
|
Layout.bottomMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
component TimeWidget: StyledText {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.leftMargin: 8
|
||||||
|
Layout.rightMargin: 6
|
||||||
|
|
||||||
|
text: DateTime.time
|
||||||
|
font {
|
||||||
|
family: Appearance.font.family.numbers
|
||||||
|
variableAxes: Appearance.font.variableAxes.numbers
|
||||||
|
pixelSize: 22
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component WidgetButton: RippleButton {
|
||||||
|
id: widgetButton
|
||||||
|
required property string identifier
|
||||||
|
required property string materialSymbol
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
|
toggled: Persistent.states.overlay.open.includes(identifier)
|
||||||
|
onClicked: {
|
||||||
|
if (widgetButton.toggled) {
|
||||||
|
Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== identifier);
|
||||||
|
} else {
|
||||||
|
Persistent.states.overlay.open.push(identifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
|
||||||
|
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||||
|
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||||
|
|
||||||
|
buttonRadius: root.radius - (root.height - height) / 2
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: 32
|
||||||
|
implicitHeight: 32
|
||||||
|
MaterialSymbol {
|
||||||
|
id: iconWidget
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 24
|
||||||
|
text: widgetButton.materialSymbol
|
||||||
|
color: widgetButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurfaceVariant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import qs.services
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Bluetooth
|
||||||
|
import qs.modules.overlay.crosshair
|
||||||
|
import qs.modules.overlay.volumeMixer
|
||||||
|
|
||||||
|
DelegateChooser {
|
||||||
|
id: root
|
||||||
|
role: "identifier"
|
||||||
|
|
||||||
|
DelegateChoice { roleValue: "crosshair"; Crosshair {} }
|
||||||
|
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import Qt5Compat.GraphicalEffects
|
||||||
|
import qs
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.common.functions
|
||||||
|
import qs.modules.common.widgets
|
||||||
|
import qs.modules.common.widgets.widgetCanvas
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To make an overlay widget:
|
||||||
|
* 1. Create a modules/overlay/<yourWidget>/<YourWidget>.qml, using this as the base class
|
||||||
|
* 2. Add an entry to OverlayContext.availableWidgets with identifier=<yourWidgetIdentifier>
|
||||||
|
* 3. Add an entry in Persistent.states.overlay.<yourWidgetIdentifier> with x, y, pinned, clickthrough properties set to reasonable defaults
|
||||||
|
* 4. Add an entry in OverlayWidgetDelegateChooser with roleValue=<yourWidgetIdentifier> and Declare your widget in there
|
||||||
|
* Use existing entries as reference.
|
||||||
|
*/
|
||||||
|
AbstractOverlayWidget {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
required property Item contentItem
|
||||||
|
|
||||||
|
readonly property string identifier: modelData.identifier
|
||||||
|
readonly property string materialSymbol: modelData.materialSymbol ?? "widgets"
|
||||||
|
property string title: identifier.replace(/([A-Z])/g, " $1").replace(/^./, function(str){ return str.toUpperCase(); })
|
||||||
|
property var persistentStateEntry: Persistent.states.overlay[identifier]
|
||||||
|
property real radius: Appearance.rounding.windowRounding
|
||||||
|
property real minWidth: 250
|
||||||
|
|
||||||
|
draggable: GlobalStates.overlayOpen
|
||||||
|
x: Math.round(persistentStateEntry.x) // Round or it'll be blurry
|
||||||
|
y: Math.round(persistentStateEntry.y) // Round or it'll be blurry
|
||||||
|
pinned: persistentStateEntry.pinned
|
||||||
|
clickthrough: persistentStateEntry.clickthrough
|
||||||
|
drag {
|
||||||
|
minimumX: 0
|
||||||
|
minimumY: 0
|
||||||
|
maximumX: root.parent.width - root.width
|
||||||
|
maximumY: root.parent.height - root.height
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guarded states & registration funcs
|
||||||
|
property bool open: Persistent.states.overlay.open
|
||||||
|
property bool actuallyPinned: pinned && open
|
||||||
|
property bool actuallyClickable: !clickthrough && actuallyPinned && open
|
||||||
|
onActuallyPinnedChanged: reportPinnedState();
|
||||||
|
onActuallyClickableChanged: reportClickableState();
|
||||||
|
function reportPinnedState() {
|
||||||
|
OverlayContext.pin(identifier, actuallyPinned);
|
||||||
|
}
|
||||||
|
function reportClickableState() {
|
||||||
|
OverlayContext.registerClickableWidget(contentItem, actuallyClickable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Self-registeration with OverlayContext
|
||||||
|
Component.onCompleted: {
|
||||||
|
reportPinnedState();
|
||||||
|
reportClickableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks
|
||||||
|
onReleased: savePosition();
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
Persistent.states.overlay.open = Persistent.states.overlay.open.filter(type => type !== root.identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
function togglePinned() {
|
||||||
|
persistentStateEntry.pinned = !persistentStateEntry.pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleClickthrough() {
|
||||||
|
persistentStateEntry.clickthrough = !persistentStateEntry.clickthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
function savePosition(xPos = root.x, yPos = root.y) {
|
||||||
|
persistentStateEntry.x = xPos;
|
||||||
|
persistentStateEntry.y = yPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
function center() {
|
||||||
|
const targetX = (root.parent.width - contentColumn.width) / 2
|
||||||
|
const targetY = (root.parent.height - contentItem.height) / 2 - titleBar.implicitHeight
|
||||||
|
root.x = targetX
|
||||||
|
root.y = targetY
|
||||||
|
root.savePosition(targetX, targetY)
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: GlobalStates.overlayOpen || actuallyPinned
|
||||||
|
implicitWidth: Math.max(contentColumn.implicitWidth, minWidth)
|
||||||
|
implicitHeight: contentColumn.implicitHeight
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: border
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "transparent"
|
||||||
|
radius: root.radius
|
||||||
|
border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
layer.enabled: GlobalStates.overlayOpen
|
||||||
|
layer.effect: OpacityMask {
|
||||||
|
maskSource: Rectangle {
|
||||||
|
width: border.width
|
||||||
|
height: border.height
|
||||||
|
radius: root.radius
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: contentColumn
|
||||||
|
z: -1
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
Rectangle {
|
||||||
|
id: titleBar
|
||||||
|
opacity: GlobalStates.overlayOpen ? 1 : 0
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
property real padding: 2
|
||||||
|
implicitWidth: titleBarRow.implicitWidth + padding * 2
|
||||||
|
implicitHeight: titleBarRow.implicitHeight + padding * 2
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
border.color: Appearance.colors.colOutlineVariant
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: titleBarRow
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: titleBar.padding
|
||||||
|
leftMargin: titleBar.padding + 8
|
||||||
|
}
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
text: root.materialSymbol
|
||||||
|
iconSize: 20
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.rightMargin: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.title
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "recenter"
|
||||||
|
onClicked: root.center()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Center"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "mouse"
|
||||||
|
toggled: !root.clickthrough
|
||||||
|
onClicked: root.toggleClickthrough()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Clickable when pinned"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "keep"
|
||||||
|
toggled: root.pinned
|
||||||
|
onClicked: root.togglePinned()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Pin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TitlebarButton {
|
||||||
|
materialSymbol: "close"
|
||||||
|
onClicked: root.close()
|
||||||
|
StyledToolTip {
|
||||||
|
text: "Close"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content
|
||||||
|
Item {
|
||||||
|
id: contentContainer
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
implicitWidth: root.contentItem.implicitWidth
|
||||||
|
implicitHeight: root.contentItem.implicitHeight
|
||||||
|
children: [root.contentItem]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
component TitlebarButton: RippleButton {
|
||||||
|
id: titlebarButton
|
||||||
|
required property string materialSymbol
|
||||||
|
buttonRadius: height / 2
|
||||||
|
implicitHeight: contentItem.implicitHeight
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
padding: 0
|
||||||
|
|
||||||
|
colBackgroundToggled: Appearance.colors.colSecondaryContainer
|
||||||
|
colBackgroundToggledHover: Appearance.colors.colSecondaryContainerHover
|
||||||
|
colRippleToggled: Appearance.colors.colSecondaryContainerActive
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
implicitWidth: 30
|
||||||
|
implicitHeight: 30
|
||||||
|
|
||||||
|
MaterialSymbol {
|
||||||
|
id: iconWidget
|
||||||
|
anchors.centerIn: parent
|
||||||
|
iconSize: 20
|
||||||
|
text: titlebarButton.materialSymbol
|
||||||
|
fill: titlebarButton.toggled
|
||||||
|
color: titlebarButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnSurface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.overlay
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
contentItem: CrosshairContent {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.modules.common
|
||||||
|
import qs.modules.overlay
|
||||||
|
import qs.modules.sidebarRight.volumeMixer
|
||||||
|
|
||||||
|
StyledOverlayWidget {
|
||||||
|
id: root
|
||||||
|
contentItem: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Appearance.m3colors.m3surfaceContainer
|
||||||
|
property real padding: 16
|
||||||
|
implicitHeight: 700
|
||||||
|
implicitWidth: 400
|
||||||
|
|
||||||
|
VolumeDialogContent {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: parent.padding
|
||||||
|
isSink: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ ContentPage {
|
|||||||
Layout.leftMargin: 10
|
Layout.leftMargin: 10
|
||||||
color: Appearance.colors.colSubtext
|
color: Appearance.colors.colSubtext
|
||||||
font.pixelSize: Appearance.font.pixelSize.smallie
|
font.pixelSize: Appearance.font.pixelSize.smallie
|
||||||
text: Translation.tr("Press Super+G to toggle appearance")
|
text: Translation.tr("Press Super+G to open the overlay and pin the crosshair")
|
||||||
}
|
}
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import qs.modules.common
|
|||||||
import qs.modules.background
|
import qs.modules.background
|
||||||
import qs.modules.bar
|
import qs.modules.bar
|
||||||
import qs.modules.cheatsheet
|
import qs.modules.cheatsheet
|
||||||
import qs.modules.crosshair
|
|
||||||
import qs.modules.dock
|
import qs.modules.dock
|
||||||
import qs.modules.lock
|
import qs.modules.lock
|
||||||
import qs.modules.mediaControls
|
import qs.modules.mediaControls
|
||||||
@@ -25,6 +24,7 @@ import qs.modules.screenCorners
|
|||||||
import qs.modules.sessionScreen
|
import qs.modules.sessionScreen
|
||||||
import qs.modules.sidebarLeft
|
import qs.modules.sidebarLeft
|
||||||
import qs.modules.sidebarRight
|
import qs.modules.sidebarRight
|
||||||
|
import qs.modules.overlay
|
||||||
import qs.modules.verticalBar
|
import qs.modules.verticalBar
|
||||||
import qs.modules.wallpaperSelector
|
import qs.modules.wallpaperSelector
|
||||||
|
|
||||||
@@ -39,7 +39,6 @@ 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
|
||||||
@@ -47,6 +46,7 @@ ShellRoot {
|
|||||||
property bool enablePolkit: true
|
property bool enablePolkit: true
|
||||||
property bool enableOnScreenDisplay: true
|
property bool enableOnScreenDisplay: true
|
||||||
property bool enableOnScreenKeyboard: true
|
property bool enableOnScreenKeyboard: true
|
||||||
|
property bool enableOverlay: true
|
||||||
property bool enableOverview: true
|
property bool enableOverview: true
|
||||||
property bool enableRegionSelector: true
|
property bool enableRegionSelector: true
|
||||||
property bool enableReloadPopup: true
|
property bool enableReloadPopup: true
|
||||||
@@ -70,13 +70,13 @@ 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 {} }
|
||||||
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
LazyLoader { active: enableNotificationPopup; component: NotificationPopup {} }
|
||||||
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
LazyLoader { active: enableOnScreenDisplay; component: OnScreenDisplay {} }
|
||||||
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
LazyLoader { active: enableOnScreenKeyboard; component: OnScreenKeyboard {} }
|
||||||
|
LazyLoader { active: enableOverlay; component: Overlay {} }
|
||||||
LazyLoader { active: enableOverview; component: Overview {} }
|
LazyLoader { active: enableOverview; component: Overview {} }
|
||||||
LazyLoader { active: enablePolkit; component: Polkit {} }
|
LazyLoader { active: enablePolkit; component: Polkit {} }
|
||||||
LazyLoader { active: enableRegionSelector; component: RegionSelector {} }
|
LazyLoader { active: enableRegionSelector; component: RegionSelector {} }
|
||||||
|
|||||||
Reference in New Issue
Block a user