8 Commits
Author SHA1 Message Date
zach f01001dcfa merge main into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 9s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 49s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m42s
C++ / build (pull_request) Successful in 2m28s
2026-06-30 18:15:43 +02:00
zach 61019204d8 formatter 2026-06-30 15:35:25 +02:00
zach bc6b0d50ab Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 34s
Python / lint-format (pull_request) Successful in 35s
Python / test (pull_request) Successful in 1m11s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m58s
C++ / build (pull_request) Successful in 4m31s
2026-06-29 22:10:45 +02:00
zach f27c9afc25 more methods for wifi and scanning + layout
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 15s
Python / test (pull_request) Successful in 29s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m6s
2026-06-26 15:15:03 +02:00
Inorishio 5962fc8354 Merge branch 'main' into module/wifi
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 17s
Python / lint-format (pull_request) Successful in 27s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m39s
2026-06-24 18:58:36 +02:00
Inorishio 1adcb9841c Added network properties, nicNames, netDevices, networkName < reminder I should use layouts.
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 10s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 50s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m52s
2026-06-22 23:34:58 +02:00
Inorishio 0e0f8555ed fixing singleton in helpers for network + getting output from systray 2026-06-15 20:48:01 +02:00
Inorishio 1d63e67554 removed network module and moved it to tray, have a popout window without content! everything still in wi.i.p. 2026-06-13 18:22:03 +02:00
20 changed files with 600 additions and 266 deletions
+1
View File
@@ -15,3 +15,4 @@ dist/
**/target/
**/test-plugins/
**/Charts/
**/network-dev/
-4
View File
@@ -40,10 +40,6 @@ JsonObject {
id: "tray",
enabled: true
},
{
id: "network",
enabled: false
},
{
id: "clock",
enabled: true
+125
View File
@@ -0,0 +1,125 @@
pragma Singleton
import Quickshell
import Quickshell.Networking
import QtQuick
Singleton {
id: root
readonly property list<NetworkDevice> devices: Networking.devices.values
readonly property list<Network> networks: initialBuildNetworks()
property bool scanning: false
readonly property list<WifiDevice> wifiDevices: devices.filter(d => wifiDevice(d))
readonly property bool wifiEnabled: Networking.wifiEnabled
// Original code
readonly property list<NetworkDevice> netDevice: Networking.devices.values
readonly property string networkName: getNetworkName()
readonly property list<string> nicNames: networkInterfaceCardNames()
// Useless will prob remove
function getConnectedDevices() {
let connectedDevices = [];
for (var i = 0; i < netDevice.length; i++) {
if (netDevice[i].connected === true) {
connectedDevices.push(netDevice[i].name);
}
}
return connectedDevices;
}
// SHOULD retrieve network names of connected devices
// Currently only gives connected nic name
function getNetworkName() {
const devices = netDevice.filter(device => device.connected === true);
for (var i = 0; i < devices.length; i++) {
return devices[i].name;
}
return "Failed network name";
}
// Searches wired/wireless devices and sets them in a list
function networkInterfaceCardNames() {
let nicList = [];
for (let i = 0; i < netDevice.length; ++i) {
nicList.push(netDevice[i].name);
}
return nicList;
}
//
function initialBuildNetworks(): void {
const init = [];
for (const d of wifiDevices) {
d.scannerEnabled = true;
init.push(...d.networks.values);
d.scannerEnabled = false;
}
networks = init;
}
function isSecure(security): bool {
return !(security === WifiSecurityType.Open);
}
function rebuildNetworks(): void {
if (!scanning)
return;
const next = [];
for (const d of wifiDevices) {
next.push(...d.networks.values);
}
networks = next;
}
function rescanWifi(): void {
scanning = true;
setScan(true);
scanTimer.restart();
}
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;
}
Timer {
id: scanTimer
interval: 5000
repeat: false
onTriggered: {
root.rebuildNetworks();
root.setScan(false);
root.scanning = false;
}
}
Timer {
interval: 500
repeat: true
running: root.scanning
onTriggered: {
root.rebuildNetworks();
}
}
}
-10
View File
@@ -9,7 +9,6 @@ import qs.Config
import qs.Helpers
import qs.Modules.SysTray
import qs.Modules.SysTray.Widgets
import qs.Modules.Network
import qs.Modules.Updates
RowLayout {
@@ -185,15 +184,6 @@ RowLayout {
}
}
DelegateChoice {
roleValue: "network"
delegate: WrappedLoader {
sourceComponent: NetworkWidget {
}
}
}
DelegateChoice {
roleValue: "media"
-1
View File
@@ -10,7 +10,6 @@ import qs.Config
import qs.Helpers
import qs.Modules.SysTray
import qs.Modules.SysTray.Widgets
import qs.Modules.Network
Item {
id: root
-1
View File
@@ -6,7 +6,6 @@ import QtQuick
import qs.Config
import qs.Components
import qs.Modules.WSOverview
import qs.Modules.Network
import qs.Modules.SysTray.Popouts
import qs.Modules.Updates
-36
View File
@@ -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"
}
}
}
}
-25
View File
@@ -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"
}
}
}
+219
View File
@@ -0,0 +1,219 @@
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
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("Network")
}
Toggle {
Layout.preferredHeight: visible ? implicitHeight : 0
checked: Network.wifiEnabled
label: qsTr("WiFi enabled")
toggle.onToggled: Network.setWifi(checked)
}
CustomText {
Layout.preferredHeight: visible ? implicitHeight : 0
Layout.rightMargin: Appearance.padding.extraSmall
Layout.topMargin: visible ? Appearance.spacing.small : 0
color: DynamicColors.palette.m3onSurfaceVariant
text: qsTr("%1 networks available").arg(Network.networks.length) // qmllint disable missing-property
}
}
ColumnLayout {
id: networks
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.large
anchors.right: parent.right
anchors.top: networkPopContent.bottom
Repeater {
model: ScriptModel {
values: [...Network.networks]
}
RowLayout {
id: networkItem
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight : 0
Layout.rightMargin: Appearance.padding.extraSmall
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: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, Network.isSecure(networkItem.modelData.security))
}
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Appearance.spacing.extraSmall
Layout.rightMargin: Appearance.spacing.extraSmall
color: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
elide: Text.ElideRight
text: networkItem.modelData.name
}
CustomRect {
color: Qt.alpha(DynamicColors.palette.m3primary, networkItem.modelData.active ? 1 : 0)
implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.extraSmall
implicitWidth: implicitHeight
radius: Appearance.rounding.full
// CircularIndicator {
// anchors.fill: parent
// running: networkItem.loading
// }
StateLayer {
color: networkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
onClicked: {
console.log(Network.devices[1].scannerEnabled, Network.devices[2].scannerEnabled, Network.devices[3].scannerEnabled, Network.devices[4].scannerEnabled);
}
}
MaterialIcon {
id: wirelessConnectIcon
anchors.centerIn: parent
animate: true
color: networkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
text: networkItem.modelData.active ? "link_off" : "link"
// opacity: networkItem.loading ? 0 : 1
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
}
}
}
}
CustomRect {
Layout.fillWidth: true
Layout.preferredHeight: visible ? implicitHeight : 0
Layout.topMargin: visible ? Appearance.spacing.small : 0
color: DynamicColors.palette.m3primaryContainer
implicitHeight: rescanBtn.implicitHeight + Appearance.padding.small
radius: Appearance.rounding.full
StateLayer {
color: DynamicColors.palette.m3onPrimaryContainer
enabled: !Network.scanning
onClicked: Network.rescanWifi()
}
RowLayout {
id: rescanBtn
anchors.centerIn: parent
opacity: Network.scanning ? 0 : 1
spacing: Appearance.spacing.small
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
MaterialIcon {
id: scanIcon
Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575)
animate: true
color: DynamicColors.palette.m3onPrimaryContainer
text: "wifi_find"
}
CustomText {
Layout.topMargin: -Math.round(scanIcon.fontInfo.pointSize * 0.0575)
color: DynamicColors.palette.m3onPrimaryContainer
text: qsTr("Rescan networks")
}
}
CircularIndicator {
anchors.centerIn: parent
bgColor: "transparent"
implicitSize: parent.implicitHeight - Appearance.padding.large
running: Network.scanning
strokeWidth: Appearance.padding.extraSmall / 2
}
}
}
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
}
}
}
+9
View File
@@ -52,6 +52,11 @@ RowLayout {
id: "audio",
item: child
};
if (child.objectName === "networkWidget" && Config.bar.popouts.network)
return {
id: "network",
item: child
};
if (child.objectName === "upowerWidget" && Config.bar.popouts.upower)
return {
id: "upower",
@@ -146,6 +151,10 @@ RowLayout {
}
}
NetworkWidget {
objectName: "networkWidget"
}
UPowerWidget {
Layout.fillHeight: true
objectName: "upowerWidget"
+24
View File
@@ -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"
}
}
+1 -2
View File
@@ -26,8 +26,7 @@ void BlobGroup::setColor(const QColor& c) {
}
void BlobGroup::setCornerFill(bool e) {
if (m_cornerFill == e)
return;
if (m_cornerFill == e) return;
m_cornerFill = e;
emit cornerFillChanged();
markDirty();
+24 -22
View File
@@ -9,11 +9,15 @@ class BlobShape;
class BlobInvertedRect;
class BlobGroup : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFillChanged)
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
smoothingChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(
bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY
cornerFillChanged)
public:
explicit BlobGroup(QObject* parent = nullptr);
@@ -27,14 +31,12 @@ Q_PROPERTY(bool cornerFill READ cornerFill WRITE setCornerFill NOTIFY cornerFill
void setColor(const QColor& c);
[[nodiscard]] bool cornerFill() const {
return m_cornerFill;
}
[[nodiscard]] bool cornerFill() const { return m_cornerFill; }
void setCornerFill(bool e);
void setCornerFill(bool e);
void addShape(BlobShape* shape);
void removeShape(BlobShape* shape);
void addShape(BlobShape* shape);
void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect);
@@ -49,16 +51,16 @@ void removeShape(BlobShape* shape);
void markShapeDirty(BlobShape* source);
void ensurePhysicsUpdated();
signals:
void smoothingChanged();
void colorChanged();
void cornerFillChanged();
signals:
void smoothingChanged();
void colorChanged();
void cornerFillChanged();
private:
qreal m_smoothing = 32.0;
QColor m_color{ 0x44, 0x88, 0xff };
bool m_cornerFill = true;
QList<BlobShape*> m_shapes;
BlobInvertedRect* m_invertedRect = nullptr;
bool m_physicsUpdated = false;
private:
qreal m_smoothing = 32.0;
QColor m_color{0x44, 0x88, 0xff};
bool m_cornerFill = true;
QList<BlobShape*> m_shapes;
BlobInvertedRect* m_invertedRect = nullptr;
bool m_physicsUpdated = false;
};
+28 -29
View File
@@ -32,11 +32,9 @@ void BlobRect::updatePolish() {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
},
Qt::QueuedConnection
);
if (m_group) m_group->markDirty();
},
Qt::QueuedConnection);
}
} else {
QMetaObject::invokeMethod(
@@ -184,8 +182,7 @@ bool BlobRect::isExcluded(const BlobShape* other) const {
bool BlobRect::isCornerExcluded(const BlobShape* other) const {
for (const auto& ptr : m_excludeCorners) {
if (ptr == other)
return true;
if (ptr == other) return true;
}
return false;
}
@@ -203,8 +200,15 @@ QQmlListProperty<BlobRect> BlobRect::exclude() {
}
QQmlListProperty<BlobRect> BlobRect::excludeCorners() {
return QQmlListProperty<BlobRect>(this, nullptr, &excludeCornersAppend, &excludeCornersCount, &excludeCornersAt,
&excludeCornersClear, &excludeCornersReplace, &excludeCornersRemoveLast);
return QQmlListProperty<BlobRect>(
this,
nullptr,
&excludeCornersAppend,
&excludeCornersCount,
&excludeCornersAt,
&excludeCornersClear,
&excludeCornersReplace,
&excludeCornersRemoveLast);
}
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
@@ -249,11 +253,11 @@ void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
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);
self->m_excludeCorners.append(rect);
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeCornersChanged();
}
@@ -262,36 +266,33 @@ qsizetype BlobRect::excludeCornersCount(QQmlListProperty<BlobRect>* prop) {
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);
return self->m_excludeCorners.at(index);
}
void BlobRect::excludeCornersClear(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_excludeCorners.isEmpty())
return;
if (self->m_excludeCorners.isEmpty()) return;
self->m_excludeCorners.clear();
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
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);
self->m_excludeCorners[index] = rect;
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeCornersChanged();
}
void BlobRect::excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_excludeCorners.isEmpty())
return;
if (self->m_excludeCorners.isEmpty()) return;
self->m_excludeCorners.removeLast();
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeCornersChanged();
}
@@ -319,11 +320,9 @@ void BlobRect::checkAtRest(float speed) {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
},
Qt::QueuedConnection
);
if (m_group) m_group->markDirty();
},
Qt::QueuedConnection);
}
}
}
+60 -41
View File
@@ -8,18 +8,32 @@
#include <qqmllist.h>
class BlobRect : public BlobShape {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
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)
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
qreal stiffness READ stiffness WRITE setStiffness NOTIFY
stiffnessChanged)
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
Q_PROPERTY(
qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
deformScaleChanged)
Q_PROPERTY(
QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
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:
explicit BlobRect(QQuickItem* parent = nullptr);
@@ -52,12 +66,12 @@ Q_PROPERTY(
}
}
QQmlListProperty<BlobRect> exclude();
QQmlListProperty<BlobRect> excludeCorners();
QQmlListProperty<BlobRect> exclude();
QQmlListProperty<BlobRect> excludeCorners();
bool isExcluded(const BlobShape* other) const override;
bool isCornerExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override;
bool isExcluded(const BlobShape* other) const override;
bool isCornerExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override;
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
@@ -77,16 +91,16 @@ void cornerRadii(float out[4]) const override;
void setBottomRightRadius(qreal r);
signals:
void stiffnessChanged();
void dampingChanged();
void deformScaleChanged();
void excludeChanged();
void excludeCornersChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
signals:
void stiffnessChanged();
void dampingChanged();
void deformScaleChanged();
void excludeChanged();
void excludeCornersChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
protected:
void updatePolish() override;
@@ -121,20 +135,25 @@ void bottomRightRadiusChanged();
qreal m_bottomLeftRadius = -1;
qreal m_bottomRightRadius = -1;
QList<QPointer<BlobRect> > m_exclude;
QList<QPointer<BlobRect> > m_excludeCorners;
QList<QPointer<BlobRect>> m_exclude;
QList<QPointer<BlobRect>> m_excludeCorners;
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeClear(QQmlListProperty<BlobRect>* prop);
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeAt(
QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeClear(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 qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeCornersAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
static void excludeCornersReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
static void excludeCornersAppend(
QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCornersCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeCornersAt(
QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeCornersClear(QQmlListProperty<BlobRect>* prop);
static void excludeCornersReplace(
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
static void excludeCornersRemoveLast(QQmlListProperty<BlobRect>* prop);
};
+4 -8
View File
@@ -38,8 +38,7 @@ static float cornerFillFactor(float sd, float smoothFactor) {
return std::max(outside, inside);
}
BlobShape::BlobShape(QQuickItem* parent)
: QQuickItem(parent) {
BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
setFlag(ItemHasContents);
}
@@ -312,13 +311,10 @@ void BlobShape::updatePolish() {
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
for (qsizetype j = 0; cornerFill && j < rectCount; ++j) {
if (j == i)
continue;
if (riExcludeMask & (1 << j))
continue;
if (j == i) continue;
if (riExcludeMask & (1 << j)) continue;
BlobShape* const sj = rectShapes[j];
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si))
continue;
if (si->isCornerExcluded(sj) || sj->isCornerExcluded(si)) continue;
const auto& rj = m_cachedRects[j];
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);
+24 -24
View File
@@ -55,11 +55,11 @@ class BlobShape : public QQuickItem {
virtual bool isExcluded(const BlobShape*) const { return false; }
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
return false;
}
virtual bool isCornerExcluded(const BlobShape* /*other*/) const {
return false;
}
virtual void cornerRadii(float out[4]) const;
virtual void cornerRadii(float out[4]) const;
virtual void updatePhysics() {}
@@ -67,25 +67,25 @@ virtual void cornerRadii(float out[4]) const;
virtual void unregisterFromGroup();
void updateCenteredDeformMatrix();
BlobGroup* m_group = nullptr;
qreal m_radius = 0;
QMatrix4x4 m_deformMatrix; // identity by default
QMatrix4x4 m_centeredDeformMatrix;
BlobGroup* m_group = nullptr;
qreal m_radius = 0;
QMatrix4x4 m_deformMatrix; // identity by default
QMatrix4x4 m_centeredDeformMatrix;
// Cached data from updatePolish
float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0;
float m_cachedPaddedH = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
QRectF m_localPaddedRect;
QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2;
float m_pendingDx = 0;
float m_pendingDy = 0;
bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {};
float m_cachedInvertedInner[4] = {};
// Cached data from updatePolish
float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0;
float m_cachedPaddedH = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
QRectF m_localPaddedRect;
QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2;
float m_pendingDx = 0;
float m_pendingDy = 0;
bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {};
float m_cachedInvertedInner[4] = {};
};
+39 -26
View File
@@ -10,23 +10,28 @@ Q_LOGGING_CATEGORY(lcAppDb, "ZShell.appdb", QtInfoMsg)
namespace ZShell {
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
: QObject(parent)
, m_entry(entry)
, m_frequency(frequency) {
: QObject(parent), m_entry(entry), m_frequency(frequency) {
const auto mo = m_entry->metaObject();
const auto tmo = &AppEntry::staticMetaObject;
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 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]() {
m_entry = nullptr;
deleteLater();
});
m_entry = nullptr;
deleteLater();
});
}
QObject* AppEntry::entry() const {
@@ -118,7 +123,9 @@ AppDb::AppDb(QObject* parent)
db.open();
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 {
@@ -145,7 +152,9 @@ void AppDb::setPath(const QString& path) {
db.open();
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();
}
@@ -183,7 +192,9 @@ void AppDb::setFavoriteApps(const QStringList& favApps) {
if (re.isValid()) {
m_favoriteAppsRegex << re;
} 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 {
if (original.startsWith('^') && original.endsWith('$'))
return original;
if (original.startsWith('^') && original.endsWith('$')) return original;
const QString escaped = QRegularExpression::escape(original);
return QStringLiteral("^%1$").arg(escaped);
@@ -206,9 +216,10 @@ void AppDb::incrementFrequency(const QString& id) {
auto db = QSqlDatabase::database(m_uuid);
QSqlQuery query(db);
query.prepare("INSERT INTO frequencies (id, frequency) "
"VALUES (:id, 1) "
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
query.prepare(
"INSERT INTO frequencies (id, frequency) "
"VALUES (:id, 1) "
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
query.bindValue(":id", id);
query.exec();
@@ -221,7 +232,8 @@ void AppDb::incrementFrequency(const QString& id) {
emit appsChanged();
}
} 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;
favSet.reserve(m_sortedApps.size());
for (const auto* app : std::as_const(m_sortedApps)) {
if (isFavorite(app))
favSet.insert(app->id());
if (isFavorite(app)) 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 bIsFav = favSet.contains(b->id());
if (aIsFav != bIsFav)
return aIsFav;
if (aIsFav != bIsFav) return aIsFav;
if (a->frequency() != b->frequency())
return a->frequency() > b->frequency();
return a->name().localeAwareCompare(b->name()) < 0;
@@ -293,10 +306,10 @@ void AppDb::updateApps() {
dirty = true;
auto* const newEntry = new AppEntry(entry, getFrequency(id), this);
QObject::connect(newEntry, &QObject::destroyed, this, [id, this]() {
if (m_apps.remove(id)) {
emit appsChanged();
}
});
if (m_apps.remove(id)) {
emit appsChanged();
}
});
m_apps.insert(id, newEntry);
}
}
+31 -26
View File
@@ -65,11 +65,16 @@ class AppDb : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
Q_PROPERTY(QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY favoriteAppsChanged REQUIRED)
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(
QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
REQUIRED)
Q_PROPERTY(
QStringList favoriteApps READ favoriteApps WRITE setFavoriteApps NOTIFY
favoriteAppsChanged REQUIRED)
Q_PROPERTY(
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
public:
explicit AppDb(QObject* parent = nullptr);
@@ -82,36 +87,36 @@ Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
[[nodiscard]] QObjectList entries() const;
void setEntries(const QObjectList& entries);
[[nodiscard]] QStringList favoriteApps() const;
void setFavoriteApps(const QStringList& favApps);
[[nodiscard]] QStringList favoriteApps() const;
void setFavoriteApps(const QStringList& favApps);
[[nodiscard]] QQmlListProperty<AppEntry> apps();
[[nodiscard]] QQmlListProperty<AppEntry> apps();
Q_INVOKABLE void incrementFrequency(const QString& id);
signals:
void pathChanged();
void entriesChanged();
void favoriteAppsChanged();
void appsChanged();
signals:
void pathChanged();
void entriesChanged();
void favoriteAppsChanged();
void appsChanged();
private:
QTimer* m_timer;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QStringList m_favoriteApps;
QList<QRegularExpression> m_favoriteAppsRegex;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QStringList m_favoriteApps;
QList<QRegularExpression> m_favoriteAppsRegex;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
QString regexifyString(const QString& original) const;
QList<AppEntry*>& getSortedApps() const;
bool isFavorite(const AppEntry* app) const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
QString regexifyString(const QString& original) const;
QList<AppEntry*>& getSortedApps() const;
bool isFavorite(const AppEntry* app) const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
};
} // namespace ZShell
+11 -11
View File
@@ -12,18 +12,18 @@ class ZUtils : public QObject {
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QString version READ version CONSTANT)
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
Q_PROPERTY(QString version READ version CONSTANT)
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
Q_INVOKABLE static bool copyFile(
const QUrl& source, const QUrl& target, bool overwrite = true);