feat: window info panel
Also disable reload popup
This commit is contained in:
parent
97c0c387f1
commit
4a86b66d06
13 changed files with 477 additions and 51 deletions
|
|
@ -14,6 +14,7 @@ Singleton {
|
|||
property alias notifs: adapter.notifs
|
||||
property alias osd: adapter.osd
|
||||
property alias session: adapter.session
|
||||
property alias winfo: adapter.winfo
|
||||
property alias paths: adapter.paths
|
||||
|
||||
FileView {
|
||||
|
|
@ -32,6 +33,7 @@ Singleton {
|
|||
property JsonObject notifs: NotifsConfig {}
|
||||
property JsonObject osd: OsdConfig {}
|
||||
property JsonObject session: SessionConfig {}
|
||||
property JsonObject winfo: WInfoConfig {}
|
||||
property JsonObject paths: UserPaths {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
config/WInfoConfig.qml
Normal file
8
config/WInfoConfig.qml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import Quickshell.Io
|
||||
|
||||
JsonObject {
|
||||
property JsonObject sizes: JsonObject {
|
||||
property real heightMult: 0.7
|
||||
property real detailsWidth: 500
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ import QtQuick.Layouts
|
|||
Item {
|
||||
id: root
|
||||
|
||||
required property Item wrapper
|
||||
|
||||
implicitWidth: Hyprland.activeClient ? child.implicitWidth : -Appearance.padding.large * 2
|
||||
implicitHeight: child.implicitHeight
|
||||
|
||||
|
|
@ -65,7 +67,7 @@ Item {
|
|||
radius: Appearance.rounding.normal
|
||||
|
||||
function onClicked(): void {
|
||||
// TODO
|
||||
root.wrapper.detach("winfo");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,15 +13,17 @@ ShapePath {
|
|||
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
|
||||
property real ibr: invertBottomRounding ? -1 : 1
|
||||
|
||||
property real sideRounding: startX > 0 ? -1 : 1
|
||||
|
||||
strokeWidth: -1
|
||||
fillColor: Config.border.colour
|
||||
|
||||
PathArc {
|
||||
relativeX: root.roundingX
|
||||
relativeY: root.rounding
|
||||
relativeY: root.rounding * root.sideRounding
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
direction: root.sideRounding < 0 ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.width - root.roundingX * 2
|
||||
|
|
@ -50,10 +52,10 @@ ShapePath {
|
|||
}
|
||||
PathArc {
|
||||
relativeX: -root.roundingX
|
||||
relativeY: root.rounding
|
||||
relativeY: root.rounding * root.sideRounding
|
||||
radiusX: Math.min(root.rounding, root.wrapper.width)
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
direction: root.sideRounding < 0 ? PathArc.Clockwise : PathArc.Counterclockwise
|
||||
}
|
||||
|
||||
Behavior on fillColor {
|
||||
|
|
@ -71,4 +73,12 @@ ShapePath {
|
|||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on sideRounding {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ import QtQuick
|
|||
Item {
|
||||
id: root
|
||||
|
||||
required property Item wrapper
|
||||
required property ShellScreen screen
|
||||
|
||||
property string currentName
|
||||
property real currentCenter
|
||||
property bool hasCurrent
|
||||
required property string currentName
|
||||
required property real currentCenter
|
||||
required property bool hasCurrent
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
implicitWidth: hasCurrent ? (content.children.find(c => c.shouldBeActive)?.implicitWidth ?? 0) + Appearance.padding.large * 2 : 0
|
||||
implicitWidth: (content.children.find(c => c.shouldBeActive)?.implicitWidth ?? 0) + Appearance.padding.large * 2
|
||||
implicitHeight: (content.children.find(c => c.shouldBeActive)?.implicitHeight ?? 0) + Appearance.padding.large * 2
|
||||
|
||||
Item {
|
||||
|
|
@ -26,11 +26,11 @@ Item {
|
|||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
clip: true
|
||||
|
||||
Popout {
|
||||
name: "activewindow"
|
||||
source: "ActiveWindow.qml"
|
||||
sourceComponent: ActiveWindow {
|
||||
wrapper: root.wrapper
|
||||
}
|
||||
}
|
||||
|
||||
Popout {
|
||||
|
|
@ -85,30 +85,6 @@ Item {
|
|||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.implicitWidth > 0
|
||||
|
||||
Anim {
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on currentCenter {
|
||||
enabled: root.implicitWidth > 0
|
||||
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
||||
component Popout: Loader {
|
||||
id: popout
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import "root:/modules/windowinfo"
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
|
|
@ -8,18 +13,168 @@ Item {
|
|||
|
||||
required property ShellScreen screen
|
||||
|
||||
property alias currentName: content.currentName
|
||||
property alias currentCenter: content.currentCenter
|
||||
property alias hasCurrent: content.hasCurrent
|
||||
readonly property real nonAnimWidth: x > 0 || hasCurrent ? children.find(c => c.shouldBeActive)?.implicitWidth ?? content.implicitWidth : 0
|
||||
readonly property real nonAnimHeight: children.find(c => c.shouldBeActive)?.implicitHeight ?? content.implicitHeight
|
||||
|
||||
property string currentName
|
||||
property real currentCenter
|
||||
property bool hasCurrent
|
||||
|
||||
property string detachedMode
|
||||
readonly property bool isDetached: detachedMode.length > 0
|
||||
|
||||
property int animLength: Appearance.anim.durations.normal
|
||||
property list<real> animCurve: Appearance.anim.curves.emphasized
|
||||
|
||||
function detach(mode: string): void {
|
||||
animLength = Appearance.anim.durations.large;
|
||||
detachedMode = mode;
|
||||
focus = true;
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
hasCurrent = false;
|
||||
animCurve = Appearance.anim.curves.emphasizedAccel;
|
||||
animLength = Appearance.anim.durations.normal;
|
||||
detachedMode = "";
|
||||
animCurve = Appearance.anim.curves.emphasized;
|
||||
}
|
||||
|
||||
visible: width > 0 && height > 0
|
||||
clip: true
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
implicitHeight: nonAnimHeight
|
||||
|
||||
Content {
|
||||
Keys.onEscapePressed: close()
|
||||
|
||||
HyprlandFocusGrab {
|
||||
active: root.isDetached
|
||||
windows: [QsWindow.window]
|
||||
onCleared: root.close()
|
||||
}
|
||||
|
||||
Binding {
|
||||
when: root.isDetached
|
||||
|
||||
target: QsWindow.window
|
||||
property: "WlrLayershell.keyboardFocus"
|
||||
value: WlrKeyboardFocus.OnDemand
|
||||
}
|
||||
|
||||
Comp {
|
||||
id: content
|
||||
|
||||
screen: root.screen
|
||||
shouldBeActive: !root.detachedMode
|
||||
asynchronous: true
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
sourceComponent: Content {
|
||||
wrapper: root
|
||||
screen: root.screen
|
||||
currentName: root.currentName
|
||||
currentCenter: root.currentCenter
|
||||
hasCurrent: root.hasCurrent
|
||||
}
|
||||
}
|
||||
|
||||
Comp {
|
||||
shouldBeActive: root.detachedMode === "winfo"
|
||||
asynchronous: true
|
||||
anchors.centerIn: parent
|
||||
|
||||
sourceComponent: WindowInfo {
|
||||
screen: root.screen
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
Anim {
|
||||
duration: root.animLength
|
||||
easing.bezierCurve: root.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
enabled: root.implicitWidth > 0
|
||||
|
||||
Anim {
|
||||
duration: root.animLength
|
||||
easing.bezierCurve: root.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
Anim {
|
||||
duration: root.animLength
|
||||
easing.bezierCurve: root.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
enabled: root.implicitWidth > 0
|
||||
|
||||
Anim {
|
||||
duration: root.animLength
|
||||
easing.bezierCurve: root.animCurve
|
||||
}
|
||||
}
|
||||
|
||||
component Comp: Loader {
|
||||
id: comp
|
||||
|
||||
property bool shouldBeActive
|
||||
|
||||
asynchronous: true
|
||||
active: false
|
||||
opacity: 0
|
||||
|
||||
states: State {
|
||||
name: "active"
|
||||
when: comp.shouldBeActive
|
||||
|
||||
PropertyChanges {
|
||||
comp.opacity: 1
|
||||
comp.active: true
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "active"
|
||||
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
property: "active"
|
||||
}
|
||||
Anim {
|
||||
property: "opacity"
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "active"
|
||||
to: ""
|
||||
|
||||
SequentialAnimation {
|
||||
Anim {
|
||||
property: "opacity"
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
PropertyAction {
|
||||
property: "active"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
component Anim: NumberAnimation {
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasized
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ Shape {
|
|||
wrapper: panels.popouts
|
||||
invertBottomRounding: wrapper.y + wrapper.height + 1 >= root.height
|
||||
|
||||
startX: 0
|
||||
startY: wrapper.y - rounding
|
||||
startX: wrapper.x
|
||||
startY: wrapper.y - rounding * sideRounding
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,11 +80,13 @@ Item {
|
|||
|
||||
screen: root.screen
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.top
|
||||
anchors.verticalCenterOffset: {
|
||||
const off = root.popouts.currentCenter - Config.border.thickness;
|
||||
const diff = root.height - Math.floor(off + implicitHeight / 2);
|
||||
x: isDetached ? (root.width - nonAnimWidth) / 2 : 0
|
||||
y: {
|
||||
if (isDetached)
|
||||
return (root.height - nonAnimHeight) / 2;
|
||||
|
||||
const off = currentCenter - Config.border.thickness - nonAnimHeight / 2;
|
||||
const diff = root.height - Math.floor(off + nonAnimHeight);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return off;
|
||||
|
|
|
|||
158
modules/windowinfo/Details.qml
Normal file
158
modules/windowinfo/Details.qml
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
Label {
|
||||
Layout.topMargin: Appearance.padding.large * 2
|
||||
|
||||
text: Hyprland.activeClient.title
|
||||
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||
|
||||
font.pointSize: Appearance.font.size.large
|
||||
font.weight: 500
|
||||
}
|
||||
|
||||
Label {
|
||||
text: Hyprland.activeClient.wmClass
|
||||
color: Colours.palette.m3tertiary
|
||||
|
||||
font.pointSize: Appearance.font.size.larger
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 1
|
||||
Layout.leftMargin: Appearance.padding.large * 2
|
||||
Layout.rightMargin: Appearance.padding.large * 2
|
||||
Layout.topMargin: Appearance.spacing.normal
|
||||
Layout.bottomMargin: Appearance.spacing.large
|
||||
|
||||
color: Colours.palette.m3secondary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "location_on"
|
||||
text: qsTr("Address: %1").arg(Hyprland.activeClient.address)
|
||||
color: Colours.palette.m3primary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "location_searching"
|
||||
text: qsTr("Position: %1, %2").arg(Hyprland.activeClient.x).arg(Hyprland.activeClient.y)
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "resize"
|
||||
text: qsTr("Size: %1 x %2").arg(Hyprland.activeClient.width).arg(Hyprland.activeClient.height)
|
||||
color: Colours.palette.m3tertiary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "workspaces"
|
||||
text: qsTr("Workspace: %1 (%2)").arg(Hyprland.activeClient.workspace.name).arg(Hyprland.activeClient.workspace.id)
|
||||
color: Colours.palette.m3secondary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "desktop_windows"
|
||||
text: {
|
||||
const mon = Hyprland.activeClient.monitor;
|
||||
return qsTr("Monitor: %1 (%2) at %3, %4").arg(mon.name).arg(mon.id).arg(mon.x).arg(mon.y);
|
||||
}
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "page_header"
|
||||
text: qsTr("Initial title: %1").arg(Hyprland.activeClient.initialTitle)
|
||||
color: Colours.palette.m3tertiary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "category"
|
||||
text: qsTr("Initial class: %1").arg(Hyprland.activeClient.initialClass)
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "account_tree"
|
||||
text: qsTr("Process id: %1").arg(Hyprland.activeClient.pid)
|
||||
color: Colours.palette.m3primary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "picture_in_picture_center"
|
||||
text: qsTr("Floating: %1").arg(Hyprland.activeClient.floating ? "yes" : "no")
|
||||
color: Colours.palette.m3secondary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "gradient"
|
||||
text: qsTr("Xwayland: %1").arg(Hyprland.activeClient.lastIpcObject.xwayland ? "yes" : "no")
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "keep"
|
||||
text: qsTr("Pinned: %1").arg(Hyprland.activeClient.pinned ? "yes" : "no")
|
||||
color: Colours.palette.m3secondary
|
||||
}
|
||||
|
||||
Detail {
|
||||
icon: "fullscreen"
|
||||
text: {
|
||||
const fs = Hyprland.activeClient.fullscreen;
|
||||
return qsTr("Fullscreen state: %1").arg(fs == 0 ? "off" : fs == 1 ? "maximised" : "on");
|
||||
}
|
||||
color: Colours.palette.m3tertiary
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
component Detail: RowLayout {
|
||||
id: detail
|
||||
|
||||
required property string icon
|
||||
required property string text
|
||||
property alias color: icon.color
|
||||
|
||||
Layout.leftMargin: Appearance.padding.large
|
||||
Layout.rightMargin: Appearance.padding.large
|
||||
Layout.fillWidth: true
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: detail.icon
|
||||
}
|
||||
|
||||
StyledText {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
text: detail.text
|
||||
elide: Text.ElideRight
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
}
|
||||
|
||||
component Label: StyledText {
|
||||
Layout.leftMargin: Appearance.padding.large
|
||||
Layout.rightMargin: Appearance.padding.large
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
animate: true
|
||||
}
|
||||
}
|
||||
65
modules/windowinfo/Preview.qml
Normal file
65
modules/windowinfo/Preview.qml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
Layout.preferredWidth: preview.implicitWidth + Appearance.padding.large * 2
|
||||
Layout.fillHeight: true
|
||||
|
||||
ClippingRectangle {
|
||||
id: preview
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: label.top
|
||||
anchors.topMargin: Appearance.padding.large
|
||||
anchors.bottomMargin: Appearance.spacing.normal
|
||||
|
||||
implicitWidth: view.implicitWidth
|
||||
|
||||
color: "transparent"
|
||||
radius: Appearance.rounding.small
|
||||
|
||||
ScreencopyView {
|
||||
id: view
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
captureSource: Hyprland.activeClient ? ToplevelManager.activeToplevel : null
|
||||
live: true
|
||||
|
||||
constraintSize.width: parent.height * Math.min(screen.width / screen.height, Hyprland.activeClient.width / Hyprland.activeClient.height)
|
||||
constraintSize.height: parent.height
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: label
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Appearance.padding.large
|
||||
|
||||
animate: true
|
||||
text: {
|
||||
const client = Hyprland.activeClient;
|
||||
if (!client)
|
||||
return qsTr("No active client");
|
||||
|
||||
const mon = Hyprland.monitors.values[Hyprland.activeClient.lastIpcObject.monitor];
|
||||
return qsTr("%1 on monitor %2 at %3, %4").arg(client.title).arg(mon.name).arg(client.x).arg(client.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
modules/windowinfo/WindowInfo.qml
Normal file
45
modules/windowinfo/WindowInfo.qml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
implicitWidth: child.implicitWidth
|
||||
implicitHeight: screen.height * Config.winfo.sizes.heightMult
|
||||
|
||||
RowLayout {
|
||||
id: child
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Preview {
|
||||
screen: root.screen
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
Layout.preferredWidth: Config.winfo.sizes.detailsWidth
|
||||
Layout.fillHeight: true
|
||||
|
||||
StyledRect {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
color: Colours.palette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
Details {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -99,6 +99,7 @@ Singleton {
|
|||
readonly property int width: lastIpcObject.size[0]
|
||||
readonly property int height: lastIpcObject.size[1]
|
||||
readonly property HyprlandWorkspace workspace: Hyprland.workspaces.values.find(w => w.id === lastIpcObject.workspace.id) ?? null
|
||||
readonly property HyprlandMonitor monitor: Hyprland.monitors.values.find(m => m.id === lastIpcObject.monitor) ?? null
|
||||
readonly property bool floating: lastIpcObject.floating
|
||||
readonly property bool fullscreen: lastIpcObject.fullscreen
|
||||
readonly property int pid: lastIpcObject.pid
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//@ pragma Env QS_NO_RELOAD_POPUP=1
|
||||
|
||||
import "modules"
|
||||
import "modules/drawers"
|
||||
import "modules/background"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue