Compare commits
40
Commits
v0.2.0
...
aadecbfe44
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aadecbfe44 | ||
|
|
53f0aa80ae | ||
|
|
1cbe9a9f8a | ||
|
|
87433334c7 | ||
|
|
6d5609fe5b | ||
|
|
ffefd3e202 | ||
|
|
2db6a57c0b | ||
|
|
0c5871e108 | ||
|
|
6ad9fe5e07 | ||
|
|
8c11aead18 | ||
|
|
d013be0f9a | ||
|
|
69774e7759 | ||
|
|
cdf66bff23 | ||
|
|
562770595f | ||
|
|
d9856c9409 | ||
|
|
8dc9753e2b | ||
|
|
2a4bd3dfdd | ||
|
|
f75f51ef50 | ||
|
|
e0873304d1 | ||
|
|
99a0155c17 | ||
|
|
9126b04b10 | ||
|
|
55dab8f7ce | ||
|
|
f309debea5 | ||
|
|
0979fd014d | ||
|
|
c62695bfff | ||
|
|
2b06a33508 | ||
|
|
4d731c5a65 | ||
|
|
53836aec1a | ||
|
|
656bd0a54d | ||
|
|
355106cb2b | ||
|
|
865b5bda53 | ||
|
|
2b89c8e4a1 | ||
|
|
f01001dcfa | ||
|
|
61019204d8 | ||
|
|
bc6b0d50ab | ||
|
|
f27c9afc25 | ||
|
|
5962fc8354 | ||
|
|
1adcb9841c | ||
|
|
0e0f8555ed | ||
|
|
1d63e67554 |
@@ -15,3 +15,4 @@ dist/
|
|||||||
**/target/
|
**/target/
|
||||||
**/test-plugins/
|
**/test-plugins/
|
||||||
**/Charts/
|
**/Charts/
|
||||||
|
**/network-dev/
|
||||||
|
|||||||
@@ -52,6 +52,65 @@ add_compile_options(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if("shell" IN_LIST ENABLE_MODULES)
|
||||||
|
# Build settings index
|
||||||
|
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||||
|
set(SETTINGS_INDEX_JSON "${CMAKE_BINARY_DIR}/settings-index.json")
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
|
"${CMAKE_SOURCE_DIR}/scripts/build-settings-index.py"
|
||||||
|
"${CMAKE_SOURCE_DIR}/Modules/Settings"
|
||||||
|
"${SETTINGS_INDEX_JSON}"
|
||||||
|
RESULT_VARIABLE SETTINGS_INDEX_RESULT
|
||||||
|
)
|
||||||
|
if(NOT SETTINGS_INDEX_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to build settings search index")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Nuitka compilation
|
||||||
|
set(ZSHELL_CLI_BUILD_DIR "${CMAKE_BINARY_DIR}/zshell-cli")
|
||||||
|
set(ZSHELL_CLI_DIST "${ZSHELL_CLI_BUILD_DIR}/zshell.dist")
|
||||||
|
set(ZSHELL_CLI_SRC "${CMAKE_SOURCE_DIR}/cli/src/zshell")
|
||||||
|
|
||||||
|
find_program(NUITKA_EXECUTABLE nuitka REQUIRED)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE ZSHELL_CLI_SOURCES CONFIGURE_DEPENDS
|
||||||
|
"${ZSHELL_CLI_SRC}/*.py"
|
||||||
|
)
|
||||||
|
file(GLOB_RECURSE ZSHELL_CLI_ASSETS CONFIGURE_DEPENDS
|
||||||
|
"${ZSHELL_CLI_SRC}/assets/*"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${ZSHELL_CLI_DIST}/zshell-cli"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${ZSHELL_CLI_BUILD_DIR}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E rm -rf "${ZSHELL_CLI_DIST}"
|
||||||
|
|
||||||
|
COMMAND
|
||||||
|
${NUITKA_EXECUTABLE}
|
||||||
|
--standalone
|
||||||
|
--include-data-dir=${CMAKE_SOURCE_DIR}/cli/src/zshell/assets=zshell/assets
|
||||||
|
--output-dir=${ZSHELL_CLI_BUILD_DIR}
|
||||||
|
--output-filename=zshell-cli
|
||||||
|
${CMAKE_SOURCE_DIR}/cli/src/zshell/
|
||||||
|
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/cli
|
||||||
|
DEPENDS ${ZSHELL_CLI_SOURCES} ${ZSHELL_CLI_ASSETS}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(zshell-cli ALL DEPENDS "${ZSHELL_CLI_DIST}/zshell-cli")
|
||||||
|
|
||||||
|
install(PROGRAMS "${ZSHELL_CLI_DIST}/zshell-cli" DESTINATION "${INSTALL_LIBDIR}/zshell-cli")
|
||||||
|
install(DIRECTORY "${ZSHELL_CLI_DIST}/" DESTINATION "${INSTALL_LIBDIR}/zshell-cli" PATTERN "zshell-cli" EXCLUDE)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_SOURCE_DIR}/Plugins/cmake/zshell-cli.cmake.in"
|
||||||
|
"${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli.cmake"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
install(SCRIPT "${CMAKE_BINARY_DIR}/Plugins/cmake/zshell-cli.cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
if("plugin" IN_LIST ENABLE_MODULES)
|
if("plugin" IN_LIST ENABLE_MODULES)
|
||||||
add_subdirectory(Plugins)
|
add_subdirectory(Plugins)
|
||||||
|
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
default property alias content: contentColumn.data
|
|
||||||
property string description: ""
|
|
||||||
property bool expanded: false
|
|
||||||
property bool nested: false
|
|
||||||
property bool showBackground: false
|
|
||||||
required property string title
|
|
||||||
|
|
||||||
signal toggleRequested
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Appearance.spacing.small
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: sectionHeaderItem
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: Math.max(titleRow.implicitHeight + Appearance.padding.normal * 2, 48)
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: titleRow
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Appearance.padding.normal
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: Appearance.padding.normal
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Appearance.spacing.normal
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
font.pointSize: Appearance.font.size.larger
|
|
||||||
font.weight: 500
|
|
||||||
text: root.title
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
color: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
font.pointSize: Appearance.font.size.normal
|
|
||||||
rotation: root.expanded ? 180 : 0
|
|
||||||
text: "expand_more"
|
|
||||||
|
|
||||||
Behavior on rotation {
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.small
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
showHoverBackground: false
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
root.toggleRequested();
|
|
||||||
root.expanded = !root.expanded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: contentWrapper
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Appearance.spacing.small * 2) : 0
|
|
||||||
clip: true
|
|
||||||
|
|
||||||
Behavior on Layout.preferredHeight {
|
|
||||||
Anim {
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: backgroundRect
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
color: DynamicColors.transparency.enabled ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, root.nested ? 3 : 2) : (root.nested ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3surfaceContainer)
|
|
||||||
opacity: root.showBackground && root.expanded ? 1.0 : 0.0
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
visible: root.showBackground
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: contentColumn
|
|
||||||
|
|
||||||
anchors.bottomMargin: Appearance.spacing.small
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Appearance.padding.normal
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: Appearance.padding.normal
|
|
||||||
opacity: root.expanded ? 1.0 : 0.0
|
|
||||||
spacing: Appearance.spacing.small
|
|
||||||
y: Appearance.spacing.small
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: descriptionText
|
|
||||||
|
|
||||||
Layout.bottomMargin: root.description !== "" ? Appearance.spacing.small : 0
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.topMargin: root.description !== "" ? Appearance.spacing.smaller : 0
|
|
||||||
color: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
font.pointSize: Appearance.font.size.small
|
|
||||||
text: root.description
|
|
||||||
visible: root.description !== ""
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Templates
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property color nonPeakColor: DynamicColors.tPalette.m3primary
|
|
||||||
required property real peak
|
|
||||||
property color peakColor: DynamicColors.palette.m3primary
|
|
||||||
|
|
||||||
background: Item {
|
|
||||||
CustomRect {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: root.implicitHeight / 3
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: root.implicitHeight / 3
|
|
||||||
bottomRightRadius: root.implicitHeight / 15
|
|
||||||
color: root.nonPeakColor
|
|
||||||
implicitWidth: root.handle.x - root.implicitHeight
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
topRightRadius: root.implicitHeight / 15
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
bottomRightRadius: root.implicitHeight / 15
|
|
||||||
color: root.peakColor
|
|
||||||
implicitWidth: parent.width * root.peak
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
topRightRadius: root.implicitHeight / 15
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
Anim {
|
|
||||||
duration: 50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.bottomMargin: root.implicitHeight / 3
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: root.implicitHeight / 3
|
|
||||||
bottomLeftRadius: root.implicitHeight / 15
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
implicitWidth: root.implicitWidth - root.handle.x - root.handle.implicitWidth - root.implicitHeight
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
topLeftRadius: root.implicitHeight / 15
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle: CustomRect {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.palette.m3primary
|
|
||||||
implicitHeight: 15
|
|
||||||
implicitWidth: 5
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
x: root.visualPosition * root.availableWidth - implicitWidth / 2
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
anchors.fill: parent
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls.Basic
|
|
||||||
|
|
||||||
BusyIndicator {
|
|
||||||
id: control
|
|
||||||
|
|
||||||
property int busySize: 64
|
|
||||||
property color color: delegate.color
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
implicitHeight: control.busySize
|
|
||||||
implicitWidth: control.busySize
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: item
|
|
||||||
|
|
||||||
height: control.busySize
|
|
||||||
opacity: control.running ? 1 : 0
|
|
||||||
width: control.busySize
|
|
||||||
x: parent.width / 2 - (control.busySize / 2)
|
|
||||||
y: parent.height / 2 - (control.busySize / 2)
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
OpacityAnimator {
|
|
||||||
duration: 250
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RotationAnimator {
|
|
||||||
duration: 1250
|
|
||||||
from: 0
|
|
||||||
loops: Animation.Infinite
|
|
||||||
running: control.visible && control.running
|
|
||||||
target: item
|
|
||||||
to: 360
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: repeater
|
|
||||||
|
|
||||||
model: 6
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: delegate
|
|
||||||
|
|
||||||
required property int index
|
|
||||||
|
|
||||||
color: control.color
|
|
||||||
implicitHeight: 10
|
|
||||||
implicitWidth: 10
|
|
||||||
radius: 5
|
|
||||||
x: item.width / 2 - width / 2
|
|
||||||
y: item.height / 2 - height / 2
|
|
||||||
|
|
||||||
transform: [
|
|
||||||
Translate {
|
|
||||||
y: -Math.min(item.width, item.height) * 0.5 + 5
|
|
||||||
},
|
|
||||||
Rotation {
|
|
||||||
angle: delegate.index / repeater.count * 360
|
|
||||||
origin.x: 5
|
|
||||||
origin.y: 5
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Button {
|
|
||||||
id: control
|
|
||||||
|
|
||||||
property color bgColor: DynamicColors.palette.m3primary
|
|
||||||
property int radius: Appearance.rounding.smallest / 2
|
|
||||||
property color textColor: DynamicColors.palette.m3onPrimary
|
|
||||||
|
|
||||||
background: CustomRect {
|
|
||||||
color: control.bgColor
|
|
||||||
opacity: control.enabled ? 1.0 : 0.5
|
|
||||||
radius: control.radius
|
|
||||||
}
|
|
||||||
contentItem: CustomText {
|
|
||||||
color: control.textColor
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
opacity: control.enabled ? 1.0 : 0.5
|
|
||||||
text: control.text
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
radius: control.radius
|
|
||||||
|
|
||||||
onClicked: {
|
|
||||||
control.clicked();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
ComboBox {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int cornerRadius: Appearance.rounding.normal
|
|
||||||
property int fieldHeight: 42
|
|
||||||
property bool filled: true
|
|
||||||
property real focusRingOpacity: 0.70
|
|
||||||
property int hPadding: 16
|
|
||||||
property int menuCornerRadius: 16
|
|
||||||
property int menuRowHeight: 46
|
|
||||||
property int menuVisibleRows: 7
|
|
||||||
property bool preferPopupWindow: false
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
implicitHeight: fieldHeight
|
|
||||||
implicitWidth: 240
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
// ---------- Field background (filled/outlined + state layers + focus ring) ----------
|
|
||||||
background: Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: container
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
color: DynamicColors.palette.m3surfaceVariant
|
|
||||||
radius: root.cornerRadius
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- Content ----------
|
|
||||||
contentItem: RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: root.hPadding
|
|
||||||
anchors.rightMargin: root.hPadding
|
|
||||||
spacing: 12
|
|
||||||
|
|
||||||
// Display text
|
|
||||||
CustomText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: root.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.pixelSize: 16
|
|
||||||
font.weight: Font.Medium
|
|
||||||
text: root.currentText
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicator chevron (simple, replace with your icon system)
|
|
||||||
CustomText {
|
|
||||||
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
rotation: root.popup.visible ? 180 : 0
|
|
||||||
text: "▾"
|
|
||||||
transformOrigin: Item.Center
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
|
|
||||||
Behavior on rotation {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: 140
|
|
||||||
easing.type: Easing.OutCubic
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
popup: Popup {
|
|
||||||
id: p
|
|
||||||
|
|
||||||
implicitHeight: list.contentItem.height + Appearance.padding.small * 2
|
|
||||||
implicitWidth: root.width
|
|
||||||
modal: true
|
|
||||||
popupType: root.preferPopupWindow ? Popup.Window : Popup.Item
|
|
||||||
y: -list.currentIndex * (root.menuRowHeight + Appearance.spacing.small) - Appearance.padding.small
|
|
||||||
|
|
||||||
background: CustomRect {
|
|
||||||
color: DynamicColors.palette.m3surface
|
|
||||||
radius: root.menuCornerRadius
|
|
||||||
}
|
|
||||||
contentItem: ListView {
|
|
||||||
id: list
|
|
||||||
|
|
||||||
anchors.bottomMargin: Appearance.padding.small
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.topMargin: Appearance.padding.small
|
|
||||||
clip: true
|
|
||||||
currentIndex: root.currentIndex
|
|
||||||
model: root.delegateModel
|
|
||||||
spacing: Appearance.spacing.small
|
|
||||||
|
|
||||||
delegate: CustomRect {
|
|
||||||
required property int index
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
color: (index === root.currentIndex) ? DynamicColors.palette.m3primary : "transparent"
|
|
||||||
implicitHeight: root.menuRowHeight
|
|
||||||
implicitWidth: p.implicitWidth - Appearance.padding.small * 2
|
|
||||||
radius: Appearance.rounding.normal - Appearance.padding.small
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: 10
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
elide: Text.ElideRight
|
|
||||||
font.pixelSize: 15
|
|
||||||
text: modelData
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
color: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
text: "✓"
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
visible: index === root.currentIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
onClicked: {
|
|
||||||
root.currentIndex = index;
|
|
||||||
p.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expressive-ish open/close motion: subtle scale+fade (tune to taste). :contentReference[oaicite:5]{index=5}
|
|
||||||
enter: Transition {
|
|
||||||
Anim {
|
|
||||||
from: 0
|
|
||||||
property: "opacity"
|
|
||||||
to: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Anim {
|
|
||||||
from: 0.98
|
|
||||||
property: "scale"
|
|
||||||
to: 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit: Transition {
|
|
||||||
Anim {
|
|
||||||
from: 1
|
|
||||||
property: "opacity"
|
|
||||||
to: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Elevation {
|
|
||||||
anchors.fill: parent
|
|
||||||
level: 2
|
|
||||||
radius: root.menuCornerRadius
|
|
||||||
z: -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Components
|
|
||||||
|
|
||||||
ToolTip {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property bool alternativeVisibleCondition: false
|
|
||||||
property bool extraVisibleCondition: true
|
|
||||||
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
|
|
||||||
|
|
||||||
background: null
|
|
||||||
horizontalPadding: 10
|
|
||||||
verticalPadding: 5
|
|
||||||
visible: internalVisibleCondition
|
|
||||||
|
|
||||||
contentItem: CustomTooltipContent {
|
|
||||||
id: contentItem
|
|
||||||
|
|
||||||
horizontalPadding: root.horizontalPadding
|
|
||||||
shown: root.internalVisibleCondition
|
|
||||||
text: root.text
|
|
||||||
verticalPadding: root.verticalPadding
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property real horizontalPadding: 10
|
|
||||||
property bool isVisible: backgroundRectangle.implicitHeight > 0
|
|
||||||
property bool shown: false
|
|
||||||
required property string text
|
|
||||||
property real verticalPadding: 5
|
|
||||||
|
|
||||||
implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding
|
|
||||||
implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: backgroundRectangle
|
|
||||||
|
|
||||||
clip: true
|
|
||||||
color: DynamicColors.tPalette.m3inverseSurface ?? "#3C4043"
|
|
||||||
implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0
|
|
||||||
implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0
|
|
||||||
opacity: shown ? 1 : 0
|
|
||||||
radius: Appearance.rounding.smallest
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors {
|
|
||||||
bottom: root.bottom
|
|
||||||
horizontalCenter: root.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: tooltipTextObject
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: DynamicColors.palette.m3inverseOnSurface ?? "#FFFFFF"
|
|
||||||
text: root.text
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import qs.Config
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
required property int extra
|
|
||||||
|
|
||||||
anchors.margins: 8
|
|
||||||
anchors.right: parent.right
|
|
||||||
color: DynamicColors.palette.m3tertiary
|
|
||||||
implicitHeight: count.implicitHeight + 4 * 2
|
|
||||||
implicitWidth: count.implicitWidth + 8 * 2
|
|
||||||
opacity: extra > 0 ? 1 : 0
|
|
||||||
radius: Appearance.rounding.smallest
|
|
||||||
scale: extra > 0 ? 1 : 0.5
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on scale {
|
|
||||||
Anim {
|
|
||||||
duration: MaterialEasing.expressiveEffectsTime
|
|
||||||
easing.bezierCurve: MaterialEasing.expressiveEffects
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Elevation {
|
|
||||||
anchors.fill: parent
|
|
||||||
level: 2
|
|
||||||
opacity: parent.opacity
|
|
||||||
radius: parent.radius
|
|
||||||
z: -1
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: count
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
animate: parent.opacity > 0
|
|
||||||
color: DynamicColors.palette.m3onTertiary
|
|
||||||
text: qsTr("+%1").arg(parent.extra)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
IconButton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property bool shouldBeVisible
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
scale: 0
|
|
||||||
visible: root.scale > 0
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.small
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on scale {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onShouldBeVisibleChanged: {
|
|
||||||
if (root.shouldBeVisible) {
|
|
||||||
root.opacity = 1;
|
|
||||||
root.scale = 1;
|
|
||||||
} else {
|
|
||||||
root.opacity = 0;
|
|
||||||
root.scale = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
enum Variant {
|
|
||||||
Outlined,
|
|
||||||
Filled
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly property color accent: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3outline))
|
|
||||||
readonly property real disabledOpacity: 0.38
|
|
||||||
readonly property string effectiveTrailingIcon: root.trailingIcon.length > 0 ? root.trailingIcon : (root.password ? (field.echoMode === TextInput.Password ? "visibility" : "visibility_off") : (root.isError ? "error" : ""))
|
|
||||||
property string errorText: ""
|
|
||||||
readonly property alias field: field
|
|
||||||
readonly property bool floating: focused || hasContent || prefixText.length > 0
|
|
||||||
readonly property bool focused: field.activeFocus
|
|
||||||
readonly property bool hasContent: field.text.length > 0
|
|
||||||
readonly property color iconColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
|
|
||||||
property int inputMethodHints: Qt.ImhNone
|
|
||||||
property bool isError: false
|
|
||||||
property string label: ""
|
|
||||||
readonly property color labelColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : (focused ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant))
|
|
||||||
property string leadingIcon: ""
|
|
||||||
property int maxLength: 0
|
|
||||||
property color notchColor: DynamicColors.palette.m3surface
|
|
||||||
readonly property bool outlined: variant === M3TextField.Variant.Outlined
|
|
||||||
property bool password: false
|
|
||||||
property string placeholder: ""
|
|
||||||
property string prefixText: ""
|
|
||||||
property string suffixText: ""
|
|
||||||
readonly property color supportColor: !root.enabled ? DynamicColors.palette.m3onSurface : (isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurfaceVariant)
|
|
||||||
property string supportingText: ""
|
|
||||||
property alias text: field.text
|
|
||||||
property string trailingIcon: ""
|
|
||||||
readonly property bool trailingInteractive: root.password && root.trailingIcon.length === 0
|
|
||||||
property var validator: null
|
|
||||||
property int variant: M3TextField.Variant.Outlined
|
|
||||||
|
|
||||||
signal accepted
|
|
||||||
|
|
||||||
function forceFieldFocus(): void {
|
|
||||||
field.forceActiveFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitHeight: 56 + (supportRow.visible ? supportRow.implicitHeight + Appearance.spacing.extraSmall : 0)
|
|
||||||
implicitWidth: 240
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: filledBg
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
color: root.enabled ? (root.focused ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 1) : DynamicColors.palette.m3surfaceContainerHigh) : DynamicColors.palette.m3onSurface
|
|
||||||
implicitHeight: 56
|
|
||||||
opacity: root.enabled ? 1 : 0.04 // M3 filled disabled container @ 4%
|
|
||||||
|
|
||||||
topLeftRadius: Appearance.rounding.small
|
|
||||||
topRightRadius: Appearance.rounding.small
|
|
||||||
visible: !root.outlined
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
|
||||||
enabled: root.enabled
|
|
||||||
topLeftRadius: Appearance.rounding.small
|
|
||||||
topRightRadius: Appearance.rounding.small
|
|
||||||
|
|
||||||
onClicked: field.forceActiveFocus()
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
color: root.accent
|
|
||||||
implicitHeight: root.focused || root.isError ? 2 : 1
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
Anim {
|
|
||||||
type: Anim.StandardSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: outline
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
border.color: root.accent
|
|
||||||
border.width: root.focused || root.isError ? 2 : 1
|
|
||||||
color: "transparent"
|
|
||||||
implicitHeight: 56
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
visible: root.outlined
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on border.width {
|
|
||||||
Anim {
|
|
||||||
type: Anim.StandardSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateLayer {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: outline.border.width
|
|
||||||
color: root.isError ? DynamicColors.palette.m3error : DynamicColors.palette.m3onSurface
|
|
||||||
enabled: root.enabled
|
|
||||||
radius: Appearance.rounding.small
|
|
||||||
|
|
||||||
onClicked: field.forceActiveFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: root.notchColor
|
|
||||||
height: outline.border.width
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
visible: root.outlined && root.floating && root.label.length > 0
|
|
||||||
width: labelText.width + Appearance.spacing.extraSmall
|
|
||||||
x: labelText.x - Appearance.spacing.extraSmall / 2
|
|
||||||
y: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: labelText
|
|
||||||
|
|
||||||
color: root.labelColor
|
|
||||||
font.pointSize: root.floating ? Appearance.font.size.small : Appearance.font.size.medium
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.label
|
|
||||||
visible: root.label.length > 0
|
|
||||||
x: root.floating ? (Appearance.padding.larger + Appearance.spacing.extraSmall / 2) : (leading.visible ? leading.x + leading.width + Appearance.spacing.small : Appearance.padding.larger)
|
|
||||||
y: root.floating ? (root.outlined ? -height / 2 : Appearance.padding.extraSmall) : (56 - height) / 2
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on x {
|
|
||||||
Anim {
|
|
||||||
type: Anim.StandardSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Behavior on y {
|
|
||||||
Anim {
|
|
||||||
type: Anim.StandardSmall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
id: leading
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Appearance.padding.larger
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: (56 - height) / 2
|
|
||||||
color: root.iconColor
|
|
||||||
font.pointSize: Appearance.font.size.medium
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.leadingIcon
|
|
||||||
visible: root.leadingIcon.length > 0
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
CAnim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
id: trailing
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: Appearance.padding.larger
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: (56 - height) / 2
|
|
||||||
color: root.isError ? DynamicColors.palette.m3error : (root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface)
|
|
||||||
font.pointSize: Appearance.font.size.medium
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.effectiveTrailingIcon
|
|
||||||
visible: root.effectiveTrailingIcon.length > 0
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: -Appearance.padding.small
|
|
||||||
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
enabled: root.trailingInteractive && root.enabled
|
|
||||||
|
|
||||||
onClicked: field.echoMode = field.echoMode === TextInput.Password ? TextInput.Normal : TextInput.Password
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: prefix
|
|
||||||
|
|
||||||
anchors.left: leading.visible ? leading.right : parent.left
|
|
||||||
anchors.leftMargin: leading.visible ? Appearance.spacing.small : Appearance.padding.larger
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
|
|
||||||
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: Appearance.font.size.medium
|
|
||||||
height: 56
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.prefixText
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
visible: root.prefixText.length > 0 && root.floating
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: suffix
|
|
||||||
|
|
||||||
anchors.right: trailing.visible ? trailing.left : parent.right
|
|
||||||
anchors.rightMargin: trailing.visible ? Appearance.spacing.small : Appearance.padding.larger
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
|
|
||||||
color: root.enabled ? DynamicColors.palette.m3onSurfaceVariant : DynamicColors.palette.m3onSurface
|
|
||||||
font.pointSize: Appearance.font.size.medium
|
|
||||||
height: 56
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.suffixText
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
visible: root.suffixText.length > 0 && root.floating
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomTextField {
|
|
||||||
id: field
|
|
||||||
|
|
||||||
anchors.left: prefix.visible ? prefix.right : (leading.visible ? leading.right : parent.left)
|
|
||||||
anchors.leftMargin: prefix.visible ? Appearance.spacing.extraSmall : (leading.visible ? Appearance.spacing.small : Appearance.padding.larger)
|
|
||||||
anchors.right: suffix.visible ? suffix.left : (trailing.visible ? trailing.left : parent.right)
|
|
||||||
anchors.rightMargin: suffix.visible ? Appearance.spacing.extraSmall : (trailing.visible ? Appearance.spacing.small : Appearance.padding.larger)
|
|
||||||
anchors.top: parent.top
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
|
||||||
echoMode: root.password ? TextInput.Password : TextInput.Normal
|
|
||||||
enabled: root.enabled
|
|
||||||
font.pointSize: Appearance.font.size.medium
|
|
||||||
height: 56
|
|
||||||
inputMethodHints: root.inputMethodHints
|
|
||||||
maximumLength: root.maxLength > 0 ? root.maxLength : 32767
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
placeholderText: root.focused ? root.placeholder : ""
|
|
||||||
topPadding: root.floating && !root.outlined ? Appearance.font.size.small + Appearance.padding.extraSmall : 0
|
|
||||||
validator: root.validator
|
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
|
||||||
|
|
||||||
onAccepted: root.accepted()
|
|
||||||
onTextEdited: if (root.isError)
|
|
||||||
root.isError = false
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: supportRow
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.topMargin: 56 + Appearance.spacing.extraSmall
|
|
||||||
implicitHeight: Math.max(supportText.implicitHeight, counter.implicitHeight)
|
|
||||||
visible: (root.isError && root.errorText.length > 0) || root.supportingText.length > 0 || root.maxLength > 0
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: supportText
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Appearance.padding.larger
|
|
||||||
anchors.right: counter.visible ? counter.left : parent.right
|
|
||||||
anchors.rightMargin: Appearance.spacing.small
|
|
||||||
color: root.supportColor
|
|
||||||
font.pointSize: Appearance.font.size.small
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: root.isError && root.errorText.length > 0 ? root.errorText : root.supportingText
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
id: counter
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: Appearance.padding.larger
|
|
||||||
color: root.supportColor
|
|
||||||
font.pointSize: Appearance.font.size.small
|
|
||||||
opacity: root.enabled ? 1 : root.disabledOpacity
|
|
||||||
text: `${root.text.length}/${root.maxLength}`
|
|
||||||
visible: root.maxLength > 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
Path {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property real viewHeight
|
|
||||||
required property real viewWidth
|
|
||||||
|
|
||||||
startX: root.viewWidth / 2
|
|
||||||
startY: 0
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.25
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight * (1 / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.45
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight * (2 / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.70
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight * (3 / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 1.00
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight * (4 / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.70
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight * (5 / 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.45
|
|
||||||
}
|
|
||||||
|
|
||||||
PathLine {
|
|
||||||
x: root.viewWidth / 2
|
|
||||||
y: root.viewHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
PathAttribute {
|
|
||||||
name: "itemOpacity"
|
|
||||||
value: 0.25
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Effects
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Elevation {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property int currentIndex
|
|
||||||
property bool expanded
|
|
||||||
required property int from
|
|
||||||
property color insideTextColor: DynamicColors.palette.m3onPrimary
|
|
||||||
property int itemHeight
|
|
||||||
property int listHeight: 200
|
|
||||||
property color outsideTextColor: DynamicColors.palette.m3onSurfaceVariant
|
|
||||||
readonly property var spinnerModel: root.range(root.from, root.to)
|
|
||||||
required property int to
|
|
||||||
property Item triggerItem
|
|
||||||
|
|
||||||
signal itemSelected(item: int)
|
|
||||||
|
|
||||||
function range(first, last) {
|
|
||||||
let out = [];
|
|
||||||
for (let i = first; i <= last; ++i)
|
|
||||||
out.push(i);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
implicitHeight: root.expanded ? view.implicitHeight : 0
|
|
||||||
level: root.expanded ? 2 : 0
|
|
||||||
radius: itemHeight / 2
|
|
||||||
visible: implicitHeight > 0
|
|
||||||
z: root.expanded ? 100 : 0
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExpandedChanged: {
|
|
||||||
if (!root.expanded)
|
|
||||||
root.itemSelected(view.currentIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: spinnerDelegate
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: wrapper
|
|
||||||
|
|
||||||
readonly property color delegateTextColor: wrapper.PathView.view ? wrapper.PathView.view.delegateTextColor : "white"
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
height: root.itemHeight
|
|
||||||
opacity: wrapper.PathView.itemOpacity
|
|
||||||
visible: wrapper.PathView.onPath
|
|
||||||
width: wrapper.PathView.view ? wrapper.PathView.view.width : 0
|
|
||||||
z: wrapper.PathView.isCurrentItem ? 100 : Math.round(wrapper.PathView.itemScale * 100)
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: wrapper.delegateTextColor
|
|
||||||
font.pointSize: Appearance.font.size.large
|
|
||||||
text: wrapper.modelData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomClippingRect {
|
|
||||||
anchors.fill: parent
|
|
||||||
color: DynamicColors.palette.m3surfaceContainer
|
|
||||||
radius: parent.radius
|
|
||||||
z: root.z
|
|
||||||
|
|
||||||
// Main visible spinner: normal/outside text color
|
|
||||||
PathView {
|
|
||||||
id: view
|
|
||||||
|
|
||||||
property color delegateTextColor: root.outsideTextColor
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
clip: true
|
|
||||||
currentIndex: root.currentIndex - 1
|
|
||||||
delegate: spinnerDelegate
|
|
||||||
dragMargin: width
|
|
||||||
highlightRangeMode: PathView.StrictlyEnforceRange
|
|
||||||
implicitHeight: root.listHeight
|
|
||||||
model: root.spinnerModel
|
|
||||||
pathItemCount: 7
|
|
||||||
preferredHighlightBegin: 0.5
|
|
||||||
preferredHighlightEnd: 0.5
|
|
||||||
snapMode: PathView.SnapToItem
|
|
||||||
|
|
||||||
path: PathMenu {
|
|
||||||
viewHeight: view.height
|
|
||||||
viewWidth: view.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The selection rectangle itself
|
|
||||||
CustomRect {
|
|
||||||
id: selectionRect
|
|
||||||
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
color: DynamicColors.palette.m3primary
|
|
||||||
height: root.itemHeight
|
|
||||||
radius: root.itemHeight / 2
|
|
||||||
width: parent.width
|
|
||||||
z: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hidden source: same PathView, but with the "inside selection" text color
|
|
||||||
Item {
|
|
||||||
id: selectedTextSource
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
layer.enabled: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
PathView {
|
|
||||||
id: selectedTextView
|
|
||||||
|
|
||||||
property color delegateTextColor: root.insideTextColor
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
clip: true
|
|
||||||
currentIndex: view.currentIndex
|
|
||||||
delegate: spinnerDelegate
|
|
||||||
dragMargin: view.dragMargin
|
|
||||||
highlightRangeMode: view.highlightRangeMode
|
|
||||||
implicitHeight: root.listHeight
|
|
||||||
interactive: false
|
|
||||||
model: view.model
|
|
||||||
|
|
||||||
// Keep this PathView visually locked to the real one
|
|
||||||
offset: view.offset
|
|
||||||
pathItemCount: view.pathItemCount
|
|
||||||
preferredHighlightBegin: view.preferredHighlightBegin
|
|
||||||
preferredHighlightEnd: view.preferredHighlightEnd
|
|
||||||
snapMode: view.snapMode
|
|
||||||
|
|
||||||
path: PathMenu {
|
|
||||||
viewHeight: selectedTextView.height
|
|
||||||
viewWidth: selectedTextView.width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mask matching the selection rectangle
|
|
||||||
Item {
|
|
||||||
id: selectionMask
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
layer.enabled: true
|
|
||||||
visible: false
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
color: "white"
|
|
||||||
height: selectionRect.height
|
|
||||||
radius: selectionRect.radius
|
|
||||||
width: selectionRect.width
|
|
||||||
x: selectionRect.x
|
|
||||||
y: selectionRect.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only show the "inside selection" text where the mask exists
|
|
||||||
MultiEffect {
|
|
||||||
anchors.fill: selectedTextSource
|
|
||||||
maskEnabled: true
|
|
||||||
maskInverted: false
|
|
||||||
maskSource: selectionMask
|
|
||||||
source: selectedTextSource
|
|
||||||
z: 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
CustomRect {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property string label
|
|
||||||
required property real max
|
|
||||||
required property real min
|
|
||||||
property var onValueModified: function (value) {}
|
|
||||||
property real step: 1
|
|
||||||
required property real value
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
|
|
||||||
implicitHeight: row.implicitHeight + Appearance.padding.large * 2
|
|
||||||
radius: Appearance.rounding.normal
|
|
||||||
|
|
||||||
Behavior on implicitHeight {
|
|
||||||
Anim {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: row
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: Appearance.padding.large
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Appearance.spacing.normal
|
|
||||||
|
|
||||||
CustomText {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
text: root.label
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomSpinBox {
|
|
||||||
max: root.max
|
|
||||||
min: root.min
|
|
||||||
step: root.step
|
|
||||||
value: root.value
|
|
||||||
|
|
||||||
onValueModified: value => {
|
|
||||||
root.onValueModified(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,10 +40,6 @@ JsonObject {
|
|||||||
id: "tray",
|
id: "tray",
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "network",
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "clock",
|
id: "clock",
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -72,10 +68,12 @@ JsonObject {
|
|||||||
property bool upower: true
|
property bool upower: true
|
||||||
}
|
}
|
||||||
component Tray: JsonObject {
|
component Tray: JsonObject {
|
||||||
|
property bool recolorIcons: false
|
||||||
property bool showAudio: true
|
property bool showAudio: true
|
||||||
property bool showBluetooth: false
|
property bool showBluetooth: false
|
||||||
property bool showMicrophone: true
|
property bool showMicrophone: true
|
||||||
property bool showNetwork: false
|
property bool showNetwork: false
|
||||||
|
property bool showOnHover: false
|
||||||
property bool showPower: true
|
property bool showPower: true
|
||||||
property bool showWifi: false
|
property bool showWifi: false
|
||||||
property int trayIconSize: 24
|
property int trayIconSize: 24
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ JsonObject {
|
|||||||
property bool showMemory: true
|
property bool showMemory: true
|
||||||
property bool showNetwork: true
|
property bool showNetwork: true
|
||||||
property bool showStorage: true
|
property bool showStorage: true
|
||||||
|
property bool showVram: true
|
||||||
}
|
}
|
||||||
component Sizes: JsonObject {
|
component Sizes: JsonObject {
|
||||||
readonly property int dateTimeWidth: 110
|
readonly property int dateTimeWidth: 110
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ Item {
|
|||||||
root.panels.osd.hovered = false;
|
root.panels.osd.hovered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!root.popouts.currentName.startsWith("traymenu")) {
|
if (!root.popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
|
||||||
root.popouts.hasCurrent = false;
|
root.popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ Item {
|
|||||||
if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight)
|
if (!root.visibilities.bar && Config.bar.autoHide && y < root.bar.implicitHeight)
|
||||||
root.bar.isHovered = true;
|
root.bar.isHovered = true;
|
||||||
|
|
||||||
if (root.panels.sidebar.width === 0) {
|
if (root.panels.sidebar.offsetScale === 1) {
|
||||||
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
|
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
|
||||||
|
|
||||||
if (showOsd) {
|
if (showOsd) {
|
||||||
@@ -204,7 +204,7 @@ Item {
|
|||||||
root.panels.osd.hovered = true;
|
root.panels.osd.hovered = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const outOfSidebar = x < root.width - root.panels.sidebar.width;
|
const outOfSidebar = x < root.width - root.panels.sidebar.width * (1 - root.panels.sidebar.offsetScale);
|
||||||
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
|
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
|
||||||
|
|
||||||
if (!root.osdShortcutActive) {
|
if (!root.osdShortcutActive) {
|
||||||
|
|||||||
+3
-7
@@ -184,18 +184,14 @@ Item {
|
|||||||
Item {
|
Item {
|
||||||
id: settingsWrapper
|
id: settingsWrapper
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
implicitHeight: settings.implicitHeight
|
|
||||||
implicitWidth: settings.implicitWidth
|
|
||||||
x: (root.width - settings.implicitWidth) / 2
|
|
||||||
y: (settings.implicitHeight + (root.height - root.bar.implicitHeight - settings.implicitHeight) / 2) * (1 - settings.offsetScale) - settings.implicitHeight - 5
|
|
||||||
|
|
||||||
Settings.Wrapper {
|
Settings.Wrapper {
|
||||||
id: settings
|
id: settings
|
||||||
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.centerIn: parent
|
||||||
anchors.top: parent.top
|
anchors.verticalCenterOffset: (-implicitHeight - 5 - ((root.height - implicitHeight) / 2)) * offsetScale
|
||||||
// anchors.centerIn: parent
|
|
||||||
panels: root
|
panels: root
|
||||||
screen: root.screen
|
screen: root.screen
|
||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ Region {
|
|||||||
}
|
}
|
||||||
|
|
||||||
R {
|
R {
|
||||||
panel: root.panels.settingsWrapper
|
panel: root.panels.settings
|
||||||
}
|
}
|
||||||
|
|
||||||
R {
|
R {
|
||||||
|
|||||||
+4
-9
@@ -1,6 +1,7 @@
|
|||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
@@ -127,7 +128,7 @@ CustomWindow {
|
|||||||
HyprlandFocusGrab {
|
HyprlandFocusGrab {
|
||||||
id: focusGrab
|
id: focusGrab
|
||||||
|
|
||||||
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
|
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu") && (!Config.bar.tray.showOnHover || (panels.popouts.current as StackView)?.depth > 1))
|
||||||
windows: [root]
|
windows: [root]
|
||||||
|
|
||||||
onCleared: {
|
onCleared: {
|
||||||
@@ -305,15 +306,9 @@ CustomWindow {
|
|||||||
PanelBg {
|
PanelBg {
|
||||||
id: settingsBg
|
id: settingsBg
|
||||||
|
|
||||||
property real extraHeight: 0
|
|
||||||
|
|
||||||
deformAmount: 0.03
|
deformAmount: 0.03
|
||||||
implicitHeight: panels.settings.height * (1 + extraHeight)
|
panel: panels.settings
|
||||||
implicitWidth: panels.settings.width
|
|
||||||
panel: panels.settingsWrapper
|
|
||||||
radius: Appearance.rounding.large + Appearance.padding.normal
|
radius: Appearance.rounding.large + Appearance.padding.normal
|
||||||
x: panels.settingsWrapper.x + panels.settings.x + root.borderThickness
|
|
||||||
y: panels.settingsWrapper.y + panels.settings.y + bar.implicitHeight - panels.settings.height * extraHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelBg {
|
PanelBg {
|
||||||
@@ -421,7 +416,7 @@ CustomWindow {
|
|||||||
resources.transform: Matrix4x4 {
|
resources.transform: Matrix4x4 {
|
||||||
matrix: resourcesBg.deformMatrix
|
matrix: resourcesBg.deformMatrix
|
||||||
}
|
}
|
||||||
settingsWrapper.transform: Matrix4x4 {
|
settings.transform: Matrix4x4 {
|
||||||
matrix: settingsBg.deformMatrix
|
matrix: settingsBg.deformMatrix
|
||||||
}
|
}
|
||||||
sidebar.transform: Matrix4x4 {
|
sidebar.transform: Matrix4x4 {
|
||||||
|
|||||||
@@ -30,18 +30,6 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CustomRect {
|
|
||||||
// Layout.fillWidth: true
|
|
||||||
// color: DynamicColors.tPalette.m3surfaceContainer
|
|
||||||
// implicitHeight: resources.implicitHeight
|
|
||||||
// radius: Appearance.rounding.small
|
|
||||||
//
|
|
||||||
// Resources {
|
|
||||||
// id: resources
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
CustomClippingRect {
|
CustomClippingRect {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
function getTrayIcon(id: string, icon: string): string {
|
|
||||||
if (icon.includes("?path=")) {
|
|
||||||
const [name, path] = icon.split("?path=");
|
|
||||||
icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`);
|
|
||||||
} else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") {
|
|
||||||
icon = icon.replace("qspixmap", "icon/discord-tray");
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property Item activeMenu: null
|
|
||||||
property Item activeTrigger: null
|
|
||||||
|
|
||||||
function close(menu) {
|
|
||||||
if (!menu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (activeMenu === menu) {
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.expanded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeActive() {
|
|
||||||
if (activeMenu)
|
|
||||||
activeMenu.expanded = false;
|
|
||||||
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forget(menu) {
|
|
||||||
if (activeMenu === menu) {
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hit(item, scenePos) {
|
|
||||||
if (!item || !item.visible)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const p = item.mapFromItem(null, scenePos.x, scenePos.y);
|
|
||||||
return item.contains(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(menu, trigger) {
|
|
||||||
if (activeMenu && activeMenu !== menu)
|
|
||||||
activeMenu.expanded = false;
|
|
||||||
|
|
||||||
activeMenu = menu;
|
|
||||||
activeTrigger = trigger || null;
|
|
||||||
menu.expanded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle(menu, trigger) {
|
|
||||||
if (activeMenu === menu && menu.expanded)
|
|
||||||
close(menu);
|
|
||||||
else
|
|
||||||
open(menu, trigger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import QtQuick
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string autoGpuType: "NONE"
|
|
||||||
property string cpuName: ""
|
|
||||||
property real cpuPerc
|
|
||||||
property real cpuTemp
|
|
||||||
|
|
||||||
// Individual disks: Array of { mount, used, total, free, perc }
|
|
||||||
property var disks: []
|
|
||||||
property real gpuMemTotal: 0
|
|
||||||
property real gpuMemUsed
|
|
||||||
property real gpuPerc
|
|
||||||
property real gpuTemp
|
|
||||||
readonly property string gpuType: Config.services.gpuType.toUpperCase() || autoGpuType
|
|
||||||
property real lastCpuIdle
|
|
||||||
property real lastCpuTotal
|
|
||||||
readonly property real memPerc: memTotal > 0 ? memUsed / memTotal : 0
|
|
||||||
property real memTotal
|
|
||||||
property real memUsed
|
|
||||||
property int refCount
|
|
||||||
readonly property real storagePerc: {
|
|
||||||
let totalUsed = 0;
|
|
||||||
let totalSize = 0;
|
|
||||||
for (const disk of disks) {
|
|
||||||
totalUsed += disk.used;
|
|
||||||
totalSize += disk.total;
|
|
||||||
}
|
|
||||||
return totalSize > 0 ? totalUsed / totalSize : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanCpuName(name: string): string {
|
|
||||||
return name.replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/CPU/gi, "").replace(/\d+th Gen /gi, "").replace(/\d+nd Gen /gi, "").replace(/\d+rd Gen /gi, "").replace(/\d+st Gen /gi, "").replace(/Core /gi, "").replace(/Processor/gi, "").replace(/\s+/g, " ").trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanGpuName(name: string): string {
|
|
||||||
return name.replace(/NVIDIA GeForce /gi, "").replace(/NVIDIA /gi, "").replace(/AMD Radeon /gi, "").replace(/AMD /gi, "").replace(/Intel /gi, "").replace(/\(R\)/gi, "").replace(/\(TM\)/gi, "").replace(/Graphics/gi, "").replace(/\s+/g, " ").trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatKib(kib: real): var {
|
|
||||||
const mib = 1024;
|
|
||||||
const gib = 1024 ** 2;
|
|
||||||
const tib = 1024 ** 3;
|
|
||||||
|
|
||||||
if (kib >= tib)
|
|
||||||
return {
|
|
||||||
value: kib / tib,
|
|
||||||
unit: "TiB"
|
|
||||||
};
|
|
||||||
if (kib >= gib)
|
|
||||||
return {
|
|
||||||
value: kib / gib,
|
|
||||||
unit: "GiB"
|
|
||||||
};
|
|
||||||
if (kib >= mib)
|
|
||||||
return {
|
|
||||||
value: kib / mib,
|
|
||||||
unit: "MiB"
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
value: kib,
|
|
||||||
unit: "KiB"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: Config.dashboard.resourceUpdateInterval
|
|
||||||
repeat: true
|
|
||||||
running: root.refCount > 0
|
|
||||||
triggeredOnStart: true
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
stat.reload();
|
|
||||||
meminfo.reload();
|
|
||||||
if (root.gpuType === "GENERIC")
|
|
||||||
gpuUsage.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: 60000 * 120
|
|
||||||
repeat: true
|
|
||||||
running: true
|
|
||||||
triggeredOnStart: true
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
storage.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
interval: Config.dashboard.resourceUpdateInterval * 5
|
|
||||||
repeat: true
|
|
||||||
running: root.refCount > 0
|
|
||||||
triggeredOnStart: true
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
sensors.running = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: cpuinfoInit
|
|
||||||
|
|
||||||
path: "/proc/cpuinfo"
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
const nameMatch = text().match(/model name\s*:\s*(.+)/);
|
|
||||||
if (nameMatch)
|
|
||||||
root.cpuName = root.cleanCpuName(nameMatch[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: stat
|
|
||||||
|
|
||||||
path: "/proc/stat"
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
const data = text().match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/);
|
|
||||||
if (data) {
|
|
||||||
const stats = data.slice(1).map(n => parseInt(n, 10));
|
|
||||||
const total = stats.reduce((a, b) => a + b, 0);
|
|
||||||
const idle = stats[3] + (stats[4] ?? 0);
|
|
||||||
|
|
||||||
const totalDiff = total - root.lastCpuTotal;
|
|
||||||
const idleDiff = idle - root.lastCpuIdle;
|
|
||||||
const newCpuPerc = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0;
|
|
||||||
|
|
||||||
root.lastCpuTotal = total;
|
|
||||||
root.lastCpuIdle = idle;
|
|
||||||
|
|
||||||
if (Math.abs(newCpuPerc - root.cpuPerc) >= 0.01)
|
|
||||||
root.cpuPerc = newCpuPerc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileView {
|
|
||||||
id: meminfo
|
|
||||||
|
|
||||||
path: "/proc/meminfo"
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
const data = text();
|
|
||||||
const total = parseInt(data.match(/MemTotal: *(\d+)/)[1], 10) || 1;
|
|
||||||
const used = (root.memTotal - parseInt(data.match(/MemAvailable: *(\d+)/)[1], 10)) || 0;
|
|
||||||
|
|
||||||
if (root.memTotal !== total)
|
|
||||||
root.memTotal = total;
|
|
||||||
|
|
||||||
if (Math.abs(used - root.memUsed) >= 16384)
|
|
||||||
root.memUsed = used;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: storage
|
|
||||||
|
|
||||||
command: ["lsblk", "-b", "-o", "NAME,SIZE,TYPE,FSUSED,FSSIZE", "-P"]
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const diskMap = {}; // Map disk name -> { name, totalSize, used, fsTotal }
|
|
||||||
const lines = text.trim().split("\n");
|
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
if (line.trim() === "")
|
|
||||||
continue;
|
|
||||||
const nameMatch = line.match(/NAME="([^"]+)"/);
|
|
||||||
const sizeMatch = line.match(/SIZE="([^"]+)"/);
|
|
||||||
const typeMatch = line.match(/TYPE="([^"]+)"/);
|
|
||||||
const fsusedMatch = line.match(/FSUSED="([^"]*)"/);
|
|
||||||
const fssizeMatch = line.match(/FSSIZE="([^"]*)"/);
|
|
||||||
|
|
||||||
if (!nameMatch || !typeMatch)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const name = nameMatch[1];
|
|
||||||
const type = typeMatch[1];
|
|
||||||
const size = parseInt(sizeMatch?.[1] || "0", 10);
|
|
||||||
const fsused = parseInt(fsusedMatch?.[1] || "0", 10);
|
|
||||||
const fssize = parseInt(fssizeMatch?.[1] || "0", 10);
|
|
||||||
|
|
||||||
if (type === "disk") {
|
|
||||||
// Skip zram (swap) devices
|
|
||||||
if (name.startsWith("zram"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Initialize disk entry
|
|
||||||
if (!diskMap[name]) {
|
|
||||||
diskMap[name] = {
|
|
||||||
name: name,
|
|
||||||
totalSize: size,
|
|
||||||
used: 0,
|
|
||||||
fsTotal: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else if (type === "part") {
|
|
||||||
// Find parent disk (remove trailing numbers/p+numbers)
|
|
||||||
let parentDisk = name.replace(/p?\d+$/, "");
|
|
||||||
// For nvme devices like nvme0n1p1, parent is nvme0n1
|
|
||||||
if (name.match(/nvme\d+n\d+p\d+/))
|
|
||||||
parentDisk = name.replace(/p\d+$/, "");
|
|
||||||
|
|
||||||
// Aggregate partition usage to parent disk
|
|
||||||
if (diskMap[parentDisk]) {
|
|
||||||
diskMap[parentDisk].used += fsused;
|
|
||||||
diskMap[parentDisk].fsTotal += fssize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const diskList = [];
|
|
||||||
let totalUsed = 0;
|
|
||||||
let totalSize = 0;
|
|
||||||
|
|
||||||
for (const diskName of Object.keys(diskMap).sort()) {
|
|
||||||
const disk = diskMap[diskName];
|
|
||||||
// Use filesystem total if available, otherwise use disk size
|
|
||||||
const total = disk.fsTotal > 0 ? disk.fsTotal : disk.totalSize;
|
|
||||||
const used = disk.used;
|
|
||||||
const perc = total > 0 ? used / total : 0;
|
|
||||||
|
|
||||||
// Convert bytes to KiB for consistency with formatKib
|
|
||||||
diskList.push({
|
|
||||||
mount: disk.name // Using 'mount' property for compatibility
|
|
||||||
,
|
|
||||||
used: used / 1024,
|
|
||||||
total: total / 1024,
|
|
||||||
free: (total - used) / 1024,
|
|
||||||
perc: perc
|
|
||||||
});
|
|
||||||
|
|
||||||
totalUsed += used;
|
|
||||||
totalSize += total;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.disks = diskList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gpuNameDetect
|
|
||||||
|
|
||||||
command: ["sh", "-c", "nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || lspci 2>/dev/null | grep -i 'vga\\|3d\\|display' | head -1"]
|
|
||||||
running: true
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
const output = text.trim();
|
|
||||||
if (!output)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Check if it's from nvidia-smi (clean GPU name)
|
|
||||||
if (output.toLowerCase().includes("nvidia") || output.toLowerCase().includes("geforce") || output.toLowerCase().includes("rtx") || output.toLowerCase().includes("gtx")) {
|
|
||||||
root.gpuName = root.cleanGpuName(output);
|
|
||||||
} else {
|
|
||||||
// Parse lspci output: extract name from brackets or after colon
|
|
||||||
const bracketMatch = output.match(/\[([^\]]+)\]/);
|
|
||||||
if (bracketMatch) {
|
|
||||||
root.gpuName = root.cleanGpuName(bracketMatch[1]);
|
|
||||||
} else {
|
|
||||||
const colonMatch = output.match(/:\s*(.+)/);
|
|
||||||
if (colonMatch)
|
|
||||||
root.gpuName = root.cleanGpuName(colonMatch[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gpuTypeCheck
|
|
||||||
|
|
||||||
command: ["sh", "-c", "if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then echo NVIDIA; elif ls /sys/class/drm/card*/device/gpu_busy_percent 2>/dev/null | grep -q .; then echo GENERIC; else echo NONE; fi"]
|
|
||||||
running: !Config.services.gpuType
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: root.autoGpuType = text.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: oneshotMem
|
|
||||||
|
|
||||||
command: ["nvidia-smi", "--query-gpu=memory.total", "--format=csv,noheader,nounits"]
|
|
||||||
running: root.gpuType === "NVIDIA" && root.gpuMemTotal === 0
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
root.gpuMemTotal = Number(this.text.trim());
|
|
||||||
oneshotMem.running = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gpuUsageNvidia
|
|
||||||
|
|
||||||
command: ["/usr/bin/nvidia-smi", "--query-gpu=utilization.gpu,temperature.gpu,memory.used", "--format=csv,noheader,nounits", "-lms", "1000"]
|
|
||||||
running: root.refCount > 0 && root.gpuType === "NVIDIA"
|
|
||||||
|
|
||||||
stdout: SplitParser {
|
|
||||||
onRead: data => {
|
|
||||||
const parts = String(data).trim().split(/\s*,\s*/);
|
|
||||||
if (parts.length < 3)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const usageRaw = parseInt(parts[0], 10);
|
|
||||||
const tempRaw = parseInt(parts[1], 10);
|
|
||||||
const memRaw = parseInt(parts[2], 10);
|
|
||||||
|
|
||||||
if (!Number.isFinite(usageRaw) || !Number.isFinite(tempRaw) || !Number.isFinite(memRaw))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const newGpuPerc = Math.max(0, Math.min(1, usageRaw / 100));
|
|
||||||
const newGpuTemp = tempRaw;
|
|
||||||
const newGpuMemUsed = root.gpuMemTotal > 0 ? Math.max(0, Math.min(1, memRaw / root.gpuMemTotal)) : 0;
|
|
||||||
|
|
||||||
// Only publish meaningful changes to avoid needless binding churn / repaints
|
|
||||||
if (Math.abs(root.gpuPerc - newGpuPerc) >= 0.01)
|
|
||||||
root.gpuPerc = newGpuPerc;
|
|
||||||
|
|
||||||
if (Math.abs(root.gpuTemp - newGpuTemp) >= 1)
|
|
||||||
root.gpuTemp = newGpuTemp;
|
|
||||||
|
|
||||||
if (Math.abs(root.gpuMemUsed - newGpuMemUsed) >= 0.01)
|
|
||||||
root.gpuMemUsed = newGpuMemUsed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: gpuUsage
|
|
||||||
|
|
||||||
command: root.gpuType === "GENERIC" ? ["sh", "-c", "cat /sys/class/drm/card*/device/gpu_busy_percent"] : ["echo"]
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
if (root.gpuType === "GENERIC") {
|
|
||||||
const percs = text.trim().split("\n");
|
|
||||||
const sum = percs.reduce((acc, d) => acc + parseInt(d, 10), 0);
|
|
||||||
root.gpuPerc = sum / percs.length / 100;
|
|
||||||
} else {
|
|
||||||
root.gpuPerc = 0;
|
|
||||||
root.gpuTemp = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
|
||||||
id: sensors
|
|
||||||
|
|
||||||
command: ["sensors"]
|
|
||||||
environment: ({
|
|
||||||
LANG: "C.UTF-8",
|
|
||||||
LC_ALL: "C.UTF-8"
|
|
||||||
})
|
|
||||||
|
|
||||||
stdout: StdioCollector {
|
|
||||||
onStreamFinished: {
|
|
||||||
let cpuTemp = text.match(/(?:Package id [0-9]+|Tdie):\s+((\+|-)[0-9.]+)(°| )C/);
|
|
||||||
if (!cpuTemp)
|
|
||||||
// If AMD Tdie pattern failed, try fallback on Tctl
|
|
||||||
cpuTemp = text.match(/Tctl:\s+((\+|-)[0-9.]+)(°| )C/);
|
|
||||||
|
|
||||||
if (cpuTemp && Math.abs(parseFloat(cpuTemp[1]) - root.cpuTemp) >= 0.5)
|
|
||||||
root.cpuTemp = parseFloat(cpuTemp[1]);
|
|
||||||
|
|
||||||
if (root.gpuType !== "GENERIC")
|
|
||||||
return;
|
|
||||||
|
|
||||||
let eligible = false;
|
|
||||||
let sum = 0;
|
|
||||||
let count = 0;
|
|
||||||
|
|
||||||
for (const line of text.trim().split("\n")) {
|
|
||||||
if (line === "Adapter: PCI adapter")
|
|
||||||
eligible = true;
|
|
||||||
else if (line === "")
|
|
||||||
eligible = false;
|
|
||||||
else if (eligible) {
|
|
||||||
let match = line.match(/^(temp[0-9]+|GPU core|edge)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
|
|
||||||
if (!match)
|
|
||||||
// Fall back to junction/mem if GPU doesn't have edge temp (for AMD GPUs)
|
|
||||||
match = line.match(/^(junction|mem)+:\s+\+([0-9]+\.[0-9]+)(°| )C/);
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
sum += parseFloat(match[2]);
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
root.gpuTemp = count > 0 ? sum / count : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property list<DesktopEntry> entryList: []
|
|
||||||
property var preppedIcons: []
|
|
||||||
property var preppedIds: []
|
|
||||||
property var preppedNames: []
|
|
||||||
|
|
||||||
// Dynamic fixups
|
|
||||||
property var regexSubstitutions: [
|
|
||||||
{
|
|
||||||
"regex": /^steam_app_(\d+)$/,
|
|
||||||
"replace": "steam_icon_$1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /Minecraft.*/,
|
|
||||||
"replace": "minecraft-launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /.*polkit.*/,
|
|
||||||
"replace": "system-lock-screen"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /gcr.prompter/,
|
|
||||||
"replace": "system-lock-screen"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
property real scoreThreshold: 0.2
|
|
||||||
|
|
||||||
// Manual overrides for tricky apps
|
|
||||||
property var substitutions: ({
|
|
||||||
"code-url-handler": "visual-studio-code",
|
|
||||||
"Code": "visual-studio-code",
|
|
||||||
"gnome-tweaks": "org.gnome.tweaks",
|
|
||||||
"pavucontrol-qt": "pavucontrol",
|
|
||||||
"wps": "wps-office2019-kprometheus",
|
|
||||||
"wpsoffice": "wps-office2019-kprometheus",
|
|
||||||
"footclient": "foot"
|
|
||||||
})
|
|
||||||
|
|
||||||
function checkCleanMatch(str) {
|
|
||||||
if (!str || str.length <= 3)
|
|
||||||
return null;
|
|
||||||
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Aggressive fallback: strip all separators
|
|
||||||
const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, '');
|
|
||||||
const list = Array.from(entryList);
|
|
||||||
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
const entry = list[i];
|
|
||||||
const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, '');
|
|
||||||
if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkFuzzySearch(str) {
|
|
||||||
if (typeof FuzzySort === 'undefined')
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Check filenames (IDs) first
|
|
||||||
if (preppedIds.length > 0) {
|
|
||||||
let results = fuzzyQuery(str, preppedIds);
|
|
||||||
if (results.length === 0) {
|
|
||||||
const underscored = str.replace(/-/g, '_').toLowerCase();
|
|
||||||
if (underscored !== str)
|
|
||||||
results = fuzzyQuery(underscored, preppedIds);
|
|
||||||
}
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then icons
|
|
||||||
if (preppedIcons.length > 0) {
|
|
||||||
const results = fuzzyQuery(str, preppedIcons);
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then names
|
|
||||||
if (preppedNames.length > 0) {
|
|
||||||
const results = fuzzyQuery(str, preppedNames);
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Lookup Helpers ---
|
|
||||||
|
|
||||||
function checkHeuristic(str) {
|
|
||||||
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
|
|
||||||
const entry = DesktopEntries.heuristicLookup(str);
|
|
||||||
if (entry)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkRegex(str) {
|
|
||||||
for (let i = 0; i < regexSubstitutions.length; i++) {
|
|
||||||
const sub = regexSubstitutions[i];
|
|
||||||
const replaced = str.replace(sub.regex, sub.replace);
|
|
||||||
if (replaced !== str) {
|
|
||||||
return findAppEntry(replaced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSimpleTransforms(str) {
|
|
||||||
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const lower = str.toLowerCase();
|
|
||||||
|
|
||||||
const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()];
|
|
||||||
|
|
||||||
for (let i = 0; i < variants.length; i++) {
|
|
||||||
const variant = variants[i];
|
|
||||||
if (variant) {
|
|
||||||
const entry = DesktopEntries.byId(variant);
|
|
||||||
if (entry)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSubstitutions(str) {
|
|
||||||
let effectiveStr = substitutions[str];
|
|
||||||
if (!effectiveStr)
|
|
||||||
effectiveStr = substitutions[str.toLowerCase()];
|
|
||||||
|
|
||||||
if (effectiveStr && effectiveStr !== str) {
|
|
||||||
return findAppEntry(effectiveStr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function distroLogoPath() {
|
|
||||||
try {
|
|
||||||
return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : "";
|
|
||||||
} catch (e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Robust lookup strategy
|
|
||||||
function findAppEntry(str) {
|
|
||||||
if (!str || str.length === 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
if (result = checkHeuristic(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkSubstitutions(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkRegex(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkSimpleTransforms(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkFuzzySearch(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkCleanMatch(str))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fuzzyQuery(search, preppedData) {
|
|
||||||
if (!search || !preppedData || preppedData.length === 0)
|
|
||||||
return [];
|
|
||||||
return FuzzySort.go(search, preppedData, {
|
|
||||||
all: true,
|
|
||||||
key: "name"
|
|
||||||
}).map(r => r.obj.entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFromReverseDomain(str) {
|
|
||||||
if (!str)
|
|
||||||
return "";
|
|
||||||
return str.split('.').slice(-1)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated shim
|
|
||||||
function guessIcon(str) {
|
|
||||||
const entry = findAppEntry(str);
|
|
||||||
return entry ? entry.icon : "image-missing";
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconExists(iconName) {
|
|
||||||
if (!iconName || iconName.length === 0)
|
|
||||||
return false;
|
|
||||||
if (iconName.startsWith("/"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const path = Quickshell.iconPath(iconName, true);
|
|
||||||
return path && path.length > 0 && !path.includes("image-missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconForAppId(appId, fallbackName) {
|
|
||||||
const fallback = fallbackName || "application-x-executable";
|
|
||||||
if (!appId)
|
|
||||||
return iconFromName(fallback, fallback);
|
|
||||||
|
|
||||||
const entry = findAppEntry(appId);
|
|
||||||
if (entry) {
|
|
||||||
return iconFromName(entry.icon, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return iconFromName(appId, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconFromName(iconName, fallbackName) {
|
|
||||||
const fallback = fallbackName || "application-x-executable";
|
|
||||||
try {
|
|
||||||
if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) {
|
|
||||||
const p = Quickshell.iconPath(iconName, fallback);
|
|
||||||
if (p && p !== "")
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : "";
|
|
||||||
} catch (e2) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeWithHyphens(str) {
|
|
||||||
if (!str)
|
|
||||||
return "";
|
|
||||||
return str.toLowerCase().replace(/\s+/g, "-");
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshEntries() {
|
|
||||||
if (typeof DesktopEntries === 'undefined')
|
|
||||||
return;
|
|
||||||
|
|
||||||
const values = Array.from(DesktopEntries.applications.values);
|
|
||||||
if (values) {
|
|
||||||
entryList = values.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
updatePreppedData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePreppedData() {
|
|
||||||
if (typeof FuzzySort === 'undefined')
|
|
||||||
return;
|
|
||||||
|
|
||||||
const list = Array.from(entryList);
|
|
||||||
preppedNames = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.name} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
preppedIcons = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.icon} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
preppedIds = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.id} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: refreshEntries()
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onValuesChanged() {
|
|
||||||
refreshEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: DesktopEntries.applications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Helpers
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: Appearance.padding.large
|
|
||||||
anchors.right: parent.right
|
|
||||||
columnSpacing: Appearance.spacing.large
|
|
||||||
columns: 2
|
|
||||||
rowSpacing: Appearance.spacing.large
|
|
||||||
rows: 1
|
|
||||||
|
|
||||||
Ref {
|
|
||||||
service: SystemUsage
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource {
|
|
||||||
Layout.bottomMargin: Appearance.padding.large
|
|
||||||
Layout.topMargin: Appearance.padding.large
|
|
||||||
colour: DynamicColors.palette.m3primary
|
|
||||||
icon: "memory"
|
|
||||||
value: SystemUsage.cpuPerc
|
|
||||||
}
|
|
||||||
|
|
||||||
Resource {
|
|
||||||
Layout.bottomMargin: Appearance.padding.large
|
|
||||||
Layout.topMargin: Appearance.padding.large
|
|
||||||
colour: DynamicColors.palette.m3secondary
|
|
||||||
icon: "memory_alt"
|
|
||||||
value: SystemUsage.memPerc
|
|
||||||
}
|
|
||||||
|
|
||||||
component Resource: CustomRect {
|
|
||||||
id: res
|
|
||||||
|
|
||||||
required property color colour
|
|
||||||
required property string icon
|
|
||||||
required property real value
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
|
||||||
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
|
||||||
implicitHeight: width
|
|
||||||
radius: Appearance.rounding.large
|
|
||||||
|
|
||||||
Behavior on value {
|
|
||||||
Anim {
|
|
||||||
duration: Appearance.anim.durations.large
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CircularProgress {
|
|
||||||
id: circ
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
bgColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 3)
|
|
||||||
fgColour: res.colour
|
|
||||||
padding: Appearance.padding.large * 3
|
|
||||||
strokeWidth: width < 200 ? Appearance.padding.smaller : Appearance.padding.normal
|
|
||||||
value: res.value
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
id: icon
|
|
||||||
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: res.colour
|
|
||||||
font.pointSize: (circ.arcRadius * 0.7) || 1
|
|
||||||
font.weight: 600
|
|
||||||
text: res.icon
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,5 +9,5 @@ export EGL_PLATFORM=gbm
|
|||||||
if command -v start-hyprland >/dev/null 2>&1; then
|
if command -v start-hyprland >/dev/null 2>&1; then
|
||||||
exec start-hyprland -- -c /etc/zshell-greeter/zshell-hyprland.conf
|
exec start-hyprland -- -c /etc/zshell-greeter/zshell-hyprland.conf
|
||||||
else
|
else
|
||||||
exec Hyprland -c /etc/zshell-greeter/zshell-hyprland.conf
|
exec Hyprland -c /etc/zshell-greeter/zshell-hyprland.lua
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
monitor = ,preferred,auto,1
|
|
||||||
|
|
||||||
env = XDG_SESSION_TYPE,wayland
|
|
||||||
env = QT_QPA_PLATFORM,wayland
|
|
||||||
env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1
|
|
||||||
|
|
||||||
misc {
|
|
||||||
disable_hyprland_logo = true
|
|
||||||
disable_splash_rendering = true
|
|
||||||
}
|
|
||||||
|
|
||||||
exec = sh -lc 'qs -c zshell-greeter; hyprctl dispatch exit'
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
hl.env("XDG_SESSION_TYPE", "wayland")
|
||||||
|
hl.env("QT_QPA_PLATFORM", "wayland")
|
||||||
|
hl.env("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1")
|
||||||
|
|
||||||
|
hl.config({
|
||||||
|
misc = {
|
||||||
|
disable_hyprland_logo = true,
|
||||||
|
disable_splash_rendering = true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
hl.on("hyprland.start", function()
|
||||||
|
hl.exec_cmd("sh -lc 'qs -c zshell-greeter; hyprctl dispatch exit'")
|
||||||
|
end)
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int displayMonth: new Date().getMonth()
|
|
||||||
property int displayYear: new Date().getFullYear()
|
|
||||||
readonly property int weekStartDay: 1 // 0 = Sunday, 1 = Monday
|
|
||||||
|
|
||||||
function getISOWeekNumber(date: var): int {
|
|
||||||
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
||||||
const dayNum = d.getUTCDay() || 7;
|
|
||||||
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
||||||
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
||||||
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWeekNumbers(month: int, year: int): var {
|
|
||||||
const days = getWeeksForMonth(month, year);
|
|
||||||
const weekNumbers = [];
|
|
||||||
let lastWeekNumber = -1;
|
|
||||||
|
|
||||||
for (let i = 0; i < days.length; i++) {
|
|
||||||
// Only add week numbers for days that belong to the current month
|
|
||||||
if (days[i].isCurrentMonth) {
|
|
||||||
const dayDate = new Date(days[i].year, days[i].month, days[i].day);
|
|
||||||
const weekNumber = getISOWeekNumber(dayDate);
|
|
||||||
|
|
||||||
// Only push if this is a new week
|
|
||||||
if (weekNumber !== lastWeekNumber) {
|
|
||||||
weekNumbers.push(weekNumber);
|
|
||||||
lastWeekNumber = weekNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return weekNumbers;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWeekStartIndex(month: int, year: int): int {
|
|
||||||
const today = new Date();
|
|
||||||
if (today.getMonth() !== month || today.getFullYear() !== year) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const days = getWeeksForMonth(month, year);
|
|
||||||
for (let i = 0; i < days.length; i++) {
|
|
||||||
if (days[i].isToday) {
|
|
||||||
// Return the start index of the week containing today
|
|
||||||
return Math.floor(i / 7) * 7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWeeksForMonth(month: int, year: int): var {
|
|
||||||
const firstDayOfMonth = new Date(year, month, 1);
|
|
||||||
const lastDayOfMonth = new Date(year, month + 1, 0);
|
|
||||||
|
|
||||||
const days = [];
|
|
||||||
let currentDate = new Date(year, month, 1);
|
|
||||||
|
|
||||||
// Back up to the start of the first week (Sunday or Monday based on locale)
|
|
||||||
const dayOfWeek = firstDayOfMonth.getDay();
|
|
||||||
const daysToBackup = (dayOfWeek - root.weekStartDay + 7) % 7;
|
|
||||||
currentDate.setDate(currentDate.getDate() - daysToBackup);
|
|
||||||
|
|
||||||
// Collect all days, including padding from previous/next month to complete the weeks
|
|
||||||
while (true) {
|
|
||||||
days.push({
|
|
||||||
day: currentDate.getDate(),
|
|
||||||
month: currentDate.getMonth(),
|
|
||||||
year: currentDate.getFullYear(),
|
|
||||||
isCurrentMonth: currentDate.getMonth() === month,
|
|
||||||
isToday: isDateToday(currentDate)
|
|
||||||
});
|
|
||||||
|
|
||||||
currentDate.setDate(currentDate.getDate() + 1);
|
|
||||||
|
|
||||||
// Stop after we've completed a full week following the last day of the month
|
|
||||||
if (currentDate > lastDayOfMonth && days.length % 7 === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return days;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDateToday(date: var): bool {
|
|
||||||
const today = new Date();
|
|
||||||
return date.getDate() === today.getDate() && date.getMonth() === today.getMonth() && date.getFullYear() === today.getFullYear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
function getTrayIcon(id: string, icon: string): string {
|
|
||||||
if (icon.includes("?path=")) {
|
|
||||||
const [name, path] = icon.split("?path=");
|
|
||||||
icon = Qt.resolvedUrl(`${path}/${name.slice(name.lastIndexOf("/") + 1)}`);
|
|
||||||
} else if (icon.includes("qspixmap") && id === "chrome_status_icon_1") {
|
|
||||||
icon = icon.replace("qspixmap", "icon/discord-tray");
|
|
||||||
}
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Hyprland
|
|
||||||
import qs.Helpers
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
function getInitialTitle(callback) {
|
|
||||||
let activeWindow = Hypr.activeToplevel.title;
|
|
||||||
let activeClass = Hypr.activeToplevel.lastIpcObject.class.toString();
|
|
||||||
let regex = new RegExp(activeClass, "i");
|
|
||||||
|
|
||||||
const evalTitle = activeWindow.match(regex);
|
|
||||||
callback(evalTitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Networking
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool active: false
|
||||||
|
readonly property list<Network> connectedNetworks: networks.filter(n => n.connected)
|
||||||
|
readonly property list<NetworkDevice> devices: Networking.devices.values
|
||||||
|
readonly property list<Network> knownNetworks: networks.filter(n => n.known && !n.known.ConnectionState.Connected)
|
||||||
|
readonly property list<Network> networks: {
|
||||||
|
const list = [];
|
||||||
|
for (const d of wifiDevices) {
|
||||||
|
for (const n of d.networks.values) {
|
||||||
|
if (!list.includes(n))
|
||||||
|
list.push(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
property bool scanning: false
|
||||||
|
readonly property list<Network> unknownNetworks: networks.filter(n => !n.known)
|
||||||
|
readonly property list<WifiDevice> wifiDevices: devices.filter(d => wifiDevice(d))
|
||||||
|
readonly property bool wifiEnabled: Networking.wifiEnabled
|
||||||
|
|
||||||
|
function isSecure(security): bool {
|
||||||
|
return (security === WifiSecurityType.WpaPsk || security === WifiSecurityType.Wpa2Psk || security === WifiSecurityType.Sae);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setScan(value: bool): void {
|
||||||
|
for (const d of wifiDevices) {
|
||||||
|
d.scannerEnabled = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setWifi(value: bool): void {
|
||||||
|
Networking.wifiEnabled = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wifiDevice(dev): bool {
|
||||||
|
return dev.type === DeviceType.Wifi;
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
for (const d of wifiDevices)
|
||||||
|
d.scannerEnabled = active;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,31 +9,25 @@ import qs.Config
|
|||||||
Singleton {
|
Singleton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// Private properties
|
|
||||||
property real _downloadSpeed: 0
|
property real _downloadSpeed: 0
|
||||||
property real _downloadTotal: 0
|
property real _downloadTotal: 0
|
||||||
|
|
||||||
// Initial readings for calculating totals
|
|
||||||
property real _initialRxBytes: 0
|
property real _initialRxBytes: 0
|
||||||
property real _initialTxBytes: 0
|
property real _initialTxBytes: 0
|
||||||
property bool _initialized: false
|
property bool _initialized: false
|
||||||
|
|
||||||
// Previous readings for calculating speed
|
|
||||||
property real _prevRxBytes: 0
|
property real _prevRxBytes: 0
|
||||||
property real _prevTimestamp: 0
|
property real _prevTimestamp: 0
|
||||||
property real _prevTxBytes: 0
|
property real _prevTxBytes: 0
|
||||||
property real _uploadSpeed: 0
|
property real _uploadSpeed: 0
|
||||||
property real _uploadTotal: 0
|
property real _uploadTotal: 0
|
||||||
|
|
||||||
// History buffers for sparkline
|
|
||||||
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
readonly property CircularBuffer downloadBuffer: _downloadBuffer
|
||||||
|
|
||||||
// Current speeds in bytes per second
|
|
||||||
readonly property real downloadSpeed: _downloadSpeed
|
readonly property real downloadSpeed: _downloadSpeed
|
||||||
|
|
||||||
// Total bytes transferred since tracking started
|
|
||||||
readonly property real downloadTotal: _downloadTotal
|
readonly property real downloadTotal: _downloadTotal
|
||||||
readonly property int historyLength: 30
|
readonly property int historyLength: Config.dashboard.performance.showVram ? 70 : 30
|
||||||
property int refCount: 0
|
property int refCount: 0
|
||||||
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
readonly property CircularBuffer uploadBuffer: _uploadBuffer
|
||||||
readonly property real uploadSpeed: _uploadSpeed
|
readonly property real uploadSpeed: _uploadSpeed
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias centerX: notifCenterSpacing.centerX
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: notifCenterSpacing
|
|
||||||
|
|
||||||
property int centerX
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell.Io
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias notifPath: storage.notifPath
|
|
||||||
|
|
||||||
JsonAdapter {
|
|
||||||
id: storage
|
|
||||||
|
|
||||||
property string notifPath: Quickshell.statePath("notifications.json")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Io
|
|
||||||
import ZShell.Models
|
|
||||||
import qs.Config
|
|
||||||
import qs.Modules
|
|
||||||
import qs.Helpers
|
|
||||||
import qs.Paths
|
|
||||||
|
|
||||||
Searcher {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string actualCurrent: WallpaperPath.currentWallpaperPath
|
|
||||||
readonly property string current: showPreview ? previewPath : actualCurrent
|
|
||||||
property string previewPath
|
|
||||||
property bool showPreview: false
|
|
||||||
|
|
||||||
function preview(path: string): void {
|
|
||||||
previewPath = path;
|
|
||||||
showPreview = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setWallpaper(path: string): void {
|
|
||||||
actualCurrent = path;
|
|
||||||
WallpaperPath.currentWallpaperPath = path;
|
|
||||||
Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/LockScreenBg.py")} --input_image=${root.actualCurrent} --output_path=${Paths.state}/lockscreen_bg.png`]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopPreview(): void {
|
|
||||||
showPreview = false;
|
|
||||||
Quickshell.execDetached(["sh", "-c", `python3 ${Quickshell.shellPath("scripts/SchemeColorGen.py")} --path=${root.actualCurrent} --thumbnail=${Paths.cache}/imagecache/thumbnail.jpg --output=${Paths.state}/scheme.json --scheme=${Config.colors.schemeType}`]);
|
|
||||||
}
|
|
||||||
|
|
||||||
extraOpts: useFuzzy ? ({}) : ({
|
|
||||||
forward: false
|
|
||||||
})
|
|
||||||
key: "relativePath"
|
|
||||||
list: wallpapers.entries
|
|
||||||
useFuzzy: true
|
|
||||||
|
|
||||||
FileSystemModel {
|
|
||||||
id: wallpapers
|
|
||||||
|
|
||||||
filter: FileSystemModel.Images
|
|
||||||
path: Config.general.wallpaperPath
|
|
||||||
recursive: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
QtObject {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property Item activeMenu: null
|
|
||||||
property Item activeTrigger: null
|
|
||||||
|
|
||||||
function close(menu) {
|
|
||||||
if (!menu)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (activeMenu === menu) {
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.expanded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeActive() {
|
|
||||||
if (activeMenu)
|
|
||||||
activeMenu.expanded = false;
|
|
||||||
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function forget(menu) {
|
|
||||||
if (activeMenu === menu) {
|
|
||||||
activeMenu = null;
|
|
||||||
activeTrigger = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hit(item, scenePos) {
|
|
||||||
if (!item || !item.visible)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const p = item.mapFromItem(null, scenePos.x, scenePos.y);
|
|
||||||
return item.contains(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
function open(menu, trigger) {
|
|
||||||
if (activeMenu && activeMenu !== menu)
|
|
||||||
activeMenu.expanded = false;
|
|
||||||
|
|
||||||
activeMenu = menu;
|
|
||||||
activeTrigger = trigger || null;
|
|
||||||
menu.expanded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggle(menu, trigger) {
|
|
||||||
if (activeMenu === menu && menu.expanded)
|
|
||||||
close(menu);
|
|
||||||
else
|
|
||||||
open(menu, trigger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property string highlightedSetting: ""
|
|
||||||
|
|
||||||
function clear() {
|
|
||||||
highlightedSetting = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function highlight(settingName: string) {
|
|
||||||
highlightedSetting = settingName;
|
|
||||||
highlightTimer.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: highlightTimer
|
|
||||||
|
|
||||||
interval: 2000
|
|
||||||
|
|
||||||
onTriggered: root.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
|
|
||||||
Text {
|
|
||||||
renderType: Text.NativeRendering
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
}
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import QtQuick
|
|
||||||
import Quickshell
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property list<DesktopEntry> entryList: []
|
|
||||||
property var preppedIcons: []
|
|
||||||
property var preppedIds: []
|
|
||||||
property var preppedNames: []
|
|
||||||
|
|
||||||
// Dynamic fixups
|
|
||||||
property var regexSubstitutions: [
|
|
||||||
{
|
|
||||||
"regex": /^steam_app_(\d+)$/,
|
|
||||||
"replace": "steam_icon_$1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /Minecraft.*/,
|
|
||||||
"replace": "minecraft-launcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /.*polkit.*/,
|
|
||||||
"replace": "system-lock-screen"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"regex": /gcr.prompter/,
|
|
||||||
"replace": "system-lock-screen"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
property real scoreThreshold: 0.2
|
|
||||||
|
|
||||||
// Manual overrides for tricky apps
|
|
||||||
property var substitutions: ({
|
|
||||||
"code-url-handler": "visual-studio-code",
|
|
||||||
"Code": "visual-studio-code",
|
|
||||||
"gnome-tweaks": "org.gnome.tweaks",
|
|
||||||
"pavucontrol-qt": "pavucontrol",
|
|
||||||
"wps": "wps-office2019-kprometheus",
|
|
||||||
"wpsoffice": "wps-office2019-kprometheus",
|
|
||||||
"footclient": "foot"
|
|
||||||
})
|
|
||||||
|
|
||||||
function checkCleanMatch(str) {
|
|
||||||
if (!str || str.length <= 3)
|
|
||||||
return null;
|
|
||||||
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Aggressive fallback: strip all separators
|
|
||||||
const cleanStr = str.toLowerCase().replace(/[\.\-_]/g, '');
|
|
||||||
const list = Array.from(entryList);
|
|
||||||
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
const entry = list[i];
|
|
||||||
const cleanId = (entry.id || "").toLowerCase().replace(/[\.\-_]/g, '');
|
|
||||||
if (cleanId.includes(cleanStr) || cleanStr.includes(cleanId)) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkFuzzySearch(str) {
|
|
||||||
if (typeof FuzzySort === 'undefined')
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Check filenames (IDs) first
|
|
||||||
if (preppedIds.length > 0) {
|
|
||||||
let results = fuzzyQuery(str, preppedIds);
|
|
||||||
if (results.length === 0) {
|
|
||||||
const underscored = str.replace(/-/g, '_').toLowerCase();
|
|
||||||
if (underscored !== str)
|
|
||||||
results = fuzzyQuery(underscored, preppedIds);
|
|
||||||
}
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then icons
|
|
||||||
if (preppedIcons.length > 0) {
|
|
||||||
const results = fuzzyQuery(str, preppedIcons);
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then names
|
|
||||||
if (preppedNames.length > 0) {
|
|
||||||
const results = fuzzyQuery(str, preppedNames);
|
|
||||||
if (results.length > 0)
|
|
||||||
return results[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Lookup Helpers ---
|
|
||||||
|
|
||||||
function checkHeuristic(str) {
|
|
||||||
if (typeof DesktopEntries !== 'undefined' && DesktopEntries.heuristicLookup) {
|
|
||||||
const entry = DesktopEntries.heuristicLookup(str);
|
|
||||||
if (entry)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkRegex(str) {
|
|
||||||
for (let i = 0; i < regexSubstitutions.length; i++) {
|
|
||||||
const sub = regexSubstitutions[i];
|
|
||||||
const replaced = str.replace(sub.regex, sub.replace);
|
|
||||||
if (replaced !== str) {
|
|
||||||
return findAppEntry(replaced);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSimpleTransforms(str) {
|
|
||||||
if (typeof DesktopEntries === 'undefined' || !DesktopEntries.byId)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const lower = str.toLowerCase();
|
|
||||||
|
|
||||||
const variants = [str, lower, getFromReverseDomain(str), getFromReverseDomain(str)?.toLowerCase(), normalizeWithHyphens(str), str.replace(/_/g, '-').toLowerCase(), str.replace(/-/g, '_').toLowerCase()];
|
|
||||||
|
|
||||||
for (let i = 0; i < variants.length; i++) {
|
|
||||||
const variant = variants[i];
|
|
||||||
if (variant) {
|
|
||||||
const entry = DesktopEntries.byId(variant);
|
|
||||||
if (entry)
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkSubstitutions(str) {
|
|
||||||
let effectiveStr = substitutions[str];
|
|
||||||
if (!effectiveStr)
|
|
||||||
effectiveStr = substitutions[str.toLowerCase()];
|
|
||||||
|
|
||||||
if (effectiveStr && effectiveStr !== str) {
|
|
||||||
return findAppEntry(effectiveStr);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function distroLogoPath() {
|
|
||||||
try {
|
|
||||||
return (typeof OSInfo !== 'undefined' && OSInfo.distroIconPath) ? OSInfo.distroIconPath : "";
|
|
||||||
} catch (e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Robust lookup strategy
|
|
||||||
function findAppEntry(str) {
|
|
||||||
if (!str || str.length === 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
let result = null;
|
|
||||||
|
|
||||||
if (result = checkHeuristic(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkSubstitutions(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkRegex(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkSimpleTransforms(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkFuzzySearch(str))
|
|
||||||
return result;
|
|
||||||
if (result = checkCleanMatch(str))
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fuzzyQuery(search, preppedData) {
|
|
||||||
if (!search || !preppedData || preppedData.length === 0)
|
|
||||||
return [];
|
|
||||||
return FuzzySort.go(search, preppedData, {
|
|
||||||
all: true,
|
|
||||||
key: "name"
|
|
||||||
}).map(r => r.obj.entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFromReverseDomain(str) {
|
|
||||||
if (!str)
|
|
||||||
return "";
|
|
||||||
return str.split('.').slice(-1)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated shim
|
|
||||||
function guessIcon(str) {
|
|
||||||
const entry = findAppEntry(str);
|
|
||||||
return entry ? entry.icon : "image-missing";
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconExists(iconName) {
|
|
||||||
if (!iconName || iconName.length === 0)
|
|
||||||
return false;
|
|
||||||
if (iconName.startsWith("/"))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
const path = Quickshell.iconPath(iconName, true);
|
|
||||||
return path && path.length > 0 && !path.includes("image-missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconForAppId(appId, fallbackName) {
|
|
||||||
const fallback = fallbackName || "application-x-executable";
|
|
||||||
if (!appId)
|
|
||||||
return iconFromName(fallback, fallback);
|
|
||||||
|
|
||||||
const entry = findAppEntry(appId);
|
|
||||||
if (entry) {
|
|
||||||
return iconFromName(entry.icon, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return iconFromName(appId, fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconFromName(iconName, fallbackName) {
|
|
||||||
const fallback = fallbackName || "application-x-executable";
|
|
||||||
try {
|
|
||||||
if (iconName && typeof Quickshell !== 'undefined' && Quickshell.iconPath) {
|
|
||||||
const p = Quickshell.iconPath(iconName, fallback);
|
|
||||||
if (p && p !== "")
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return Quickshell.iconPath ? (Quickshell.iconPath(fallback, true) || "") : "";
|
|
||||||
} catch (e2) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeWithHyphens(str) {
|
|
||||||
if (!str)
|
|
||||||
return "";
|
|
||||||
return str.toLowerCase().replace(/\s+/g, "-");
|
|
||||||
}
|
|
||||||
|
|
||||||
function refreshEntries() {
|
|
||||||
if (typeof DesktopEntries === 'undefined')
|
|
||||||
return;
|
|
||||||
|
|
||||||
const values = Array.from(DesktopEntries.applications.values);
|
|
||||||
if (values) {
|
|
||||||
entryList = values.sort((a, b) => a.name.localeCompare(b.name));
|
|
||||||
updatePreppedData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePreppedData() {
|
|
||||||
if (typeof FuzzySort === 'undefined')
|
|
||||||
return;
|
|
||||||
|
|
||||||
const list = Array.from(entryList);
|
|
||||||
preppedNames = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.name} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
preppedIcons = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.icon} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
preppedIds = list.map(a => ({
|
|
||||||
name: FuzzySort.prepare(`${a.id} `),
|
|
||||||
entry: a
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.onCompleted: refreshEntries()
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onValuesChanged() {
|
|
||||||
refreshEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: DesktopEntries.applications
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
JsonObject {
|
|
||||||
property list<var> week_0: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_1: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_10: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_11: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_12: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_13: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_14: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_15: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_16: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_17: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_18: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_19: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_2: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_20: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_21: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_22: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_23: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_24: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_25: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_26: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_27: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_28: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_29: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_3: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_30: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_31: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_32: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_33: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_34: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_35: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_36: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_37: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_38: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_39: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_4: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_40: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_41: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_42: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_43: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_44: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_45: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_46: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_47: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_48: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_49: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_5: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_50: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_51: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_6: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_7: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_8: [0, 0, 0, 0, 0, 0,]
|
|
||||||
property list<var> week_9: [0, 0, 0, 0, 0, 0,]
|
|
||||||
}
|
|
||||||
+3
-29
@@ -3,13 +3,9 @@ pragma ComponentBehavior: Bound
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import qs.Components
|
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Helpers
|
|
||||||
import qs.Modules.SysTray
|
import qs.Modules.SysTray
|
||||||
import qs.Modules.SysTray.Widgets
|
|
||||||
import qs.Modules.Network
|
|
||||||
import qs.Modules.Updates
|
import qs.Modules.Updates
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -26,7 +22,7 @@ RowLayout {
|
|||||||
const ch = childAt(x, height / 2) as WrappedLoader;
|
const ch = childAt(x, height / 2) as WrappedLoader;
|
||||||
|
|
||||||
if (!ch || ch?.id === "spacer") {
|
if (!ch || ch?.id === "spacer") {
|
||||||
if (!popouts.currentName.startsWith("traymenu"))
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover)
|
||||||
popouts.hasCurrent = false;
|
popouts.hasCurrent = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -48,7 +44,7 @@ RowLayout {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!popouts.currentName.startsWith("traymenu"))
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover)
|
||||||
popouts.hasCurrent = false;
|
popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,19 +53,7 @@ RowLayout {
|
|||||||
const item = ch.item;
|
const item = ch.item;
|
||||||
const itemWidth = item.implicitWidth;
|
const itemWidth = item.implicitWidth;
|
||||||
|
|
||||||
if (id === "audio" && Config.bar.popouts.audio) {
|
if (id === "updates") {
|
||||||
popouts.currentName = "audio";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
} else if (id === "network" && Config.bar.popouts.network) {
|
|
||||||
popouts.currentName = "network";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
} else if (id === "upower" && Config.bar.popouts.upower) {
|
|
||||||
popouts.currentName = "upower";
|
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
|
||||||
popouts.hasCurrent = true;
|
|
||||||
} else if (id === "updates") {
|
|
||||||
popouts.currentName = "updates";
|
popouts.currentName = "updates";
|
||||||
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
popouts.currentCenter = Qt.binding(() => item.mapToItem(root, itemWidth / 2, 0).x);
|
||||||
popouts.hasCurrent = true;
|
popouts.hasCurrent = true;
|
||||||
@@ -81,7 +65,6 @@ RowLayout {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
// model: Config.bar.entries.filted(n => n.index > 50).sort(n => n.index)
|
|
||||||
model: Config.bar.entries
|
model: Config.bar.entries
|
||||||
|
|
||||||
DelegateChooser {
|
DelegateChooser {
|
||||||
@@ -185,15 +168,6 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "network"
|
|
||||||
|
|
||||||
delegate: WrappedLoader {
|
|
||||||
sourceComponent: NetworkWidget {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
DelegateChoice {
|
||||||
roleValue: "media"
|
roleValue: "media"
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import qs.Config
|
|||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Modules.SysTray
|
import qs.Modules.SysTray
|
||||||
import qs.Modules.SysTray.Widgets
|
import qs.Modules.SysTray.Widgets
|
||||||
import qs.Modules.Network
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|||||||
@@ -34,21 +34,11 @@ Item {
|
|||||||
implicitHeight: 50
|
implicitHeight: 50
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
MaterialIcon {
|
SearchBar {
|
||||||
id: searchIcon
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: Appearance.padding.large
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: "search"
|
|
||||||
}
|
|
||||||
|
|
||||||
CustomTextField {
|
|
||||||
id: searchField
|
id: searchField
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: searchIcon.right
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Appearance.spacing.small
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
@@ -170,7 +160,6 @@ Item {
|
|||||||
id: lineText
|
id: lineText
|
||||||
|
|
||||||
color: DynamicColors.palette.m3onSurface
|
color: DynamicColors.palette.m3onSurface
|
||||||
font.family: ClipHistory.previewIsCode ? Appearance.font.family.mono : Appearance.font.family.sans
|
|
||||||
height: lineText.paintedHeight + Appearance.padding.extraSmall * 2
|
height: lineText.paintedHeight + Appearance.padding.extraSmall * 2
|
||||||
text: lineRow.modelData.text.trim()
|
text: lineRow.modelData.text.trim()
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
@@ -336,6 +325,8 @@ Item {
|
|||||||
}
|
}
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: ClipHistory.fuzzyQuery(searchField.text)
|
values: ClipHistory.fuzzyQuery(searchField.text)
|
||||||
|
|
||||||
|
onValuesChanged: view.currentIndex = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentItemChanged: {
|
onCurrentItemChanged: {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import QtQuick
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Modules.WSOverview
|
import qs.Modules.WSOverview
|
||||||
import qs.Modules.Network
|
|
||||||
import qs.Modules.SysTray.Popouts
|
import qs.Modules.SysTray.Popouts
|
||||||
import qs.Modules.Updates
|
import qs.Modules.Updates
|
||||||
|
|
||||||
|
|||||||
@@ -52,11 +52,7 @@ Item {
|
|||||||
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
placeholderText: qsTr("Type \"%1\" for commands").arg(Config.launcher.actionPrefix)
|
||||||
topPadding: Appearance.padding.larger
|
topPadding: Appearance.padding.larger
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: forceActiveFocus()
|
||||||
console.log(search.color);
|
|
||||||
console.log(search.placeholderTextColor);
|
|
||||||
forceActiveFocus();
|
|
||||||
}
|
|
||||||
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
Keys.onDownPressed: list.currentList?.decrementCurrentIndex()
|
||||||
Keys.onEscapePressed: root.visibilities.launcher = false
|
Keys.onEscapePressed: root.visibilities.launcher = false
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Item {
|
|||||||
if (visibilities.resources && panels.resourcesWrapper.x + panels.resourcesWrapper.width > root.x)
|
if (visibilities.resources && panels.resourcesWrapper.x + panels.resourcesWrapper.width > root.x)
|
||||||
max -= panels.resources.nonAnimHeight;
|
max -= panels.resources.nonAnimHeight;
|
||||||
if (panels.popouts.hasCurrent)
|
if (panels.popouts.hasCurrent)
|
||||||
if (panels.popouts.current.x + panels.popouts.current.width > root.x && panels.popouts.current.x < root.x + root.width)
|
if (panels.popouts.current?.x + panels.popouts.current?.width > root.x && panels.popouts.current?.x < root.x + root.width)
|
||||||
max -= panels.popouts.nonAnimHeight;
|
max -= panels.popouts.nonAnimHeight;
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,36 +149,71 @@ WlSessionLockSurface {
|
|||||||
Image {
|
Image {
|
||||||
id: background
|
id: background
|
||||||
|
|
||||||
|
anchors.bottomMargin: -8 - lockContent.positions[lockContent.positionIndex].y
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: -8 + lockContent.positions[lockContent.positionIndex].x
|
||||||
|
anchors.rightMargin: -8 - lockContent.positions[lockContent.positionIndex].x
|
||||||
|
anchors.topMargin: -8 + lockContent.positions[lockContent.positionIndex].y
|
||||||
fillMode: Image.PreserveAspectCrop
|
fillMode: Image.PreserveAspectCrop
|
||||||
source: WallpaperPath.lockscreenBg
|
source: WallpaperPath.lockscreenBg
|
||||||
|
|
||||||
|
Behavior on anchors.bottomMargin {
|
||||||
|
Anim {
|
||||||
|
duration: 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on anchors.leftMargin {
|
||||||
|
Anim {
|
||||||
|
duration: 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on anchors.rightMargin {
|
||||||
|
Anim {
|
||||||
|
duration: 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on anchors.topMargin {
|
||||||
|
Anim {
|
||||||
|
duration: 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: lockContent
|
id: lockContent
|
||||||
|
|
||||||
|
property int positionIndex: 0
|
||||||
|
readonly property var positions: [Qt.point(0, 0), Qt.point(4, 0), Qt.point(4, 4), Qt.point(0, 4), Qt.point(-4, 4), Qt.point(-4, 0), Qt.point(-4, -4), Qt.point(0, -4), Qt.point(4, -4),]
|
||||||
readonly property int radius: size / 4 * Appearance.rounding.scale
|
readonly property int radius: size / 4 * Appearance.rounding.scale
|
||||||
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
readonly property int size: lockIcon.implicitHeight + Appearance.padding.large * 4
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
anchors.horizontalCenterOffset: positions[positionIndex].x
|
||||||
|
anchors.verticalCenterOffset: positions[positionIndex].y
|
||||||
implicitHeight: size
|
implicitHeight: size
|
||||||
implicitWidth: size
|
implicitWidth: size
|
||||||
scale: 0
|
scale: 0
|
||||||
|
|
||||||
// MultiEffect {
|
Behavior on anchors.horizontalCenterOffset {
|
||||||
// anchors.fill: lockBg
|
Anim {
|
||||||
// autoPaddingEnabled: false
|
duration: 5000
|
||||||
// blur: 1
|
}
|
||||||
// blurEnabled: true
|
}
|
||||||
// blurMax: 64
|
Behavior on anchors.verticalCenterOffset {
|
||||||
// maskEnabled: true
|
Anim {
|
||||||
// maskSource: lockBg
|
duration: 5000
|
||||||
//
|
}
|
||||||
// source: ShaderEffectSource {
|
}
|
||||||
// sourceItem: background
|
|
||||||
// sourceRect: Qt.rect(lockBg.x, lockBg.y, lockBg.width, lockBg, height)
|
Timer {
|
||||||
// }
|
interval: 120000
|
||||||
// }
|
repeat: true
|
||||||
|
running: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
lockContent.positionIndex = (lockContent.positionIndex + 1) % lockContent.positions.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: lockBg
|
id: lockBg
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Networking
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Config
|
|
||||||
import qs.Modules
|
|
||||||
import qs.Helpers as Helpers
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property var wrapper
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: layout
|
|
||||||
|
|
||||||
spacing: 8
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: Helpers.Network.devices
|
|
||||||
|
|
||||||
CustomRadioButton {
|
|
||||||
id: network
|
|
||||||
|
|
||||||
required property NetworkDevice modelData
|
|
||||||
|
|
||||||
checked: Helpers.Network.activeDevice?.name === modelData.name
|
|
||||||
text: modelData.description
|
|
||||||
visible: modelData.name !== "lo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Components
|
|
||||||
import qs.Modules
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.top: parent.top
|
|
||||||
implicitWidth: layout.implicitWidth
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: layout
|
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.top: parent.top
|
|
||||||
|
|
||||||
MaterialIcon {
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
text: "android_wifi_4_bar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -133,7 +133,7 @@ CustomRect {
|
|||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardAccel
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
property: "scale"
|
property: "scale"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 0.7
|
to: 0.7
|
||||||
@@ -141,7 +141,7 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardAccel
|
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 0
|
to: 0
|
||||||
@@ -166,7 +166,7 @@ CustomRect {
|
|||||||
ParallelAnimation {
|
ParallelAnimation {
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardDecel
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
property: "scale"
|
property: "scale"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 1
|
to: 1
|
||||||
@@ -174,7 +174,7 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.small
|
duration: Appearance.anim.durations.small
|
||||||
easing: Appearance.anim.curves.standardDecel
|
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||||
property: "opacity"
|
property: "opacity"
|
||||||
target: listOrControls
|
target: listOrControls
|
||||||
to: 1
|
to: 1
|
||||||
@@ -217,14 +217,14 @@ CustomRect {
|
|||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.large
|
duration: Appearance.anim.durations.large
|
||||||
easing: Appearance.anim.curves.emphasizedAccel
|
easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
|
||||||
from: 1
|
from: 1
|
||||||
to: 0
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Anim {
|
Anim {
|
||||||
duration: Appearance.anim.durations.extraLarge
|
duration: Appearance.anim.durations.extraLarge
|
||||||
easing: Appearance.anim.curves.emphasizedDecel
|
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||||
from: 0
|
from: 0
|
||||||
to: 1
|
to: 1
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-37
@@ -1,8 +1,8 @@
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.Polkit
|
import Quickshell.Services.Polkit
|
||||||
import Quickshell.Wayland
|
import Quickshell.Wayland
|
||||||
import Quickshell.Hyprland
|
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
import ZShell.Components
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
@@ -243,17 +243,16 @@ Scope {
|
|||||||
Layout.preferredWidth: contentRow.implicitWidth
|
Layout.preferredWidth: contentRow.implicitWidth
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
CustomButton {
|
IconTextButton {
|
||||||
id: detailsButton
|
id: detailsButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
Layout.preferredHeight: 40
|
icon: "info"
|
||||||
Layout.preferredWidth: 92
|
inactiveColor: DynamicColors.palette.m3surfaceContainer
|
||||||
bgColor: DynamicColors.palette.m3surfaceContainer
|
inactiveOnColor: DynamicColors.palette.m3onSurface
|
||||||
enabled: true
|
isRound: true
|
||||||
radius: Appearance.rounding.full
|
shapeMorph: true
|
||||||
text: "Details"
|
text: "Details"
|
||||||
textColor: DynamicColors.palette.m3onSurface
|
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
panelWindow.detailsOpen = !panelWindow.detailsOpen;
|
panelWindow.detailsOpen = !panelWindow.detailsOpen;
|
||||||
@@ -266,41 +265,50 @@ Scope {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomButton {
|
ButtonRow {
|
||||||
id: okButton
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight
|
||||||
Layout.preferredHeight: 40
|
spacing: Appearance.spacing.normal
|
||||||
Layout.preferredWidth: 76
|
|
||||||
bgColor: DynamicColors.palette.m3primary
|
|
||||||
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
|
||||||
radius: Appearance.rounding.full
|
|
||||||
text: "OK"
|
|
||||||
textColor: DynamicColors.palette.m3onPrimary
|
|
||||||
|
|
||||||
onClicked: {
|
IconTextButton {
|
||||||
polkitAgent.flow.submit(passInput.text);
|
id: okButton
|
||||||
passInput.text = "";
|
|
||||||
passInput.forceActiveFocus();
|
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
||||||
|
horizontalPadding: Appearance.padding.large
|
||||||
|
icon: "check"
|
||||||
|
inactiveColor: DynamicColors.palette.m3primary
|
||||||
|
inactiveOnColor: DynamicColors.palette.m3onPrimary
|
||||||
|
isRound: true
|
||||||
|
isToggle: false
|
||||||
|
shapeMorph: true
|
||||||
|
shapeMorphExpansion: pressed ? 12 : 0
|
||||||
|
text: "OK"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
polkitAgent.flow.submit(passInput.text);
|
||||||
|
passInput.text = "";
|
||||||
|
passInput.forceActiveFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CustomButton {
|
IconTextButton {
|
||||||
id: cancelButton
|
id: cancelButton
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignRight
|
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
||||||
Layout.preferredHeight: 40
|
horizontalPadding: Appearance.padding.large
|
||||||
Layout.preferredWidth: 76
|
icon: "close"
|
||||||
bgColor: DynamicColors.palette.m3surfaceContainer
|
inactiveColor: DynamicColors.palette.m3surfaceContainer
|
||||||
enabled: passInput.text.length > 0 || !!polkitAgent.flow?.isResponseRequired
|
inactiveOnColor: DynamicColors.palette.m3onSurface
|
||||||
radius: Appearance.rounding.full
|
isRound: true
|
||||||
text: "Cancel"
|
isToggle: false
|
||||||
textColor: DynamicColors.palette.m3onSurface
|
shapeMorph: true
|
||||||
|
shapeMorphExpansion: pressed ? 12 : 0
|
||||||
|
text: "Cancel"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.shouldShow = false;
|
root.shouldShow = false;
|
||||||
polkitAgent.flow.cancelAuthenticationRequest();
|
polkitAgent.flow.cancelAuthenticationRequest();
|
||||||
passInput.text = "";
|
passInput.text = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ CustomRect {
|
|||||||
|
|
||||||
readonly property color accent: DynamicColors.palette.m3tertiary
|
readonly property color accent: DynamicColors.palette.m3tertiary
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
||||||
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased * 2
|
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased * 2
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import ZShell.Internal
|
import ZShell.Internal
|
||||||
import qs.Modules.Resources
|
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
@@ -9,8 +8,6 @@ import qs.Config
|
|||||||
CustomRect {
|
CustomRect {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property Wrapper wrapper
|
|
||||||
|
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
implicitHeight: 220
|
implicitHeight: 220
|
||||||
implicitWidth: 300
|
implicitWidth: 300
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import ZShell.Services
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property color accent: DynamicColors.palette.m3tertiary
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
implicitHeight: layout.implicitHeight + Appearance.padding.large * 2
|
||||||
|
implicitWidth: layout.implicitWidth + Appearance.padding.extraLargeIncreased
|
||||||
|
radius: Appearance.rounding.medium
|
||||||
|
|
||||||
|
ServiceRef {
|
||||||
|
service: Gpu
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: layout
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Appearance.spacing.extraSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.leftMargin: -Appearance.padding.extraSmall
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: root.accent
|
||||||
|
fill: 1
|
||||||
|
text: "memory_alt"
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
text: qsTr("Video memory")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CircularProgress {
|
||||||
|
id: circularIndicator
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.topMargin: Appearance.spacing.large
|
||||||
|
fgColor: root.accent
|
||||||
|
implicitSize: usageColumn.implicitHeight + thickness + Appearance.padding.largeIncreased * 2
|
||||||
|
startAngle: -225
|
||||||
|
sweepAngle: 270
|
||||||
|
value: Gpu.memoryUsed / Gpu.memoryTotal
|
||||||
|
|
||||||
|
Behavior on clampedVal {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: usageColumn
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
anchors.verticalCenterOffset: Appearance.padding.extraSmall
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: root.accent
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: Math.round(circularIndicator.value * 100) + "%"
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: qsTr("Used")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
text: {
|
||||||
|
const fmt = UsageFmt.formatKib(Gpu.memoryUsed, Gpu.memoryTotal);
|
||||||
|
return `${fmt.value.toFixed(1)} / ${Math.floor(fmt.total)} ${fmt.unit}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ Item {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
visible: storageCard.active || networkCard.active || memoryCard.active
|
visible: storageCard.active || memoryCard.active || vramCard.active || networkCard1.active
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
id: storageCard
|
id: storageCard
|
||||||
@@ -95,15 +95,32 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
id: networkCard
|
id: vramCard
|
||||||
|
|
||||||
active: Config.dashboard.performance.showNetwork
|
active: Config.dashboard.performance.showVram
|
||||||
|
|
||||||
|
sourceComponent: VramCard {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WrappedLoader {
|
||||||
|
id: networkCard1
|
||||||
|
|
||||||
|
active: Config.dashboard.performance.showNetwork && !vramCard.active
|
||||||
|
|
||||||
sourceComponent: NetworkCard {
|
sourceComponent: NetworkCard {
|
||||||
wrapper: root.wrapper
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WrappedLoader {
|
||||||
|
id: networkCard2
|
||||||
|
|
||||||
|
active: Config.dashboard.performance.showNetwork && vramCard.active
|
||||||
|
|
||||||
|
sourceComponent: NetworkCard {
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WrappedLoader {
|
WrappedLoader {
|
||||||
|
|||||||
@@ -3,12 +3,64 @@ import qs.Components
|
|||||||
import qs.Config
|
import qs.Config
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
property bool first
|
property bool first
|
||||||
property bool last
|
property bool last
|
||||||
|
property string settingAnchor
|
||||||
|
|
||||||
|
function flashHighlight(): void {
|
||||||
|
flash.restart();
|
||||||
|
}
|
||||||
|
|
||||||
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
color: DynamicColors.tPalette.m3surfaceContainer
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: highlight
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
bottomLeftRadius: parent.bottomLeftRadius
|
||||||
|
bottomRightRadius: parent.bottomRightRadius
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
opacity: 0
|
||||||
|
radius: parent.radius
|
||||||
|
topLeftRadius: parent.topLeftRadius
|
||||||
|
topRightRadius: parent.topRightRadius
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
id: flash
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.08
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.small
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
duration: Appearance.anim.durations.extraLarge
|
||||||
|
property: "opacity"
|
||||||
|
target: highlight
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import qs.Config
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
property bool animateScroll: false
|
||||||
readonly property int cappedWidth: Math.min(800, width)
|
readonly property int cappedWidth: Math.min(800, width)
|
||||||
default property Item contentChild
|
default property Item contentChild
|
||||||
readonly property alias flickable: flickable
|
readonly property alias flickable: flickable
|
||||||
@@ -16,11 +17,100 @@ ColumnLayout {
|
|||||||
required property SettingsState sState
|
required property SettingsState sState
|
||||||
required property string title
|
required property string title
|
||||||
|
|
||||||
|
function applySearchAnchor(): void {
|
||||||
|
if (!sState.searchAnchor)
|
||||||
|
return;
|
||||||
|
scrollRetry.tries = 0;
|
||||||
|
scrollRetry.lastHeight = -1;
|
||||||
|
scrollRetry.stableFrames = 0;
|
||||||
|
scrollRetry.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function findAnchor(item: Item, anchor: string): Item {
|
||||||
|
if (!item)
|
||||||
|
return null;
|
||||||
|
if (item.settingAnchor !== undefined && item.settingAnchor === anchor)
|
||||||
|
return item;
|
||||||
|
const kids = item.children;
|
||||||
|
for (let i = 0; i < kids.length; i++) {
|
||||||
|
const found = findAnchor(kids[i], anchor);
|
||||||
|
if (found)
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightAnchor(anchor: string): void {
|
||||||
|
const row = findAnchor(contentChild, anchor);
|
||||||
|
if (row && row.flashHighlight !== undefined)
|
||||||
|
row.flashHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToAnchor(anchor: string): bool {
|
||||||
|
if (!anchor || !contentChild)
|
||||||
|
return false;
|
||||||
|
const row = findAnchor(contentChild, anchor);
|
||||||
|
if (!row)
|
||||||
|
return false;
|
||||||
|
const pos = row.mapToItem(flickable.contentItem, 0, 0);
|
||||||
|
const inset = flickable.height * flickable.fadeAmount + Appearance.padding.large;
|
||||||
|
const minY = -flickable.topMargin;
|
||||||
|
const maxY = Math.max(minY, flickable.contentHeight + flickable.bottomMargin - flickable.height);
|
||||||
|
const target = Math.max(minY, Math.min(pos.y - inset, maxY));
|
||||||
|
root.animateScroll = true;
|
||||||
|
flickable.contentY = target;
|
||||||
|
Qt.callLater(() => root.animateScroll = false);
|
||||||
|
if (row.flashHighlight !== undefined)
|
||||||
|
row.flashHighlight();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
spacing: Appearance.spacing.large
|
spacing: Appearance.spacing.large
|
||||||
|
|
||||||
MouseArea { // Prevent clicks from reaching flickable
|
Component.onCompleted: applySearchAnchor()
|
||||||
Layout.bottomMargin: -flickable.topMargin // Extra height to block clicks on flickable top margin
|
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: scrollRetry
|
||||||
|
|
||||||
|
property real lastHeight: -1
|
||||||
|
property int stableFrames: 0
|
||||||
|
property int tries: 0
|
||||||
|
|
||||||
|
interval: 16
|
||||||
|
repeat: true
|
||||||
|
|
||||||
|
onTriggered: {
|
||||||
|
const h = flickable.contentHeight;
|
||||||
|
if (h === lastHeight && h > flickable.height)
|
||||||
|
stableFrames++;
|
||||||
|
else
|
||||||
|
stableFrames = 0;
|
||||||
|
lastHeight = h;
|
||||||
|
|
||||||
|
const ready = stableFrames >= 3 || tries >= 30;
|
||||||
|
if (ready) {
|
||||||
|
if (root.scrollToAnchor(root.sState.searchAnchor))
|
||||||
|
root.sState.searchAnchor = "";
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onHighlightSetting(anchor: string): void {
|
||||||
|
root.highlightAnchor(anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearchAnchorChanged(): void {
|
||||||
|
root.applySearchAnchor();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: root.sState
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
Layout.bottomMargin: -flickable.topMargin
|
||||||
implicitHeight: header.implicitHeight - Layout.bottomMargin
|
implicitHeight: header.implicitHeight - Layout.bottomMargin
|
||||||
implicitWidth: header.implicitWidth
|
implicitWidth: header.implicitWidth
|
||||||
z: 1
|
z: 1
|
||||||
@@ -69,6 +159,14 @@ ColumnLayout {
|
|||||||
fadeAmount: 0.1
|
fadeAmount: 0.1
|
||||||
topMargin: Appearance.padding.large
|
topMargin: Appearance.padding.large
|
||||||
|
|
||||||
|
Behavior on contentY {
|
||||||
|
enabled: root.animateScroll
|
||||||
|
|
||||||
|
Anim {
|
||||||
|
type: Anim.DefaultSpatial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
onTapped: flickable.focus = true
|
onTapped: flickable.focus = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,13 @@ CustomSwitch {
|
|||||||
readonly property alias bg: bg
|
readonly property alias bg: bg
|
||||||
property alias first: bg.first
|
property alias first: bg.first
|
||||||
property alias last: bg.last
|
property alias last: bg.last
|
||||||
|
property string settingAnchor
|
||||||
property string subtext
|
property string subtext
|
||||||
|
|
||||||
|
function flashHighlight(): void {
|
||||||
|
bg.flashHighlight();
|
||||||
|
}
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
cLayer: 2
|
cLayer: 2
|
||||||
horizontalPadding: Appearance.padding.largeIncreased
|
horizontalPadding: Appearance.padding.largeIncreased
|
||||||
|
|||||||
@@ -267,8 +267,6 @@ Item {
|
|||||||
function restoreFromData() {
|
function restoreFromData() {
|
||||||
let data = Wallpapers.getCrop(wrapper.currentScreen.name);
|
let data = Wallpapers.getCrop(wrapper.currentScreen.name);
|
||||||
|
|
||||||
console.log(data.x, data.y);
|
|
||||||
|
|
||||||
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
|
if (data && (Math.abs(data.x) > 0.001 || Math.abs(data.y) > 0.001 || Math.abs(data.width - 1.0) > 0.001 || Math.abs(data.height - 1.0) > 0.001)) {
|
||||||
zoom = data.zoom > 0 ? data.zoom : 1.0;
|
zoom = data.zoom > 0 ? data.zoom : 1.0;
|
||||||
x = imageX + (data.x * scaledImg.paintedWidth);
|
x = imageX + (data.x * scaledImg.paintedWidth);
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ ColumnLayout {
|
|||||||
target: root.sState
|
target: root.sState
|
||||||
value: searchField.text.length > 0
|
value: searchField.text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
property: "searchText"
|
||||||
|
target: root.sState
|
||||||
|
value: searchField.text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NavLocations {
|
NavLocations {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
|||||||
|
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
@@ -9,7 +10,33 @@ import qs.Modules.Settings
|
|||||||
VerticalFadeFlickable {
|
VerticalFadeFlickable {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
readonly property var groups: {
|
||||||
|
const out = [];
|
||||||
|
const byPage = ({});
|
||||||
|
for (const e of results) {
|
||||||
|
const key = e.pageIdx;
|
||||||
|
if (byPage[key] === undefined) {
|
||||||
|
byPage[key] = {
|
||||||
|
"pageIdx": e.pageIdx,
|
||||||
|
"page": e.crumbLabels[0],
|
||||||
|
"icon": e.crumbIcons[0],
|
||||||
|
"entries": []
|
||||||
|
};
|
||||||
|
out.push(byPage[key]);
|
||||||
|
}
|
||||||
|
byPage[key].entries.push(e);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
readonly property var results: {
|
||||||
|
if (!searching)
|
||||||
|
return [];
|
||||||
|
const all = SettingsSearcher.query(search);
|
||||||
|
return all;
|
||||||
|
}
|
||||||
required property SettingsState sState
|
required property SettingsState sState
|
||||||
|
readonly property string search: sState.searchText
|
||||||
|
readonly property bool searching: search.length > 0
|
||||||
|
|
||||||
bottomMargin: Appearance.padding.large
|
bottomMargin: Appearance.padding.large
|
||||||
contentHeight: content.implicitHeight
|
contentHeight: content.implicitHeight
|
||||||
@@ -30,14 +57,14 @@ VerticalFadeFlickable {
|
|||||||
Repeater {
|
Repeater {
|
||||||
id: list
|
id: list
|
||||||
|
|
||||||
model: PageRegistry.pages
|
model: root.searching ? [] : PageRegistry.pages
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
id: item
|
id: item
|
||||||
|
|
||||||
required property int index
|
required property int index
|
||||||
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1].category !== modelData.category
|
readonly property bool isCategoryEnd: index === list.model.length - 1 || PageRegistry.pages[index + 1]?.category !== modelData.category
|
||||||
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1].category !== modelData.category
|
readonly property bool isCategoryStart: index === 0 || PageRegistry.pages[index - 1]?.category !== modelData.category
|
||||||
readonly property bool isCurrentPage: index === root.sState.currentPageIdx
|
readonly property bool isCurrentPage: index === root.sState.currentPageIdx
|
||||||
required property var modelData
|
required property var modelData
|
||||||
|
|
||||||
@@ -120,6 +147,166 @@ VerticalFadeFlickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: resultList
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
cacheBuffer: 10000
|
||||||
|
implicitHeight: contentHeight
|
||||||
|
interactive: false
|
||||||
|
spacing: Appearance.padding.large
|
||||||
|
|
||||||
|
delegate: ColumnLayout {
|
||||||
|
id: group
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
width: resultList.width
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Appearance.padding.small
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: group.modelData.icon
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3primary
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: group.modelData.page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Appearance.spacing.extraSmall / 2
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: group.modelData.entries
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
id: result
|
||||||
|
|
||||||
|
required property int index
|
||||||
|
readonly property bool isFirst: index === 0
|
||||||
|
readonly property bool isLast: index === group.modelData.entries.length - 1
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
bottomLeftRadius: layer.pressed ? Appearance.rounding.medium : isLast ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
bottomRightRadius: layer.pressed ? Appearance.rounding.medium : isLast ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
|
||||||
|
implicitHeight: {
|
||||||
|
const h = resultLayout.implicitHeight + resultLayout.anchors.margins * 2;
|
||||||
|
return h % 2 === 0 ? h : h + 1;
|
||||||
|
}
|
||||||
|
topLeftRadius: layer.pressed ? Appearance.rounding.medium : isFirst ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
topRightRadius: layer.pressed ? Appearance.rounding.medium : isFirst ? Appearance.rounding.large : Appearance.rounding.extraSmall
|
||||||
|
|
||||||
|
RadiusBehavior on bottomLeftRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on bottomRightRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on topLeftRadius {
|
||||||
|
}
|
||||||
|
RadiusBehavior on topRightRadius {
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: resultLayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Appearance.padding.large
|
||||||
|
anchors.rightMargin: result.modelData.togglePath ? toggle.width + Appearance.padding.large * 2 : Appearance.padding.large
|
||||||
|
spacing: Appearance.spacing.small / 2
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
text: {
|
||||||
|
const labels = result.modelData.crumbLabels.slice(1);
|
||||||
|
const section = result.modelData.section;
|
||||||
|
const parts = section && section !== labels[labels.length - 1] ? labels.concat(section) : labels;
|
||||||
|
return parts.join(" \u203a ");
|
||||||
|
}
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3onSurface
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.medium
|
||||||
|
text: SettingsSearcher.highlight(result.modelData.title, root.search, DynamicColors.palette.m3primary)
|
||||||
|
textFormat: text.includes("<font") ? Text.StyledText : Text.PlainText
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
color: DynamicColors.palette.m3outline
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
text: SettingsSearcher.highlight(result.modelData.subtext, root.search, DynamicColors.palette.m3primary)
|
||||||
|
textFormat: text.includes("<font") ? Text.StyledText : Text.PlainText
|
||||||
|
visible: result.modelData.subtext.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
id: layer
|
||||||
|
|
||||||
|
z: 1
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.sState.jumpToSetting(result.modelData.pageIdx, result.modelData.subPath, result.modelData.anchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
id: toggle
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Appearance.padding.large
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
cLayer: 3
|
||||||
|
checked: result.modelData.toggleValue
|
||||||
|
scale: 0.85
|
||||||
|
transformOrigin: Item.Right
|
||||||
|
visible: result.modelData.togglePath
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
onToggled: result.modelData.setToggle(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
model: ScriptModel {
|
||||||
|
objectProp: "pageIdx"
|
||||||
|
values: root.groups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Appearance.padding.large
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.medium
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: qsTr("No matching settings")
|
||||||
|
visible: root.searching && root.results.length === 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component RadiusBehavior: Behavior {
|
component RadiusBehavior: Behavior {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ PageBase {
|
|||||||
DefaultRow {
|
DefaultRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "terminal"
|
icon: "terminal"
|
||||||
|
settingAnchor: "apps-default-terminal"
|
||||||
status: Config.general.apps.terminal.join(" ")
|
status: Config.general.apps.terminal.join(" ")
|
||||||
text: qsTr("Terminal")
|
text: qsTr("Terminal")
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ PageBase {
|
|||||||
|
|
||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "volume_up"
|
icon: "volume_up"
|
||||||
|
settingAnchor: "apps-default-audio"
|
||||||
status: Config.general.apps.audio.join(" ")
|
status: Config.general.apps.audio.join(" ")
|
||||||
text: qsTr("Audio")
|
text: qsTr("Audio")
|
||||||
|
|
||||||
@@ -46,6 +48,7 @@ PageBase {
|
|||||||
|
|
||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "play_circle"
|
icon: "play_circle"
|
||||||
|
settingAnchor: "apps-default-playback"
|
||||||
status: Config.general.apps.playback.join(" ")
|
status: Config.general.apps.playback.join(" ")
|
||||||
text: qsTr("Media playback")
|
text: qsTr("Media playback")
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ PageBase {
|
|||||||
DefaultRow {
|
DefaultRow {
|
||||||
icon: "folder"
|
icon: "folder"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "apps-default-file-manager"
|
||||||
status: Config.general.apps.explorer.join(" ")
|
status: Config.general.apps.explorer.join(" ")
|
||||||
text: qsTr("File manager")
|
text: qsTr("File manager")
|
||||||
|
|
||||||
@@ -70,6 +74,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: "apps"
|
icon: "apps"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "apps-all-apps"
|
||||||
status: qsTr("Browse installed apps, set favorites and hidden")
|
status: qsTr("Browse installed apps, set favorites and hidden")
|
||||||
text: qsTr("All apps")
|
text: qsTr("All apps")
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
icon: Icons.getVolumeIcon(Audio.volume, Audio.muted)
|
||||||
label: qsTr("Output")
|
label: qsTr("Output")
|
||||||
|
settingAnchor: "audio-output"
|
||||||
value: Audio.volume
|
value: Audio.volume
|
||||||
valueLabel: Math.round(value * 100) + "%"
|
valueLabel: Math.round(value * 100) + "%"
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
icon: Icons.getMicVolumeIcon(Audio.sourceVolume, Audio.sourceMuted)
|
||||||
label: qsTr("Input")
|
label: qsTr("Input")
|
||||||
|
settingAnchor: "audio-input"
|
||||||
value: Audio.sourceVolume
|
value: Audio.sourceVolume
|
||||||
valueLabel: Math.round(value * 100) + "%"
|
valueLabel: Math.round(value * 100) + "%"
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
menuItems: root.clockFormats
|
menuItems: root.clockFormats
|
||||||
|
settingAnchor: "bar-clock-format"
|
||||||
subtext: qsTr("Change how time is displayed in the widget")
|
subtext: qsTr("Change how time is displayed in the widget")
|
||||||
text: qsTr("Time format")
|
text: qsTr("Time format")
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showAudio
|
checked: Config.bar.tray.showAudio
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "bar-status-speakers"
|
||||||
text: qsTr("Speakers")
|
text: qsTr("Speakers")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showAudio = checked
|
onToggled: Config.bar.tray.showAudio = checked
|
||||||
@@ -32,18 +33,20 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showMicrophone
|
checked: Config.bar.tray.showMicrophone
|
||||||
|
settingAnchor: "bar-status-mic"
|
||||||
text: qsTr("Microphone")
|
text: qsTr("Microphone")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showMicrophone = checked
|
onToggled: Config.bar.tray.showMicrophone = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.bar.tray.showNetwork
|
||||||
|
text: qsTr("Network")
|
||||||
|
|
||||||
|
onToggled: Config.bar.tray.showNetwork = checked
|
||||||
|
}
|
||||||
|
|
||||||
//////// FOR LATER:
|
//////// FOR LATER:
|
||||||
// ToggleRow {
|
|
||||||
// checked: Config.bar.tray.showNetwork
|
|
||||||
// text: qsTr("Network")
|
|
||||||
//
|
|
||||||
// onToggled: Config.bar.tray.showNetwork = checked
|
|
||||||
// }
|
|
||||||
//
|
//
|
||||||
// ToggleRow {
|
// ToggleRow {
|
||||||
// checked: Config.bar.tray.showWifi
|
// checked: Config.bar.tray.showWifi
|
||||||
@@ -62,6 +65,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.tray.showPower
|
checked: Config.bar.tray.showPower
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-status-power"
|
||||||
text: qsTr("Battery")
|
text: qsTr("Battery")
|
||||||
|
|
||||||
onToggled: Config.bar.tray.showPower = checked
|
onToggled: Config.bar.tray.showPower = checked
|
||||||
@@ -75,6 +79,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.popouts.audio
|
checked: Config.bar.popouts.audio
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "bar-status-audio-popout"
|
||||||
subtext: qsTr("Show a details popout when hovering the audio icons")
|
subtext: qsTr("Show a details popout when hovering the audio icons")
|
||||||
text: qsTr("Audio popout on hover")
|
text: qsTr("Audio popout on hover")
|
||||||
|
|
||||||
@@ -84,10 +89,21 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.bar.popouts.upower
|
checked: Config.bar.popouts.upower
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-status-power-popout"
|
||||||
subtext: qsTr("Show a details popout when hovering the power icon")
|
subtext: qsTr("Show a details popout when hovering the power icon")
|
||||||
text: qsTr("Power popout on hover")
|
text: qsTr("Power popout on hover")
|
||||||
|
|
||||||
onToggled: Config.bar.popouts.upower = checked
|
onToggled: Config.bar.popouts.upower = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.bar.popouts.network
|
||||||
|
last: true
|
||||||
|
settingAnchor: "bar-status-network-popout"
|
||||||
|
subtext: qsTr("Show a details popout when hovering the network icon")
|
||||||
|
text: qsTr("Network popout on hover")
|
||||||
|
|
||||||
|
onToggled: Config.bar.popouts.network = checked
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 12
|
from: 12
|
||||||
last: true
|
settingAnchor: "bar-tray-iconsize"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Icon size")
|
text: qsTr("Icon size")
|
||||||
to: 24
|
to: 24
|
||||||
@@ -27,5 +27,24 @@ PageBase {
|
|||||||
|
|
||||||
onMoved: value => Config.bar.tray.trayIconSize = value
|
onMoved: value => Config.bar.tray.trayIconSize = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.bar.tray.showOnHover
|
||||||
|
settingAnchor: "bar-tray-show-popout-on-hover"
|
||||||
|
subtext: Config.bar.tray.showOnHover ? qsTr("Will show context menu on hover") : qsTr("Will show context menu on right-click")
|
||||||
|
text: qsTr("Show popout on hover")
|
||||||
|
|
||||||
|
onToggled: Config.bar.tray.showOnHover = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.bar.tray.recolorIcons
|
||||||
|
last: true
|
||||||
|
settingAnchor: "bar-tray-recolor-icons"
|
||||||
|
subtext: qsTr("Recolors icons to fit current scheme")
|
||||||
|
text: qsTr("Recolor icons")
|
||||||
|
|
||||||
|
onToggled: Config.bar.tray.recolorIcons = checked
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ PageBase {
|
|||||||
checked: Config.bar.autoHide
|
checked: Config.bar.autoHide
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-autohide"
|
||||||
subtext: qsTr("Hide the bar, reveal on hover")
|
subtext: qsTr("Hide the bar, reveal on hover")
|
||||||
text: qsTr("Auto hide")
|
text: qsTr("Auto hide")
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "widgets"
|
icon: "widgets"
|
||||||
|
settingAnchor: "bar-tray"
|
||||||
status: qsTr("System tray icons")
|
status: qsTr("System tray icons")
|
||||||
text: qsTr("Tray")
|
text: qsTr("Tray")
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "signal_cellular_alt"
|
icon: "signal_cellular_alt"
|
||||||
|
settingAnchor: "bar-statusicons"
|
||||||
status: qsTr("Visible indicators")
|
status: qsTr("Visible indicators")
|
||||||
text: qsTr("Status icons")
|
text: qsTr("Status icons")
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
icon: "schedule"
|
icon: "schedule"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "bar-clock"
|
||||||
status: qsTr("Date, icon, background")
|
status: qsTr("Date, icon, background")
|
||||||
text: qsTr("Clock")
|
text: qsTr("Clock")
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.dashboard.enabled
|
checked: Config.dashboard.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "dashboard-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.dashboard.enabled = checked
|
onToggled: Config.dashboard.enabled = checked
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.launcher.enabled
|
checked: Config.launcher.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.launcher.enabled = checked
|
onToggled: Config.launcher.enabled = checked
|
||||||
@@ -41,6 +42,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "launcher-max-items-shown"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Max items shown")
|
text: qsTr("Max items shown")
|
||||||
to: 20
|
to: 20
|
||||||
@@ -51,6 +53,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "launcher-max-wallpapers"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Max wallpapers")
|
text: qsTr("Max wallpapers")
|
||||||
to: 30
|
to: 30
|
||||||
@@ -62,6 +65,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-drag-threshold"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Pixels dragged before the launcher opens")
|
subtext: qsTr("Pixels dragged before the launcher opens")
|
||||||
text: qsTr("Drag threshold")
|
text: qsTr("Drag threshold")
|
||||||
@@ -80,6 +84,7 @@ PageBase {
|
|||||||
checked: Config.launcher.enableDangerousActions
|
checked: Config.launcher.enableDangerousActions
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-enable-dangerous-actions"
|
||||||
subtext: qsTr("Allow actions that shut down or log out")
|
subtext: qsTr("Allow actions that shut down or log out")
|
||||||
text: qsTr("Enable dangerous actions")
|
text: qsTr("Enable dangerous actions")
|
||||||
|
|
||||||
@@ -94,6 +99,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.apps
|
checked: Config.launcher.useFuzzy.apps
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "launcher-apps"
|
||||||
text: qsTr("Apps")
|
text: qsTr("Apps")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.apps = checked
|
onToggled: Config.launcher.useFuzzy.apps = checked
|
||||||
@@ -101,20 +107,15 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.actions
|
checked: Config.launcher.useFuzzy.actions
|
||||||
|
settingAnchor: "launcher-actions"
|
||||||
text: qsTr("Actions")
|
text: qsTr("Actions")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.actions = checked
|
onToggled: Config.launcher.useFuzzy.actions = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleRow {
|
|
||||||
checked: Config.launcher.useFuzzy.schemes
|
|
||||||
text: qsTr("Schemes")
|
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.schemes = checked
|
|
||||||
}
|
|
||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.variants
|
checked: Config.launcher.useFuzzy.variants
|
||||||
|
settingAnchor: "launcher-variants"
|
||||||
text: qsTr("Variants")
|
text: qsTr("Variants")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.variants = checked
|
onToggled: Config.launcher.useFuzzy.variants = checked
|
||||||
@@ -123,6 +124,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.launcher.useFuzzy.wallpapers
|
checked: Config.launcher.useFuzzy.wallpapers
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "launcher-wallpapers"
|
||||||
text: qsTr("Wallpapers")
|
text: qsTr("Wallpapers")
|
||||||
|
|
||||||
onToggled: Config.launcher.useFuzzy.wallpapers = checked
|
onToggled: Config.launcher.useFuzzy.wallpapers = checked
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
checked: Config.dashboard.performance.enabled
|
checked: Config.dashboard.performance.enabled
|
||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "resources-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.enabled = checked
|
onToggled: Config.dashboard.performance.enabled = checked
|
||||||
@@ -41,6 +42,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showBattery
|
checked: Config.dashboard.performance.showBattery
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "resources-battery"
|
||||||
text: qsTr("Battery")
|
text: qsTr("Battery")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showBattery = checked
|
onToggled: Config.dashboard.performance.showBattery = checked
|
||||||
@@ -48,13 +50,23 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showGpu
|
checked: Config.dashboard.performance.showGpu
|
||||||
|
settingAnchor: "resources-gpu"
|
||||||
text: qsTr("GPU")
|
text: qsTr("GPU")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showGpu = checked
|
onToggled: Config.dashboard.performance.showGpu = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ToggleRow {
|
||||||
|
checked: Config.dashboard.performance.showVram
|
||||||
|
settingAnchor: "resources-vram"
|
||||||
|
text: qsTr("Video memory")
|
||||||
|
|
||||||
|
onToggled: Config.dashboard.performance.showVram = checked
|
||||||
|
}
|
||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showCpu
|
checked: Config.dashboard.performance.showCpu
|
||||||
|
settingAnchor: "resources-cpu"
|
||||||
text: qsTr("CPU")
|
text: qsTr("CPU")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showCpu = checked
|
onToggled: Config.dashboard.performance.showCpu = checked
|
||||||
@@ -62,6 +74,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showMemory
|
checked: Config.dashboard.performance.showMemory
|
||||||
|
settingAnchor: "resources-memory"
|
||||||
text: qsTr("Memory")
|
text: qsTr("Memory")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showMemory = checked
|
onToggled: Config.dashboard.performance.showMemory = checked
|
||||||
@@ -69,6 +82,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showStorage
|
checked: Config.dashboard.performance.showStorage
|
||||||
|
settingAnchor: "resources-storage"
|
||||||
text: qsTr("Storage")
|
text: qsTr("Storage")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showStorage = checked
|
onToggled: Config.dashboard.performance.showStorage = checked
|
||||||
@@ -77,6 +91,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.dashboard.performance.showNetwork
|
checked: Config.dashboard.performance.showNetwork
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "resources-network"
|
||||||
text: qsTr("Network")
|
text: qsTr("Network")
|
||||||
|
|
||||||
onToggled: Config.dashboard.performance.showNetwork = checked
|
onToggled: Config.dashboard.performance.showNetwork = checked
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.sidebar.enabled
|
checked: Config.sidebar.enabled
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "sidebar-enabled"
|
||||||
text: qsTr("Enabled")
|
text: qsTr("Enabled")
|
||||||
|
|
||||||
onToggled: Config.sidebar.enabled = checked
|
onToggled: Config.sidebar.enabled = checked
|
||||||
@@ -34,6 +35,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "sidebar-drag-threshold"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Pixels dragged before the sidebar opens")
|
subtext: qsTr("Pixels dragged before the sidebar opens")
|
||||||
text: qsTr("Drag threshold")
|
text: qsTr("Drag threshold")
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
first: true
|
first: true
|
||||||
icon: "dock_to_bottom"
|
icon: "dock_to_bottom"
|
||||||
|
settingAnchor: "panels-bar"
|
||||||
status: !Config.bar.autoHide ? qsTr("Always visible") : qsTr("Reveal on hover")
|
status: !Config.bar.autoHide ? qsTr("Always visible") : qsTr("Reveal on hover")
|
||||||
text: qsTr("Bar")
|
text: qsTr("Bar")
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "dashboard"
|
icon: "dashboard"
|
||||||
|
settingAnchor: "panels-dashboard"
|
||||||
status: Config.dashboard.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.dashboard.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Dashboard")
|
text: qsTr("Dashboard")
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "insert_chart"
|
icon: "insert_chart"
|
||||||
|
settingAnchor: "panels-resources"
|
||||||
status: Config.dashboard.performance.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.dashboard.performance.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Resources")
|
text: qsTr("Resources")
|
||||||
|
|
||||||
@@ -40,6 +43,7 @@ PageBase {
|
|||||||
|
|
||||||
NavRow {
|
NavRow {
|
||||||
icon: "apps"
|
icon: "apps"
|
||||||
|
settingAnchor: "panels-launcher"
|
||||||
status: Config.launcher.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.launcher.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Launcher")
|
text: qsTr("Launcher")
|
||||||
|
|
||||||
@@ -49,6 +53,7 @@ PageBase {
|
|||||||
NavRow {
|
NavRow {
|
||||||
icon: "dock_to_right"
|
icon: "dock_to_right"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "panels-sidebar"
|
||||||
status: Config.sidebar.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
status: Config.sidebar.enabled ? qsTr("Enabled") : qsTr("Disabled")
|
||||||
text: qsTr("Sidebar")
|
text: qsTr("Sidebar")
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.screenshot.enable_pp
|
checked: Config.screenshot.enable_pp
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable"
|
||||||
text: qsTr("Enable effects")
|
text: qsTr("Enable effects")
|
||||||
|
|
||||||
onToggled: Config.screenshot.enable_pp = checked
|
onToggled: Config.screenshot.enable_pp = checked
|
||||||
@@ -37,6 +38,7 @@ PageBase {
|
|||||||
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
active: Config.screenshot.mode === "manual" ? menuItems[0] : menuItems[1]
|
||||||
enabled: Config.screenshot.enable_pp
|
enabled: Config.screenshot.enable_pp
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-mode"
|
||||||
subtext: qsTr("Automatic or manual effect values")
|
subtext: qsTr("Automatic or manual effect values")
|
||||||
text: qsTr("Effects mode")
|
text: qsTr("Effects mode")
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ PageBase {
|
|||||||
checked: Config.screenshot.rounding
|
checked: Config.screenshot.rounding
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable-rounded-corners"
|
||||||
text: qsTr("Enable rounded corners")
|
text: qsTr("Enable rounded corners")
|
||||||
|
|
||||||
onToggled: Config.screenshot.rounding = checked
|
onToggled: Config.screenshot.rounding = checked
|
||||||
@@ -76,6 +79,7 @@ PageBase {
|
|||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.rounding
|
||||||
from: 0
|
from: 0
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-corner-radius"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Corner radius")
|
text: qsTr("Corner radius")
|
||||||
to: 50
|
to: 50
|
||||||
@@ -95,6 +99,7 @@ PageBase {
|
|||||||
checked: Config.screenshot.shadow
|
checked: Config.screenshot.shadow
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual"
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "screenshot-enable-shadow"
|
||||||
text: qsTr("Enable shadow")
|
text: qsTr("Enable shadow")
|
||||||
|
|
||||||
onToggled: Config.screenshot.shadow = checked
|
onToggled: Config.screenshot.shadow = checked
|
||||||
@@ -103,6 +108,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: 0
|
from: 0
|
||||||
|
settingAnchor: "screenshot-shadow-blur-amount"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
text: qsTr("Shadow blur amount")
|
text: qsTr("Shadow blur amount")
|
||||||
to: 100
|
to: 100
|
||||||
@@ -117,6 +123,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: -100
|
from: -100
|
||||||
|
settingAnchor: "screenshot-shadow-horizontal-offset"
|
||||||
stepSize: 10
|
stepSize: 10
|
||||||
text: qsTr("Shadow horizontal offset")
|
text: qsTr("Shadow horizontal offset")
|
||||||
to: 100
|
to: 100
|
||||||
@@ -132,6 +139,7 @@ PageBase {
|
|||||||
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
enabled: Config.screenshot.enable_pp && Config.screenshot.mode === "manual" && Config.screenshot.shadow
|
||||||
from: -100
|
from: -100
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "screenshot-shadow-vertical-offset"
|
||||||
stepSize: 10
|
stepSize: 10
|
||||||
text: qsTr("Shadow vertical offset")
|
text: qsTr("Shadow vertical offset")
|
||||||
to: 100
|
to: 100
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.showInFullscreen
|
checked: Config.notifs.showInFullscreen
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "notif-show-in-fullscreen"
|
||||||
subtext: qsTr("Whether notifications appear over fullscreen apps")
|
subtext: qsTr("Whether notifications appear over fullscreen apps")
|
||||||
text: qsTr("Show in fullscreen")
|
text: qsTr("Show in fullscreen")
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.expire
|
checked: Config.notifs.expire
|
||||||
|
settingAnchor: "notif-expire-automatically"
|
||||||
subtext: qsTr("Dismiss notifications after their timeout")
|
subtext: qsTr("Dismiss notifications after their timeout")
|
||||||
text: qsTr("Expire automatically")
|
text: qsTr("Expire automatically")
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.notifs.openExpanded
|
checked: Config.notifs.openExpanded
|
||||||
|
settingAnchor: "notif-open-expanded"
|
||||||
subtext: qsTr("Show notifications expanded by default")
|
subtext: qsTr("Show notifications expanded by default")
|
||||||
text: qsTr("Open expanded")
|
text: qsTr("Open expanded")
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1000
|
from: 1000
|
||||||
|
settingAnchor: "notif-default-timeout"
|
||||||
stepSize: 500
|
stepSize: 500
|
||||||
subtext: qsTr("Time before a notification dismisses (ms)")
|
subtext: qsTr("Time before a notification dismisses (ms)")
|
||||||
text: qsTr("Default timeout")
|
text: qsTr("Default timeout")
|
||||||
@@ -61,6 +65,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "notif-group-preview-count"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Notifications shown per group before collapsing")
|
subtext: qsTr("Notifications shown per group before collapsing")
|
||||||
text: qsTr("Group preview count")
|
text: qsTr("Group preview count")
|
||||||
@@ -78,6 +83,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "notif-visible-toasts"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Maximum number of toasts shown at once")
|
subtext: qsTr("Maximum number of toasts shown at once")
|
||||||
text: qsTr("Visible toasts")
|
text: qsTr("Visible toasts")
|
||||||
@@ -89,6 +95,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.chargingChanged
|
checked: Config.utilities.toasts.chargingChanged
|
||||||
|
settingAnchor: "notif-charging-changes"
|
||||||
text: qsTr("Charging changes")
|
text: qsTr("Charging changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.chargingChanged = checked
|
onToggled: Config.utilities.toasts.chargingChanged = checked
|
||||||
@@ -96,6 +103,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.gameModeChanged
|
checked: Config.utilities.toasts.gameModeChanged
|
||||||
|
settingAnchor: "notif-game-mode-changes"
|
||||||
text: qsTr("Game mode changes")
|
text: qsTr("Game mode changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.gameModeChanged = checked
|
onToggled: Config.utilities.toasts.gameModeChanged = checked
|
||||||
@@ -110,6 +118,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.audioOutputChanged
|
checked: Config.utilities.toasts.audioOutputChanged
|
||||||
|
settingAnchor: "notif-audio-output-changes"
|
||||||
text: qsTr("Audio output changes")
|
text: qsTr("Audio output changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.audioOutputChanged = checked
|
onToggled: Config.utilities.toasts.audioOutputChanged = checked
|
||||||
@@ -118,6 +127,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.utilities.toasts.audioInputChanged
|
checked: Config.utilities.toasts.audioInputChanged
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "notif-audio-input-changes"
|
||||||
text: qsTr("Audio input changes")
|
text: qsTr("Audio input changes")
|
||||||
|
|
||||||
onToggled: Config.utilities.toasts.audioInputChanged = checked
|
onToggled: Config.utilities.toasts.audioInputChanged = checked
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
icon: "notifications"
|
icon: "notifications"
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-notifications"
|
||||||
status: qsTr("Notifications, toasts, timeouts")
|
status: qsTr("Notifications, toasts, timeouts")
|
||||||
text: qsTr("Notifications")
|
text: qsTr("Notifications")
|
||||||
|
|
||||||
@@ -85,6 +86,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 100
|
from: 100
|
||||||
|
settingAnchor: "services-media-refresh"
|
||||||
stepSize: 50
|
stepSize: 50
|
||||||
subtext: qsTr("How often the media position updates (ms)")
|
subtext: qsTr("How often the media position updates (ms)")
|
||||||
text: qsTr("Media refresh")
|
text: qsTr("Media refresh")
|
||||||
@@ -97,6 +99,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 0.5
|
from: 0.5
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-system-stats-refresh"
|
||||||
stepSize: 0.5
|
stepSize: 0.5
|
||||||
subtext: qsTr("CPU, memory and GPU update interval (seconds)")
|
subtext: qsTr("CPU, memory and GPU update interval (seconds)")
|
||||||
text: qsTr("System stats refresh")
|
text: qsTr("System stats refresh")
|
||||||
@@ -118,6 +121,7 @@ PageBase {
|
|||||||
first: true
|
first: true
|
||||||
last: true
|
last: true
|
||||||
menuItems: playerVariants.instances
|
menuItems: playerVariants.instances
|
||||||
|
settingAnchor: "services-default-player"
|
||||||
subtext: qsTr("Preferred media player when several are open")
|
subtext: qsTr("Preferred media player when several are open")
|
||||||
text: qsTr("Default player")
|
text: qsTr("Default player")
|
||||||
|
|
||||||
@@ -132,6 +136,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-volume-step"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Amount the volume changes per input (%)")
|
subtext: qsTr("Amount the volume changes per input (%)")
|
||||||
text: qsTr("Volume step")
|
text: qsTr("Volume step")
|
||||||
@@ -143,6 +148,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-brightness-step"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Amount the brightness changes per input (%)")
|
subtext: qsTr("Amount the brightness changes per input (%)")
|
||||||
text: qsTr("Brightness step")
|
text: qsTr("Brightness step")
|
||||||
@@ -154,6 +160,7 @@ PageBase {
|
|||||||
|
|
||||||
SpinRow {
|
SpinRow {
|
||||||
from: 1
|
from: 1
|
||||||
|
settingAnchor: "services-brightness-minimum"
|
||||||
stepSize: 1
|
stepSize: 1
|
||||||
subtext: qsTr("Lowest allowed brightness (%)")
|
subtext: qsTr("Lowest allowed brightness (%)")
|
||||||
text: qsTr("Brightness minimum")
|
text: qsTr("Brightness minimum")
|
||||||
@@ -166,6 +173,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
from: 50
|
from: 50
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "services-max-volume"
|
||||||
stepSize: 5
|
stepSize: 5
|
||||||
subtext: qsTr("Upper limit for output volume (%)")
|
subtext: qsTr("Upper limit for output volume (%)")
|
||||||
text: qsTr("Max volume")
|
text: qsTr("Max volume")
|
||||||
@@ -183,6 +191,7 @@ PageBase {
|
|||||||
SpinRow {
|
SpinRow {
|
||||||
first: true
|
first: true
|
||||||
from: 10
|
from: 10
|
||||||
|
settingAnchor: "services-visualizer-resolution"
|
||||||
stepSize: 2
|
stepSize: 2
|
||||||
subtext: qsTr("Resolution of the audio visualizer")
|
subtext: qsTr("Resolution of the audio visualizer")
|
||||||
text: qsTr("Visualizer resolution")
|
text: qsTr("Visualizer resolution")
|
||||||
@@ -194,6 +203,7 @@ PageBase {
|
|||||||
|
|
||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.general.color.smart
|
checked: Config.general.color.smart
|
||||||
|
settingAnchor: "services-smart-color-scheme"
|
||||||
subtext: qsTr("Derive theme mode from the wallpaper")
|
subtext: qsTr("Derive theme mode from the wallpaper")
|
||||||
text: qsTr("Smart color scheme")
|
text: qsTr("Smart color scheme")
|
||||||
|
|
||||||
@@ -205,6 +215,7 @@ PageBase {
|
|||||||
last: true
|
last: true
|
||||||
menuItems: root.gpuItems
|
menuItems: root.gpuItems
|
||||||
menuOnTop: true
|
menuOnTop: true
|
||||||
|
settingAnchor: "services-gpu"
|
||||||
subtext: Gpu.name ? qsTr("Monitoring: %1").arg(Gpu.name) : qsTr("Override for GPU type")
|
subtext: Gpu.name ? qsTr("Monitoring: %1").arg(Gpu.name) : qsTr("Override for GPU type")
|
||||||
text: qsTr("GPU")
|
text: qsTr("GPU")
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
checked: Config.background.enabled
|
checked: Config.background.enabled
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "style-display-wallpaper"
|
||||||
text: qsTr("Display wallpaper")
|
text: qsTr("Display wallpaper")
|
||||||
|
|
||||||
onToggled: Config.background.enabled = checked
|
onToggled: Config.background.enabled = checked
|
||||||
@@ -57,6 +58,7 @@ PageBase {
|
|||||||
ToggleRow {
|
ToggleRow {
|
||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: DynamicColors.transparency.enabled
|
checked: DynamicColors.transparency.enabled
|
||||||
|
settingAnchor: "style-transparency"
|
||||||
subtext: qsTr("Base %1, layers %2").arg(DynamicColors.transparency.base).arg(DynamicColors.transparency.layers)
|
subtext: qsTr("Base %1, layers %2").arg(DynamicColors.transparency.base).arg(DynamicColors.transparency.layers)
|
||||||
text: qsTr("Transparency")
|
text: qsTr("Transparency")
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ PageBase {
|
|||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: !DynamicColors.light
|
checked: !DynamicColors.light
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "style-dark-theme"
|
||||||
text: qsTr("Dark theme")
|
text: qsTr("Dark theme")
|
||||||
|
|
||||||
onToggled: DynamicColors.setMode(checked ? "dark" : "light")
|
onToggled: DynamicColors.setMode(checked ? "dark" : "light")
|
||||||
@@ -88,6 +91,7 @@ PageBase {
|
|||||||
|
|
||||||
checked: Config.general.color.scheduleDark
|
checked: Config.general.color.scheduleDark
|
||||||
first: true
|
first: true
|
||||||
|
settingAnchor: "style-schedule-dark-mode"
|
||||||
subtext: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
subtext: qsTr("Dark mode will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
||||||
text: qsTr("Schedule dark mode")
|
text: qsTr("Schedule dark mode")
|
||||||
|
|
||||||
@@ -129,6 +133,7 @@ PageBase {
|
|||||||
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
Layout.topMargin: Appearance.spacing.extraSmall / 2 - parent.spacing
|
||||||
checked: Config.general.color.scheduleHyprsunset
|
checked: Config.general.color.scheduleHyprsunset
|
||||||
last: true
|
last: true
|
||||||
|
settingAnchor: "style-schedule-hyprsunset"
|
||||||
subtext: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
subtext: qsTr("Hyprsunset will turn on at %1, and turn off at %2.").arg(startTime).arg(endTime)
|
||||||
text: qsTr("Schedule hyprsunset")
|
text: qsTr("Schedule hyprsunset")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import "../../scripts/fzf.js" as Fzf
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import ZShell
|
||||||
|
import qs.Config
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var fzfFinder: null
|
||||||
|
readonly property var highlightCache: ({
|
||||||
|
"search": "",
|
||||||
|
"pattern": null
|
||||||
|
})
|
||||||
|
property var inverted: ({})
|
||||||
|
property var ranking: ({})
|
||||||
|
|
||||||
|
function highlight(text: string, search: string, colour: color): string {
|
||||||
|
const escaped = text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
if (search.length === 0)
|
||||||
|
return escaped;
|
||||||
|
|
||||||
|
const cache = root.highlightCache;
|
||||||
|
if (search !== cache.search) {
|
||||||
|
const tokens = tokenize(search);
|
||||||
|
cache.search = search;
|
||||||
|
if (tokens.length === 0)
|
||||||
|
cache.pattern = null;
|
||||||
|
else {
|
||||||
|
const escapedTokens = tokens.map(t => t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||||
|
cache.pattern = new RegExp("\\b(" + escapedTokens.join("|") + ")", "gi");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pattern = cache.pattern;
|
||||||
|
if (!pattern)
|
||||||
|
return escaped;
|
||||||
|
|
||||||
|
pattern.lastIndex = 0;
|
||||||
|
if (!pattern.test(escaped))
|
||||||
|
return escaped;
|
||||||
|
|
||||||
|
pattern.lastIndex = 0;
|
||||||
|
return escaped.replace(pattern, `<font color="${colour}">$1</font>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lookup(token: string): var {
|
||||||
|
const result = ({});
|
||||||
|
const exact = root.inverted[token] !== undefined;
|
||||||
|
const keys = exact ? [token] : Object.keys(root.inverted).filter(k => k.startsWith(token));
|
||||||
|
for (const key of keys) {
|
||||||
|
const rank = root.ranking[key] ?? ({});
|
||||||
|
for (const id of root.inverted[key]) {
|
||||||
|
const w = rank[id] ?? 0.1;
|
||||||
|
if (result[id] === undefined || w > result[id])
|
||||||
|
result[id] = w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function query(search: string): list<QtObject> {
|
||||||
|
const tokens = root.tokenize(search);
|
||||||
|
if (tokens.length === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
const scores = ({});
|
||||||
|
const hitCounts = ({});
|
||||||
|
for (const token of tokens) {
|
||||||
|
const matches = root.lookup(token);
|
||||||
|
for (const id in matches) {
|
||||||
|
scores[id] = (scores[id] ?? 0) + matches[id];
|
||||||
|
hitCounts[id] = (hitCounts[id] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ranked = Object.keys(scores).filter(id => hitCounts[id] === tokens.length).sort((a, b) => scores[b] - scores[a] || (parseInt(a) - parseInt(b))).slice(0, 25);
|
||||||
|
|
||||||
|
const all = entries.instances;
|
||||||
|
const out = ranked.map(id => all[parseInt(id)]).filter(e => e !== undefined);
|
||||||
|
|
||||||
|
if (out.length < 5 && root.fzfFinder) {
|
||||||
|
const seen = ({});
|
||||||
|
for (const id of ranked)
|
||||||
|
seen[id] = true;
|
||||||
|
const fuzzy = root.fzfFinder.find(search);
|
||||||
|
for (const r of fuzzy) {
|
||||||
|
const idx = r.item.idx;
|
||||||
|
if (seen[idx])
|
||||||
|
continue;
|
||||||
|
seen[idx] = true;
|
||||||
|
const entry = all[idx];
|
||||||
|
if (entry !== undefined)
|
||||||
|
out.push(entry);
|
||||||
|
if (out.length >= 25)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenize(text: string): var {
|
||||||
|
return text.toLowerCase().split(/[^a-z0-9]+/).filter(t => t.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(ZUtils.settingsIndex());
|
||||||
|
entries.model = data.entries;
|
||||||
|
root.inverted = data.inverted ?? {};
|
||||||
|
root.ranking = data.ranking ?? {};
|
||||||
|
const docs = data.entries.map((e, i) => ({
|
||||||
|
idx: i,
|
||||||
|
text: e.title
|
||||||
|
}));
|
||||||
|
root.fzfFinder = new Fzf.Finder(docs, {
|
||||||
|
selector: d => d.text,
|
||||||
|
limit: 25
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
entries.model = [];
|
||||||
|
root.inverted = {};
|
||||||
|
root.ranking = {};
|
||||||
|
root.fzfFinder = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Variants {
|
||||||
|
id: entries
|
||||||
|
|
||||||
|
SettingEntry {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SettingEntry: QtObject {
|
||||||
|
readonly property string anchor: modelData.anchor ?? ""
|
||||||
|
readonly property var crumbIcons: modelData.crumbIcons
|
||||||
|
readonly property var crumbLabels: modelData.crumbLabels
|
||||||
|
readonly property bool isToggle: togglePath.length > 0
|
||||||
|
required property var modelData
|
||||||
|
readonly property int pageIdx: modelData.pageIdx
|
||||||
|
readonly property string section: modelData.section ?? ""
|
||||||
|
readonly property var subPath: modelData.subPath
|
||||||
|
readonly property string subtext: modelData.subtext ?? ""
|
||||||
|
readonly property string title: modelData.title
|
||||||
|
readonly property string togglePath: modelData.togglePath ?? ""
|
||||||
|
readonly property bool toggleValue: {
|
||||||
|
if (!isToggle)
|
||||||
|
return false;
|
||||||
|
let obj = Config;
|
||||||
|
const parts = togglePath.split(".");
|
||||||
|
for (const part of parts) {
|
||||||
|
if (obj === undefined || obj === null)
|
||||||
|
return false;
|
||||||
|
obj = obj[part];
|
||||||
|
}
|
||||||
|
return obj ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setToggle(value: bool): void {
|
||||||
|
if (!isToggle)
|
||||||
|
return;
|
||||||
|
const parts = togglePath.split(".");
|
||||||
|
let obj = Config;
|
||||||
|
for (let k = 0; k < parts.length - 1; k++) {
|
||||||
|
if (obj === undefined || obj === null)
|
||||||
|
return;
|
||||||
|
obj = obj[parts[k]];
|
||||||
|
}
|
||||||
|
if (obj !== undefined && obj !== null)
|
||||||
|
obj[parts[parts.length - 1]] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,14 +8,19 @@ QtObject {
|
|||||||
property bool animatingContainer
|
property bool animatingContainer
|
||||||
property int currentPageIdx
|
property int currentPageIdx
|
||||||
property bool isWindow
|
property bool isWindow
|
||||||
|
property string lastAnchor
|
||||||
|
property list<int> pendingSubPath
|
||||||
property ShellScreen screen
|
property ShellScreen screen
|
||||||
|
property string searchAnchor
|
||||||
property bool searchOpen
|
property bool searchOpen
|
||||||
|
property string searchText
|
||||||
property DesktopEntry selectedApp
|
property DesktopEntry selectedApp
|
||||||
property BluetoothDevice selectedBtDevice
|
property BluetoothDevice selectedBtDevice
|
||||||
property string selectedWallpaperCategory
|
property string selectedWallpaperCategory
|
||||||
property list<int> subPageIdxStack
|
property list<int> subPageIdxStack
|
||||||
|
|
||||||
signal close
|
signal close
|
||||||
|
signal highlightSetting(anchor: string)
|
||||||
signal subPageClosed
|
signal subPageClosed
|
||||||
signal subPageOpened(idx: int)
|
signal subPageOpened(idx: int)
|
||||||
|
|
||||||
@@ -24,10 +29,38 @@ QtObject {
|
|||||||
subPageIdxStack.pop();
|
subPageIdxStack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jumpToSetting(pageIdx: int, subPath: var, anchor: string): void {
|
||||||
|
const samePage = currentPageIdx === pageIdx;
|
||||||
|
const sameSub = subPageIdxStack.length === subPath.length && subPath.every((v, i) => subPageIdxStack[i] === v);
|
||||||
|
if (samePage && sameSub && anchor === lastAnchor) {
|
||||||
|
highlightSetting(anchor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastAnchor = anchor;
|
||||||
|
if (samePage && sameSub) {
|
||||||
|
searchAnchor = "";
|
||||||
|
searchAnchor = anchor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchAnchor = anchor;
|
||||||
|
if (!samePage) {
|
||||||
|
pendingSubPath = subPath.slice();
|
||||||
|
currentPageIdx = pageIdx;
|
||||||
|
} else {
|
||||||
|
while (subPageIdxStack.length > 0)
|
||||||
|
closeSubPage();
|
||||||
|
for (let i = 0; i < subPath.length; i++)
|
||||||
|
openSubPage(subPath[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function openSubPage(idx: int): void {
|
function openSubPage(idx: int): void {
|
||||||
subPageIdxStack.push(idx);
|
subPageIdxStack.push(idx);
|
||||||
subPageOpened(idx);
|
subPageOpened(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCurrentPageIdxChanged: subPageIdxStack.length = 0
|
onCurrentPageIdxChanged: {
|
||||||
|
subPageIdxStack = pendingSubPath;
|
||||||
|
pendingSubPath = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ Item {
|
|||||||
sState.animatingContainer: content.opacity < 1
|
sState.animatingContainer: content.opacity < 1
|
||||||
sState.currentPageIdx: ["wallpaper"][0]
|
sState.currentPageIdx: ["wallpaper"][0]
|
||||||
sState.screen: root.screen
|
sState.screen: root.screen
|
||||||
|
|
||||||
onClose: console.log("shouldclose")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,223 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.Components
|
||||||
|
import qs.Config
|
||||||
|
import qs.Helpers
|
||||||
|
|
||||||
|
CustomClippingRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property var wrapper
|
||||||
|
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: DynamicColors.tPalette.m3surfaceContainer
|
||||||
|
implicitHeight: networkPopContent.height + networks.implicitHeight + networkPopContent.anchors.margins + networks.anchors.margins * 2
|
||||||
|
implicitWidth: 500 + 8 * 2
|
||||||
|
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale
|
||||||
|
|
||||||
|
Component.onCompleted: Network.active = true
|
||||||
|
Component.onDestruction: Network.active = false
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: networkPopContent
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.large
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
||||||
|
Layout.rightMargin: Appearance.padding.extraSmall
|
||||||
|
font.pointSize: Appearance.font.size.large
|
||||||
|
text: qsTr("Wifi")
|
||||||
|
}
|
||||||
|
|
||||||
|
Toggle {
|
||||||
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
||||||
|
checked: Network.wifiEnabled
|
||||||
|
label: qsTr("WiFi enabled")
|
||||||
|
|
||||||
|
toggle.onToggled: Network.setWifi(checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: networks
|
||||||
|
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: Appearance.padding.normal
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: networkPopContent.bottom
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.leftMargin: Appearance.padding.normal
|
||||||
|
text: qsTr("Connected")
|
||||||
|
visible: Network.connectedNetworks.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
visible: Network.connectedNetworks.length > 0
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...Network.connectedNetworks].sort((a, b) => {
|
||||||
|
return b.signalStrength - a.signalStrength;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer {
|
||||||
|
visible: Network.connectedNetworks.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.leftMargin: Appearance.padding.normal
|
||||||
|
text: qsTr("Known networks")
|
||||||
|
visible: Network.knownNetworks.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
visible: Network.knownNetworks.length > 0
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...Network.knownNetworks].sort((a, b) => {
|
||||||
|
return b.signalStrength - a.signalStrength;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer {
|
||||||
|
visible: Network.knownNetworks.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.leftMargin: Appearance.padding.normal
|
||||||
|
text: qsTr("Networks")
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.leftMargin: Appearance.padding.normal
|
||||||
|
Layout.preferredHeight: visible ? implicitHeight : 0
|
||||||
|
color: DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.extraSmall
|
||||||
|
text: qsTr("%1 networks available").arg(Network.networks.length) // qmllint disable missing-property
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: networkRepeater
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: [...Network.unknownNetworks].sort((a, b) => {
|
||||||
|
return b.signalStrength - a.signalStrength;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkItem {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component NetworkItem: CustomRect {
|
||||||
|
id: knownNetworkItem
|
||||||
|
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: visible ? knownNetworkRow.implicitHeight + Appearance.padding.smaller * 2 : 0
|
||||||
|
Layout.rightMargin: Appearance.padding.extraSmall
|
||||||
|
radius: Appearance.rounding.small
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: knownNetworkRow
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Appearance.padding.larger
|
||||||
|
anchors.rightMargin: Appearance.padding.normal
|
||||||
|
opacity: 0
|
||||||
|
scale: 0.7
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
Anim {
|
||||||
|
type: Anim.DefaultEffects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Behavior on scale {
|
||||||
|
Anim {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
opacity = 1;
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
|
||||||
|
text: Icons.getNetworkIcon(knownNetworkItem.modelData.signalStrength * 100, Network.isSecure(knownNetworkItem.modelData.security))
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: Appearance.spacing.extraSmall
|
||||||
|
Layout.rightMargin: Appearance.spacing.extraSmall
|
||||||
|
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
|
||||||
|
elide: Text.ElideRight
|
||||||
|
text: knownNetworkItem.modelData.name
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRect {
|
||||||
|
color: Qt.alpha(DynamicColors.palette.m3primary, knownNetworkItem.modelData.active ? 1 : 0)
|
||||||
|
implicitHeight: knownWirelessConnectIcon.implicitHeight + Appearance.padding.extraSmall
|
||||||
|
implicitWidth: implicitHeight
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
|
// CircularIndicator {
|
||||||
|
// anchors.fill: parent
|
||||||
|
// running: knownNetworkItem.loading
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateLayer {
|
||||||
|
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
NetworkPassword.requestOpen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
component Spacer: Item {
|
||||||
|
id: spacer
|
||||||
|
|
||||||
|
Layout.preferredHeight: networkRepeater.count > 0 ? Appearance.spacing.extraSmall : 0
|
||||||
|
}
|
||||||
|
component Toggle: RowLayout {
|
||||||
|
property alias checked: toggle.checked
|
||||||
|
required property string label
|
||||||
|
property alias toggle: toggle
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.rightMargin: Appearance.padding.extraSmall
|
||||||
|
spacing: Appearance.spacing.small
|
||||||
|
|
||||||
|
CustomText {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: parent.label
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomSwitch {
|
||||||
|
id: toggle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Effects
|
import QtQuick.Effects
|
||||||
import qs.Components
|
import qs.Components
|
||||||
@@ -12,14 +13,13 @@ import qs.Config
|
|||||||
StackView {
|
StackView {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property int biggestWidth: 0
|
|
||||||
readonly property int itemHeight: 30
|
readonly property int itemHeight: 30
|
||||||
readonly property int panelRadius: ((itemHeight / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
readonly property int panelRadius: ((itemHeight / 2) + Appearance.padding.small) * Appearance.rounding.scale
|
||||||
required property PopoutState popouts
|
required property PopoutState popouts
|
||||||
property int rootWidth: 0
|
property int rootWidth: 0
|
||||||
required property QsMenuHandle trayItem
|
required property QsMenuHandle trayItem
|
||||||
|
|
||||||
implicitHeight: currentItem.implicitHeight
|
implicitHeight: currentItem.isSubMenu ? currentItem.implicitHeight : currentItem.implicitHeight - currentItem.spacing
|
||||||
implicitWidth: currentItem.implicitWidth
|
implicitWidth: currentItem.implicitWidth
|
||||||
|
|
||||||
initialItem: SubMenu {
|
initialItem: SubMenu {
|
||||||
@@ -46,7 +46,7 @@ StackView {
|
|||||||
duration: 0
|
duration: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
component SubMenu: Column {
|
component SubMenu: ColumnLayout {
|
||||||
id: menu
|
id: menu
|
||||||
|
|
||||||
required property QsMenuHandle handle
|
required property QsMenuHandle handle
|
||||||
@@ -54,9 +54,8 @@ StackView {
|
|||||||
property bool shown
|
property bool shown
|
||||||
|
|
||||||
opacity: shown ? 1 : 0
|
opacity: shown ? 1 : 0
|
||||||
padding: 0
|
|
||||||
scale: shown ? 1 : 0.8
|
scale: shown ? 1 : 0.8
|
||||||
spacing: 4
|
spacing: Appearance.spacing.extraSmall
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -87,22 +86,27 @@ StackView {
|
|||||||
required property int index
|
required property int index
|
||||||
required property QsMenuEntry modelData
|
required property QsMenuEntry modelData
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: modelData.isSeparator ? Appearance.padding.normal : 0
|
||||||
|
Layout.rightMargin: modelData.isSeparator ? Appearance.padding.normal : 0
|
||||||
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
color: modelData.isSeparator ? DynamicColors.palette.m3outlineVariant : "transparent"
|
||||||
implicitHeight: modelData.isSeparator ? 1 : children.implicitHeight
|
implicitHeight: modelData.isSeparator ? (visible ? 1 : 0) : childrenLoader.item.implicitHeight
|
||||||
implicitWidth: root.biggestWidth
|
implicitWidth: childrenLoader.item?.implicitWidth ?? 0
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
visible: index !== (menuOpener.children.values.length - 1) ? true : (modelData.isSeparator ? false : true)
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: children
|
id: childrenLoader
|
||||||
|
|
||||||
active: !item.modelData.isSeparator
|
active: !item.modelData.isSeparator
|
||||||
anchors.left: parent.left
|
anchors.fill: parent
|
||||||
anchors.right: parent.right
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
|
property int iconWidth: icon.active ? icon.width + Appearance.spacing.normal + icon.anchors.rightMargin : 0
|
||||||
|
|
||||||
implicitHeight: root.itemHeight
|
implicitHeight: root.itemHeight
|
||||||
|
implicitWidth: label.width + label.anchors.leftMargin * 2 + iconWidth + (expand.item?.width ?? 0)
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
enabled: item.modelData.enabled
|
enabled: item.modelData.enabled
|
||||||
@@ -111,8 +115,6 @@ StackView {
|
|||||||
onClicked: {
|
onClicked: {
|
||||||
const entry = item.modelData;
|
const entry = item.modelData;
|
||||||
if (entry.hasChildren) {
|
if (entry.hasChildren) {
|
||||||
root.rootWidth = root.biggestWidth;
|
|
||||||
root.biggestWidth = 0;
|
|
||||||
root.push(subMenuComp.createObject(null, {
|
root.push(subMenuComp.createObject(null, {
|
||||||
handle: entry,
|
handle: entry,
|
||||||
isSubMenu: true
|
isSubMenu: true
|
||||||
@@ -161,23 +163,7 @@ StackView {
|
|||||||
anchors.leftMargin: 10
|
anchors.leftMargin: 10
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
color: item.modelData.enabled ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3outline
|
||||||
text: labelMetrics.elidedText
|
|
||||||
}
|
|
||||||
|
|
||||||
TextMetrics {
|
|
||||||
id: labelMetrics
|
|
||||||
|
|
||||||
font.family: label.font.family
|
|
||||||
font.pointSize: label.font.pointSize
|
|
||||||
text: item.modelData.text
|
text: item.modelData.text
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
var biggestWidth = root.biggestWidth;
|
|
||||||
var currentWidth = labelMetrics.width + (item.modelData.icon ?? "" ? 30 : 0) + (item.modelData.hasChildren ? 30 : 0) + 20;
|
|
||||||
if (currentWidth > biggestWidth) {
|
|
||||||
root.biggestWidth = currentWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
@@ -201,17 +187,17 @@ StackView {
|
|||||||
Loader {
|
Loader {
|
||||||
id: loader
|
id: loader
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: active ? implicitHeight : 0
|
||||||
active: menu.isSubMenu
|
active: menu.isSubMenu
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
implicitHeight: 30
|
implicitHeight: 30
|
||||||
implicitWidth: back.implicitWidth
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.bottom: parent.bottom
|
anchors.fill: parent
|
||||||
implicitHeight: 30
|
implicitHeight: 30
|
||||||
implicitWidth: root.biggestWidth
|
|
||||||
|
|
||||||
CustomRect {
|
CustomRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -224,7 +210,6 @@ StackView {
|
|||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.pop();
|
root.pop();
|
||||||
root.biggestWidth = root.rootWidth;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ Item {
|
|||||||
CustomRect {
|
CustomRect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 3
|
anchors.margins: 3
|
||||||
color: icon.layer.enabled && root.current ? DynamicColors.palette.m3primary : "transparent"
|
color: icon.layer.enabled && enabled && root.current ? DynamicColors.palette.m3primary : "transparent"
|
||||||
|
enabled: !Config.bar.tray.showOnHover
|
||||||
radius: Appearance.rounding.full
|
radius: Appearance.rounding.full
|
||||||
|
|
||||||
StateLayer {
|
StateLayer {
|
||||||
@@ -57,7 +58,7 @@ Item {
|
|||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.LeftButton) {
|
||||||
root.item.activate();
|
root.item.activate();
|
||||||
console.log(icon.source + "\n" + root.item.id);
|
console.log(icon.source + "\n" + root.item.id);
|
||||||
} else if (mouse.button === Qt.RightButton && Config.bar.popouts.tray) {
|
} else if (mouse.button === Qt.RightButton && Config.bar.popouts.tray && !Config.bar.tray.showOnHover) {
|
||||||
root.popouts.currentName = `traymenu${root.ind}`;
|
root.popouts.currentName = `traymenu${root.ind}`;
|
||||||
root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x);
|
root.popouts.currentCenter = Qt.binding(() => root.mapToItem(root.loader, root.implicitWidth / 2, 0).x);
|
||||||
root.popouts.hasCurrent = true;
|
root.popouts.hasCurrent = true;
|
||||||
@@ -76,9 +77,9 @@ Item {
|
|||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
antialiasing: true
|
antialiasing: true
|
||||||
color: root.current ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
color: root.current && !Config.bar.tray.showOnHover ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
|
||||||
implicitSize: Config.bar.tray.trayIconSize * root.dpr
|
implicitSize: Config.bar.tray.trayIconSize * root.dpr
|
||||||
layer.enabled: Config.general.color.smart || Config.general.color.scheduleDark
|
layer.enabled: Config.bar.tray.recolorIcons
|
||||||
scale: 1 / root.dpr
|
scale: 1 / root.dpr
|
||||||
source: root.resolveIcon(root.item.id, root.item.icon)
|
source: root.resolveIcon(root.item.id, root.item.icon)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ RowLayout {
|
|||||||
id: "audio",
|
id: "audio",
|
||||||
item: child
|
item: child
|
||||||
};
|
};
|
||||||
|
if (child.objectName === "networkWidget" && Config.bar.popouts.network)
|
||||||
|
return {
|
||||||
|
id: "network",
|
||||||
|
item: child
|
||||||
|
};
|
||||||
if (child.objectName === "upowerWidget" && Config.bar.popouts.upower)
|
if (child.objectName === "upowerWidget" && Config.bar.popouts.upower)
|
||||||
return {
|
return {
|
||||||
id: "upower",
|
id: "upower",
|
||||||
@@ -61,12 +66,12 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let trayPos = mapToItem(sysTray, localX, localY);
|
let trayPos = mapToItem(sysTray, localX, localY);
|
||||||
if (sysTray.contains(Qt.point(trayPos.x, trayPos.y))) {
|
if (Config.bar.tray.showOnHover && sysTray.contains(Qt.point(trayPos.x, trayPos.y))) {
|
||||||
let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y);
|
let trayRowPos = sysTray.mapToItem(sysRow, trayPos.x, trayPos.y);
|
||||||
let child = sysRow.childAt(trayRowPos.x, trayRowPos.y);
|
let child = sysRow.childAt(trayRowPos.x, trayRowPos.y);
|
||||||
if (child && child.hasOwnProperty("popoutId")) {
|
if (child && child.hasOwnProperty("ind")) {
|
||||||
return {
|
return {
|
||||||
id: child.popoutId,
|
id: `traymenu${child.ind}`,
|
||||||
item: child
|
item: child
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -146,6 +151,10 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkWidget {
|
||||||
|
objectName: "networkWidget"
|
||||||
|
}
|
||||||
|
|
||||||
UPowerWidget {
|
UPowerWidget {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
objectName: "upowerWidget"
|
objectName: "upowerWidget"
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
import qs.Daemons
|
||||||
|
import qs.Modules
|
||||||
|
import qs.Config
|
||||||
|
import qs.Components
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
// property color barColor: DynamicColors.palette.m3primary
|
||||||
|
property color textColor: DynamicColors.palette.m3onSurface
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
animate: true
|
||||||
|
color: root.textColor // Network.connected ? root.textColor : DynamicColors.palette.m3error
|
||||||
|
fill: 1
|
||||||
|
font.pointSize: Appearance.font.size.larger
|
||||||
|
text: "android_wifi_4_bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,8 +26,7 @@ void BlobGroup::setColor(const QColor& c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::setCornerFill(bool e) {
|
void BlobGroup::setCornerFill(bool e) {
|
||||||
if (m_cornerFill == e)
|
if (m_cornerFill == e) return;
|
||||||
return;
|
|
||||||
m_cornerFill = e;
|
m_cornerFill = e;
|
||||||
emit cornerFillChanged();
|
emit cornerFillChanged();
|
||||||
markDirty();
|
markDirty();
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ class BlobShape;
|
|||||||
class BlobInvertedRect;
|
class BlobInvertedRect;
|
||||||
|
|
||||||
class BlobGroup : public QObject {
|
class BlobGroup : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
|
||||||
Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFillChanged)
|
smoothingChanged)
|
||||||
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY
|
||||||
|
cornerFillChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobGroup(QObject* parent = nullptr);
|
explicit BlobGroup(QObject* parent = nullptr);
|
||||||
@@ -27,14 +31,12 @@ Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFill
|
|||||||
|
|
||||||
void setColor(const QColor& c);
|
void setColor(const QColor& c);
|
||||||
|
|
||||||
[[nodiscard]] bool cornerFill() const {
|
[[nodiscard]] bool cornerFill() const { return m_cornerFill; }
|
||||||
return m_cornerFill;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCornerFill(bool e);
|
void setCornerFill(bool e);
|
||||||
|
|
||||||
void addShape(BlobShape* shape);
|
void addShape(BlobShape* shape);
|
||||||
void removeShape(BlobShape* shape);
|
void removeShape(BlobShape* shape);
|
||||||
|
|
||||||
void setInvertedRect(BlobInvertedRect* rect);
|
void setInvertedRect(BlobInvertedRect* rect);
|
||||||
void clearInvertedRect(BlobInvertedRect* rect);
|
void clearInvertedRect(BlobInvertedRect* rect);
|
||||||
@@ -49,16 +51,16 @@ void removeShape(BlobShape* shape);
|
|||||||
void markShapeDirty(BlobShape* source);
|
void markShapeDirty(BlobShape* source);
|
||||||
void ensurePhysicsUpdated();
|
void ensurePhysicsUpdated();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void smoothingChanged();
|
void smoothingChanged();
|
||||||
void colorChanged();
|
void colorChanged();
|
||||||
void cornerFillChanged();
|
void cornerFillChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
qreal m_smoothing = 32.0;
|
qreal m_smoothing = 32.0;
|
||||||
QColor m_color{ 0x44, 0x88, 0xff };
|
QColor m_color{0x44, 0x88, 0xff};
|
||||||
bool m_cornerFill = true;
|
bool m_cornerFill = true;
|
||||||
QList<BlobShape*> m_shapes;
|
QList<BlobShape*> m_shapes;
|
||||||
BlobInvertedRect* m_invertedRect = nullptr;
|
BlobInvertedRect* m_invertedRect = nullptr;
|
||||||
bool m_physicsUpdated = false;
|
bool m_physicsUpdated = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,11 +32,9 @@ void BlobRect::updatePolish() {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
},
|
||||||
},
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
@@ -184,8 +182,7 @@ bool BlobRect::isExcluded(const BlobShape* other) const {
|
|||||||
|
|
||||||
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
|
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
|
||||||
for (const auto& ptr : m_excludeCorners) {
|
for (const auto& ptr : m_excludeCorners) {
|
||||||
if (ptr == other)
|
if (ptr == other) return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -203,8 +200,15 @@ QQmlListProperty<BlobRect> BlobRect::exclude() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
|
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
|
||||||
return QQmlListProperty<BlobRect>(this, nullptr, &excludeCornersAppend, &excludeCornersCount, &excludeCornersAt,
|
return QQmlListProperty<BlobRect>(
|
||||||
&excludeCornersClear, &excludeCornersReplace, &excludeCornersRemoveLast);
|
this,
|
||||||
|
nullptr,
|
||||||
|
&excludeCornersAppend,
|
||||||
|
&excludeCornersCount,
|
||||||
|
&excludeCornersAt,
|
||||||
|
&excludeCornersClear,
|
||||||
|
&excludeCornersReplace,
|
||||||
|
&excludeCornersRemoveLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
@@ -249,11 +253,11 @@ void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
|||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeCornersAppend(
|
||||||
|
QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_excludeCorners.append(rect);
|
self->m_excludeCorners.append(rect);
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,36 +266,33 @@ qsizetype BlobRect::excludeCornersCount(QQmlListProperty<BlobRect>* prop) {
|
|||||||
return self->m_excludeCorners.size();
|
return self->m_excludeCorners.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobRect* BlobRect::excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
BlobRect* BlobRect::excludeCornersAt(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
return self->m_excludeCorners.at(index);
|
return self->m_excludeCorners.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_excludeCorners.isEmpty())
|
if (self->m_excludeCorners.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_excludeCorners.clear();
|
self->m_excludeCorners.clear();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
void BlobRect::excludeCornersReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_excludeCorners[index] = rect;
|
self->m_excludeCorners[index] = rect;
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_excludeCorners.isEmpty())
|
if (self->m_excludeCorners.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_excludeCorners.removeLast();
|
self->m_excludeCorners.removeLast();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeCornersChanged();
|
emit self->excludeCornersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,11 +320,9 @@ void BlobRect::checkAtRest(float speed) {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
},
|
||||||
},
|
Qt::QueuedConnection);
|
||||||
Qt::QueuedConnection
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,32 @@
|
|||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
|
||||||
class BlobRect : public BlobShape {
|
class BlobRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
qreal stiffness READ stiffness WRITE setStiffness NOTIFY
|
||||||
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
stiffnessChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> excludeCorners READ excludeCorners NOTIFY excludeCornersChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
|
qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
|
||||||
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
|
deformScaleChanged)
|
||||||
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(
|
QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
||||||
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<BlobRect> excludeCorners READ excludeCorners NOTIFY
|
||||||
|
excludeCornersChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY
|
||||||
|
topLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY
|
||||||
|
topRightRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius
|
||||||
|
NOTIFY bottomLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomRightRadius READ bottomRightRadius WRITE
|
||||||
|
setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobRect(QQuickItem* parent = nullptr);
|
explicit BlobRect(QQuickItem* parent = nullptr);
|
||||||
@@ -52,12 +66,12 @@ Q_PROPERTY(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> exclude();
|
QQmlListProperty<BlobRect> exclude();
|
||||||
QQmlListProperty<BlobRect> excludeCorners();
|
QQmlListProperty<BlobRect> excludeCorners();
|
||||||
|
|
||||||
bool isExcluded(const BlobShape* other) const override;
|
bool isExcluded(const BlobShape* other) const override;
|
||||||
bool isCornerExcluded(const BlobShape* other) const override;
|
bool isCornerExcluded(const BlobShape* other) const override;
|
||||||
void cornerRadii(float out[4]) const override;
|
void cornerRadii(float out[4]) const override;
|
||||||
|
|
||||||
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
|
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
|
||||||
|
|
||||||
@@ -77,16 +91,16 @@ void cornerRadii(float out[4]) const override;
|
|||||||
|
|
||||||
void setBottomRightRadius(qreal r);
|
void setBottomRightRadius(qreal r);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void stiffnessChanged();
|
void stiffnessChanged();
|
||||||
void dampingChanged();
|
void dampingChanged();
|
||||||
void deformScaleChanged();
|
void deformScaleChanged();
|
||||||
void excludeChanged();
|
void excludeChanged();
|
||||||
void excludeCornersChanged();
|
void excludeCornersChanged();
|
||||||
void topLeftRadiusChanged();
|
void topLeftRadiusChanged();
|
||||||
void topRightRadiusChanged();
|
void topRightRadiusChanged();
|
||||||
void bottomLeftRadiusChanged();
|
void bottomLeftRadiusChanged();
|
||||||
void bottomRightRadiusChanged();
|
void bottomRightRadiusChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
@@ -121,20 +135,25 @@ void bottomRightRadiusChanged();
|
|||||||
qreal m_bottomLeftRadius = -1;
|
qreal m_bottomLeftRadius = -1;
|
||||||
qreal m_bottomRightRadius = -1;
|
qreal m_bottomRightRadius = -1;
|
||||||
|
|
||||||
QList<QPointer<BlobRect> > m_exclude;
|
QList<QPointer<BlobRect>> m_exclude;
|
||||||
QList<QPointer<BlobRect> > m_excludeCorners;
|
QList<QPointer<BlobRect>> m_excludeCorners;
|
||||||
|
|
||||||
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
||||||
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
static BlobRect* excludeAt(
|
||||||
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
|
|
||||||
static void excludeCornersAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeCornersAppend(
|
||||||
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
|
QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static BlobRect* excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
|
static BlobRect* excludeCornersAt(
|
||||||
static void excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
|
||||||
|
static void excludeCornersReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
|
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ static float cornerFillFactor(float sd, float smoothFactor) {
|
|||||||
return std::max(outside, inside);
|
return std::max(outside, inside);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobShape::BlobShape(QQuickItem* parent)
|
BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
|
||||||
: QQuickItem(parent) {
|
|
||||||
setFlag(ItemHasContents);
|
setFlag(ItemHasContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,13 +311,10 @@ void BlobShape::updatePolish() {
|
|||||||
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
||||||
|
|
||||||
for (qsizetype j = 0; cornerFill && j < rectCount; ++j) {
|
for (qsizetype j = 0; cornerFill && j < rectCount; ++j) {
|
||||||
if (j == i)
|
if (j == i) continue;
|
||||||
continue;
|
if (riExcludeMask & (1 << j)) continue;
|
||||||
if (riExcludeMask & (1 << j))
|
|
||||||
continue;
|
|
||||||
BlobShape* const sj = rectShapes[j];
|
BlobShape* const sj = rectShapes[j];
|
||||||
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si))
|
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si)) continue;
|
||||||
continue;
|
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
const float sdTr = cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
const float sdTr = cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
||||||
const float sdBr = cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
const float sdBr = cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh);
|
||||||
|
|||||||
@@ -55,11 +55,11 @@ class BlobShape : public QQuickItem {
|
|||||||
|
|
||||||
virtual bool isExcluded(const BlobShape*) const { return false; }
|
virtual bool isExcluded(const BlobShape*) const { return false; }
|
||||||
|
|
||||||
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
|
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void cornerRadii(float out[4]) const;
|
virtual void cornerRadii(float out[4]) const;
|
||||||
|
|
||||||
virtual void updatePhysics() {}
|
virtual void updatePhysics() {}
|
||||||
|
|
||||||
@@ -67,25 +67,25 @@ virtual void cornerRadii(float out[4]) const;
|
|||||||
virtual void unregisterFromGroup();
|
virtual void unregisterFromGroup();
|
||||||
void updateCenteredDeformMatrix();
|
void updateCenteredDeformMatrix();
|
||||||
|
|
||||||
BlobGroup* m_group = nullptr;
|
BlobGroup* m_group = nullptr;
|
||||||
qreal m_radius = 0;
|
qreal m_radius = 0;
|
||||||
QMatrix4x4 m_deformMatrix; // identity by default
|
QMatrix4x4 m_deformMatrix; // identity by default
|
||||||
QMatrix4x4 m_centeredDeformMatrix;
|
QMatrix4x4 m_centeredDeformMatrix;
|
||||||
|
|
||||||
// Cached data from updatePolish
|
// Cached data from updatePolish
|
||||||
float m_cachedPaddedX = 0;
|
float m_cachedPaddedX = 0;
|
||||||
float m_cachedPaddedY = 0;
|
float m_cachedPaddedY = 0;
|
||||||
float m_cachedPaddedW = 0;
|
float m_cachedPaddedW = 0;
|
||||||
float m_cachedPaddedH = 0;
|
float m_cachedPaddedH = 0;
|
||||||
float m_pendingDw = 0;
|
float m_pendingDw = 0;
|
||||||
float m_pendingDh = 0;
|
float m_pendingDh = 0;
|
||||||
QRectF m_localPaddedRect;
|
QRectF m_localPaddedRect;
|
||||||
QVector<BlobRectData> m_cachedRects;
|
QVector<BlobRectData> m_cachedRects;
|
||||||
int m_cachedMyIndex = -2;
|
int m_cachedMyIndex = -2;
|
||||||
float m_pendingDx = 0;
|
float m_pendingDx = 0;
|
||||||
float m_pendingDy = 0;
|
float m_pendingDy = 0;
|
||||||
bool m_cachedHasInverted = false;
|
bool m_cachedHasInverted = false;
|
||||||
float m_cachedInvertedRadius = 0;
|
float m_cachedInvertedRadius = 0;
|
||||||
float m_cachedInvertedOuter[4] = {};
|
float m_cachedInvertedOuter[4] = {};
|
||||||
float m_cachedInvertedInner[4] = {};
|
float m_cachedInvertedInner[4] = {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,11 +24,12 @@ set(QT_QML_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml")
|
|||||||
qt_standard_project_setup(REQUIRES 6.9)
|
qt_standard_project_setup(REQUIRES 6.9)
|
||||||
|
|
||||||
function(qml_module arg_TARGET)
|
function(qml_module arg_TARGET)
|
||||||
cmake_parse_arguments(PARSE_ARGV 1 arg "" "URI" "SOURCES;LIBRARIES")
|
cmake_parse_arguments(PARSE_ARGV 1 arg "" "URI" "SOURCES;LIBRARIES;RESOURCES")
|
||||||
qt_add_qml_module(${arg_TARGET}
|
qt_add_qml_module(${arg_TARGET}
|
||||||
URI ${arg_URI}
|
URI ${arg_URI}
|
||||||
VERSION 1.0
|
VERSION 1.0
|
||||||
SOURCES ${arg_SOURCES}
|
SOURCES ${arg_SOURCES}
|
||||||
|
RESOURCES ${arg_RESOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_query_qml_module(${arg_TARGET}
|
qt_query_qml_module(${arg_TARGET}
|
||||||
@@ -47,6 +48,8 @@ function(qml_module arg_TARGET)
|
|||||||
target_link_libraries(${arg_TARGET} PRIVATE Qt::Core Qt::Qml ${arg_LIBRARIES})
|
target_link_libraries(${arg_TARGET} PRIVATE Qt::Core Qt::Qml ${arg_LIBRARIES})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
set_source_files_properties("${SETTINGS_INDEX_JSON}" PROPERTIES QT_RESOURCE_ALIAS "settings-index.json")
|
||||||
|
|
||||||
qml_module(ZShell
|
qml_module(ZShell
|
||||||
URI ZShell
|
URI ZShell
|
||||||
SOURCES
|
SOURCES
|
||||||
@@ -57,6 +60,8 @@ qml_module(ZShell
|
|||||||
toaster.hpp toaster.cpp
|
toaster.hpp toaster.cpp
|
||||||
qalculator.hpp qalculator.cpp
|
qalculator.hpp qalculator.cpp
|
||||||
zutils.hpp zutils.cpp
|
zutils.hpp zutils.cpp
|
||||||
|
RESOURCES
|
||||||
|
"${SETTINGS_INDEX_JSON}"
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
Qt::Gui
|
Qt::Gui
|
||||||
Qt::Quick
|
Qt::Quick
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
#include "sensorslib.hpp"
|
#include "sensorslib.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfile.h>
|
#include <qfile.h>
|
||||||
|
#include <qobject.h>
|
||||||
#include <qregularexpression.h>
|
#include <qregularexpression.h>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
@@ -243,6 +245,45 @@ void Gpu::detectNameOnce() {
|
|||||||
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
{QStringLiteral("-c"), QString::fromLatin1(kNameDetectScript)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Gpu::readGenericMemory() {
|
||||||
|
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
||||||
|
.entryList(
|
||||||
|
QStringList() << QStringLiteral("card*"),
|
||||||
|
QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
|
||||||
|
qreal totalMem = 0.0;
|
||||||
|
qreal usedMem = 0.0;
|
||||||
|
for (const QString& card : paths) {
|
||||||
|
QFile total(
|
||||||
|
QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_total")
|
||||||
|
.arg(card));
|
||||||
|
if (!total.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool ok = false;
|
||||||
|
const qreal v = total.readAll().trimmed().toDouble(&ok);
|
||||||
|
total.close();
|
||||||
|
if (ok) {
|
||||||
|
totalMem += v;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile used(QStringLiteral("/sys/class/drm/%1/device/mem_info_vram_used")
|
||||||
|
.arg(card));
|
||||||
|
if (!used.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool ok1 = false;
|
||||||
|
const qreal v1 = used.readAll().trimmed().toDouble(&ok1);
|
||||||
|
used.close();
|
||||||
|
if (ok1) {
|
||||||
|
usedMem += v1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMemoryTotal(totalMem);
|
||||||
|
setMemoryUsed(usedMem);
|
||||||
|
}
|
||||||
|
|
||||||
void Gpu::readGenericUsage() {
|
void Gpu::readGenericUsage() {
|
||||||
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
const QStringList paths = QDir(QStringLiteral("/sys/class/drm"))
|
||||||
.entryList(
|
.entryList(
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class Gpu : public TickingService {
|
|||||||
void readGenericUsage();
|
void readGenericUsage();
|
||||||
void startNvidiaUsage();
|
void startNvidiaUsage();
|
||||||
void readGpuTemperature();
|
void readGpuTemperature();
|
||||||
|
void readGenericMemory();
|
||||||
|
|
||||||
void setUserType(Type value);
|
void setUserType(Type value);
|
||||||
void setAutoType(Type value);
|
void setAutoType(Type value);
|
||||||
|
|||||||
+39
-26
@@ -10,23 +10,28 @@ Q_LOGGING_CATEGORY(lcAppDb, "ZShell.appdb", QtInfoMsg)
|
|||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_entry(entry), m_frequency(frequency) {
|
||||||
, m_entry(entry)
|
|
||||||
, m_frequency(frequency) {
|
|
||||||
const auto mo = m_entry->metaObject();
|
const auto mo = m_entry->metaObject();
|
||||||
const auto tmo = &AppEntry::staticMetaObject;
|
const auto tmo = &AppEntry::staticMetaObject;
|
||||||
|
|
||||||
for (const auto& prop :
|
for (const auto& prop :
|
||||||
{ "name", "comment", "execString", "startupClass", "genericName", "categories", "keywords" }) {
|
{"name",
|
||||||
|
"comment",
|
||||||
|
"execString",
|
||||||
|
"startupClass",
|
||||||
|
"genericName",
|
||||||
|
"categories",
|
||||||
|
"keywords"}) {
|
||||||
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
||||||
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
||||||
QObject::connect(m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
QObject::connect(
|
||||||
|
m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
||||||
m_entry = nullptr;
|
m_entry = nullptr;
|
||||||
deleteLater();
|
deleteLater();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* AppEntry::entry() const {
|
QObject* AppEntry::entry() const {
|
||||||
@@ -118,7 +123,9 @@ AppDb::AppDb(QObject* parent)
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AppDb::uuid() const {
|
QString AppDb::uuid() const {
|
||||||
@@ -145,7 +152,9 @@ void AppDb::setPath(const QString& path) {
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
|
|
||||||
updateAppFrequencies();
|
updateAppFrequencies();
|
||||||
}
|
}
|
||||||
@@ -183,7 +192,9 @@ void AppDb::setFavoriteApps(const QStringList& favApps) {
|
|||||||
if (re.isValid()) {
|
if (re.isValid()) {
|
||||||
m_favoriteAppsRegex << re;
|
m_favoriteAppsRegex << re;
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcAppDb) << "setFavoriteApps: regular expression is not valid:" << re.pattern();
|
qCWarning(lcAppDb)
|
||||||
|
<< "setFavoriteApps: regular expression is not valid:"
|
||||||
|
<< re.pattern();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,8 +202,7 @@ void AppDb::setFavoriteApps(const QStringList& favApps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString AppDb::regexifyString(const QString& original) const {
|
QString AppDb::regexifyString(const QString& original) const {
|
||||||
if (original.startsWith('^') && original.endsWith('$'))
|
if (original.startsWith('^') && original.endsWith('$')) return original;
|
||||||
return original;
|
|
||||||
|
|
||||||
const QString escaped = QRegularExpression::escape(original);
|
const QString escaped = QRegularExpression::escape(original);
|
||||||
return QStringLiteral("^%1$").arg(escaped);
|
return QStringLiteral("^%1$").arg(escaped);
|
||||||
@@ -206,9 +216,10 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
auto db = QSqlDatabase::database(m_uuid);
|
auto db = QSqlDatabase::database(m_uuid);
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
|
|
||||||
query.prepare("INSERT INTO frequencies (id, frequency) "
|
query.prepare(
|
||||||
"VALUES (:id, 1) "
|
"INSERT INTO frequencies (id, frequency) "
|
||||||
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
"VALUES (:id, 1) "
|
||||||
|
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
||||||
query.bindValue(":id", id);
|
query.bindValue(":id", id);
|
||||||
query.exec();
|
query.exec();
|
||||||
|
|
||||||
@@ -221,7 +232,8 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
emit appsChanged();
|
emit appsChanged();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcAppDb) << "incrementFrequency: could not find app with id" << id;
|
qCWarning(lcAppDb) << "incrementFrequency: could not find app with id"
|
||||||
|
<< id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,15 +244,16 @@ QList<AppEntry*>& AppDb::getSortedApps() const {
|
|||||||
QSet<QString> favSet;
|
QSet<QString> favSet;
|
||||||
favSet.reserve(m_sortedApps.size());
|
favSet.reserve(m_sortedApps.size());
|
||||||
for (const auto* app : std::as_const(m_sortedApps)) {
|
for (const auto* app : std::as_const(m_sortedApps)) {
|
||||||
if (isFavorite(app))
|
if (isFavorite(app)) favSet.insert(app->id());
|
||||||
favSet.insert(app->id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(m_sortedApps.begin(), m_sortedApps.end(), [&favSet](AppEntry* a, AppEntry* b) {
|
std::sort(
|
||||||
|
m_sortedApps.begin(),
|
||||||
|
m_sortedApps.end(),
|
||||||
|
[&favSet](AppEntry* a, AppEntry* b) {
|
||||||
const bool aIsFav = favSet.contains(a->id());
|
const bool aIsFav = favSet.contains(a->id());
|
||||||
const bool bIsFav = favSet.contains(b->id());
|
const bool bIsFav = favSet.contains(b->id());
|
||||||
if (aIsFav != bIsFav)
|
if (aIsFav != bIsFav) return aIsFav;
|
||||||
return aIsFav;
|
|
||||||
if (a->frequency() != b->frequency())
|
if (a->frequency() != b->frequency())
|
||||||
return a->frequency() > b->frequency();
|
return a->frequency() > b->frequency();
|
||||||
return a->name().localeAwareCompare(b->name()) < 0;
|
return a->name().localeAwareCompare(b->name()) < 0;
|
||||||
@@ -293,10 +306,10 @@ void AppDb::updateApps() {
|
|||||||
dirty = true;
|
dirty = true;
|
||||||
auto* const newEntry = new AppEntry(entry, getFrequency(id), this);
|
auto* const newEntry = new AppEntry(entry, getFrequency(id), this);
|
||||||
QObject::connect(newEntry, &QObject::destroyed, this, [id, this]() {
|
QObject::connect(newEntry, &QObject::destroyed, this, [id, this]() {
|
||||||
if (m_apps.remove(id)) {
|
if (m_apps.remove(id)) {
|
||||||
emit appsChanged();
|
emit appsChanged();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
m_apps.insert(id, newEntry);
|
m_apps.insert(id, newEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-26
@@ -65,11 +65,16 @@ class AppDb : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
||||||
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY favoriteAppsChanged REQUIRED)
|
QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY
|
||||||
|
favoriteAppsChanged REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppDb(QObject* parent = nullptr);
|
explicit AppDb(QObject* parent = nullptr);
|
||||||
@@ -82,36 +87,36 @@ Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
|||||||
[[nodiscard]] QObjectList entries() const;
|
[[nodiscard]] QObjectList entries() const;
|
||||||
void setEntries(const QObjectList& entries);
|
void setEntries(const QObjectList& entries);
|
||||||
|
|
||||||
[[nodiscard]] QStringList favoriteApps() const;
|
[[nodiscard]] QStringList favoriteApps() const;
|
||||||
void setFavoriteApps(const QStringList& favApps);
|
void setFavoriteApps(const QStringList& favApps);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<AppEntry> apps();
|
[[nodiscard]] QQmlListProperty<AppEntry> apps();
|
||||||
|
|
||||||
Q_INVOKABLE void incrementFrequency(const QString& id);
|
Q_INVOKABLE void incrementFrequency(const QString& id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
void entriesChanged();
|
void entriesChanged();
|
||||||
void favoriteAppsChanged();
|
void favoriteAppsChanged();
|
||||||
void appsChanged();
|
void appsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTimer* m_timer;
|
QTimer* m_timer;
|
||||||
|
|
||||||
const QString m_uuid;
|
const QString m_uuid;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QObjectList m_entries;
|
QObjectList m_entries;
|
||||||
QStringList m_favoriteApps;
|
QStringList m_favoriteApps;
|
||||||
QList<QRegularExpression> m_favoriteAppsRegex;
|
QList<QRegularExpression> m_favoriteAppsRegex;
|
||||||
QHash<QString, AppEntry*> m_apps;
|
QHash<QString, AppEntry*> m_apps;
|
||||||
mutable QList<AppEntry*> m_sortedApps;
|
mutable QList<AppEntry*> m_sortedApps;
|
||||||
|
|
||||||
QString regexifyString(const QString& original) const;
|
QString regexifyString(const QString& original) const;
|
||||||
QList<AppEntry*>& getSortedApps() const;
|
QList<AppEntry*>& getSortedApps() const;
|
||||||
bool isFavorite(const AppEntry* app) const;
|
bool isFavorite(const AppEntry* app) const;
|
||||||
quint32 getFrequency(const QString& id) const;
|
quint32 getFrequency(const QString& id) const;
|
||||||
void updateAppFrequencies();
|
void updateAppFrequencies();
|
||||||
void updateApps();
|
void updateApps();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell
|
} // namespace ZShell
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user