drawing reworked into c++ plugin, moved input handler to Interactions.qml as a PointHandler
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 11s
Python / lint-format (pull_request) Successful in 16s
Python / test (pull_request) Successful in 31s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m17s

This commit is contained in:
2026-06-18 13:48:58 +02:00
parent fbadcbb717
commit c102d0e38f
15 changed files with 281 additions and 118 deletions
+1
View File
@@ -51,6 +51,7 @@ MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: !enabled ? undefined : Qt.PointingHandCursor cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled
hoverEnabled: true hoverEnabled: true
Behavior on stateOpacity { Behavior on stateOpacity {
+4 -31
View File
@@ -1,36 +1,9 @@
import QtQuick import QtQuick
import ZShell.Internal
Canvas { StrokeCanvas {
id: root id: root
property color penColor: "white" penColor: "white"
property real penWidth: 4 penWidth: 4
property var points: []
function clear(): void {
var ctx = getContext('2d');
root.points = [];
ctx.reset();
root.requestPaint();
}
renderStrategy: Canvas.Cooperative
onPaint: {
if (points.length < 2)
return;
var ctx = root.getContext('2d');
ctx.save();
ctx.lineWidth = root.penWidth;
ctx.strokeStyle = root.penColor;
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for (var i = 1; i < points.length; i++)
ctx.lineTo(points[i].x, points[i].y);
ctx.stroke();
points = points.slice(points.length - 2);
ctx.restore();
}
} }
-60
View File
@@ -1,60 +0,0 @@
import Quickshell
import QtQuick
import qs.Components
import qs.Config
CustomMouseArea {
id: root
required property var bar
required property Drawing drawing
required property Panels panels
required property var popout
required property PersistentProperties visibilities
function inLeftPanel(panel: Item, x: real, y: real): bool {
return x < panel.x + panel.width + Config.barConfig.border && withinPanelHeight(panel, x, y);
}
function withinPanelHeight(panel: Item, x: real, y: real): bool {
const panelY = panel.y + bar.implicitHeight;
return y >= panelY && y <= panelY + panel.height;
}
acceptedButtons: Qt.LeftButton | Qt.RightButton
enabled: z > 0
hoverEnabled: true
visible: root.visibilities.isDrawing
onPositionChanged: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
return;
}
if (!(event.buttons & Qt.LeftButton) && root.inLeftPanel(root.popout, x, y)) {
root.z = -2;
root.panels.drawing.expanded = true;
}
}
onPressed: event => {
const x = event.x;
const y = event.y;
if (root.visibilities.isDrawing && (event.buttons & Qt.LeftButton)) {
root.panels.drawing.expanded = false;
root.drawing.points.push(Qt.point(x, y));
root.drawing.requestPaint();
return;
}
if (event.buttons & Qt.RightButton)
root.drawing.clear();
}
onReleased: {
root.drawing.points = [];
}
}
+39 -4
View File
@@ -12,7 +12,6 @@ Item {
required property real borderThickness required property real borderThickness
property bool dashboardShortcutActive property bool dashboardShortcutActive
required property Drawing drawing required property Drawing drawing
required property DrawingInput input
property bool osdShortcutActive property bool osdShortcutActive
required property Panels panels required property Panels panels
required property BarPopouts.Wrapper popouts required property BarPopouts.Wrapper popouts
@@ -60,6 +59,7 @@ Item {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0 dragThreshold: 0
enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 1 maximumPointCount: 1
minimumPointCount: 1 minimumPointCount: 1
@@ -117,6 +117,40 @@ Item {
} }
} }
PointHandler {
id: drawingHandler
property bool setInitialPoint: false
enabled: root.visibilities.isDrawing
onActiveChanged: {
console.log(active);
if (!active) {
setInitialPoint = false;
root.drawing.endStroke();
} else {
root.panels.drawing.expanded = false;
}
}
onPointChanged: {
const x = point.position.x;
const y = point.position.y;
const origX = point.pressPosition.x;
const origY = point.pressPosition.y;
if (x === 0 && y === 0 && origX === 0 && origY === 0)
return;
if (!setInitialPoint) {
setInitialPoint = true;
root.drawing.beginStroke(origX, origY);
return;
}
root.drawing.appendPoint(x, y);
}
}
HoverHandler { HoverHandler {
id: hoverHandler id: hoverHandler
@@ -139,9 +173,10 @@ Item {
const x = point.position.x; const x = point.position.x;
const y = point.position.y; const y = point.position.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) { if (root.visibilities.isDrawing) {
root.input.z = 2; if (root.inLeftPanel(root.panels.drawing, x, y))
root.panels.drawing.expanded = false; root.panels.drawing.expanded = true;
return;
} }
if (!root.visibilities.bar && Config.barConfig.autoHide && y < root.bar.implicitHeight) if (!root.visibilities.bar && Config.barConfig.autoHide && y < root.bar.implicitHeight)
+1 -1
View File
@@ -26,7 +26,7 @@ Item {
readonly property alias dashboardWrapper: dashboardWrapper readonly property alias dashboardWrapper: dashboardWrapper
readonly property alias dock: dock readonly property alias dock: dock
readonly property alias drawing: drawing readonly property alias drawing: drawing
required property Canvas drawingItem required property var drawingItem
readonly property alias launcher: launcher readonly property alias launcher: launcher
readonly property alias notifications: notifications readonly property alias notifications: notifications
readonly property alias osd: osd readonly property alias osd: osd
+4 -19
View File
@@ -195,6 +195,8 @@ CustomWindow {
} }
Item { Item {
id: surface
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: true
opacity: root.surfaceColor.a opacity: root.surfaceColor.a
@@ -364,30 +366,13 @@ CustomWindow {
active: visibilities.isDrawing active: visibilities.isDrawing
anchors.fill: parent anchors.fill: parent
z: 2
sourceComponent: Drawing { sourceComponent: Drawing {
id: drawing id: drawing
} }
} }
Loader {
id: inputLoader
active: visibilities.isDrawing
anchors.fill: parent
z: 2
sourceComponent: DrawingInput {
id: input
bar: bar
drawing: drawingLoader.item
panels: panels
popout: panels.drawing
visibilities: visibilities
}
}
Interactions { Interactions {
id: interactions id: interactions
@@ -396,7 +381,6 @@ CustomWindow {
borderThickness: root.borderLayoutThickness borderThickness: root.borderLayoutThickness
drawing: drawingLoader.item drawing: drawingLoader.item
enabled: true enabled: true
input: inputLoader.item
panels: panels panels: panels
popouts: panels.popouts popouts: panels.popouts
screen: root.screen screen: root.screen
@@ -451,6 +435,7 @@ CustomWindow {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen fullscreen: root.hasFullscreen
popouts: panels.popouts popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper popoutsWrapper: panels.popoutsWrapper
+1 -1
View File
@@ -9,7 +9,7 @@ Item {
id: root id: root
readonly property var colors: ["#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#a855f7", "#ec4899", "#ffffff", "#000000"] readonly property var colors: ["#ef4444", "#f97316", "#eab308", "#22c55e", "#06b6d4", "#3b82f6", "#a855f7", "#ec4899", "#ffffff", "#000000"]
required property Canvas drawing required property var drawing
required property var visibilities required property var visibilities
function syncFromPenColor() { function syncFromPenColor() {
+1 -1
View File
@@ -10,7 +10,7 @@ import qs.Daemons
Item { Item {
id: root id: root
required property Canvas drawing required property var drawing
property bool expanded: true property bool expanded: true
property real offsetScale: shouldBeActive ? 0 : 1 property real offsetScale: shouldBeActive ? 0 : 1
required property ShellScreen screen required property ShellScreen screen
+1 -1
View File
@@ -1,4 +1,4 @@
find_package(Qt6 REQUIRED COMPONENTS ShaderTools Core Qml Gui Quick Concurrent Sql Network DBus) find_package(Qt6 REQUIRED COMPONENTS ShaderTools Core Qml Gui Quick Concurrent Sql Network DBus CanvasPainter)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
find_library(SENSORS_LIBRARY NAMES sensors REQUIRED) find_library(SENSORS_LIBRARY NAMES sensors REQUIRED)
find_path(SENSORS_INCLUDE_DIR NAMES sensors/sensors.h REQUIRED) find_path(SENSORS_INCLUDE_DIR NAMES sensors/sensors.h REQUIRED)
+3
View File
@@ -12,6 +12,8 @@ qml_module(ZShell-internal
lidwatcher.hpp lidwatcher.cpp lidwatcher.hpp lidwatcher.cpp
visualizerbars.hpp visualizerbars.cpp visualizerbars.hpp visualizerbars.cpp
linearindicatormanager.hpp linearindicatormanager.cpp linearindicatormanager.hpp linearindicatormanager.cpp
strokecanvasitem.hpp strokecanvasitem.cpp
strokecanvasrenderer.hpp strokecanvasrenderer.cpp
LIBRARIES LIBRARIES
Qt::Gui Qt::Gui
Qt::Quick Qt::Quick
@@ -19,4 +21,5 @@ qml_module(ZShell-internal
Qt::Core Qt::Core
Qt::Network Qt::Network
Qt::DBus Qt::DBus
Qt::CanvasPainter
) )
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <QColor>
#include <QPointF>
#include <QVector>
namespace ZShell::internal {
struct Stroke {
QVector<QPointF> points;
QColor color;
float width;
};
};
@@ -0,0 +1,68 @@
#include "strokecanvasitem.hpp"
#include "strokecanvasrenderer.hpp"
#include <qnamespace.h>
namespace ZShell::internal {
StrokeCanvasItem::StrokeCanvasItem(QQuickItem *parent) : QCanvasPainterItem(parent) {
setFillColor(Qt::transparent);
setAlphaBlending(true);
}
QCanvasPainterItemRenderer *StrokeCanvasItem::createItemRenderer() const {
return new StrokeCanvasRenderer;
}
void StrokeCanvasItem::setPenColor(const QColor &color) {
if (m_penColor == color)
return;
m_penColor = color;
update();
emit penColorChanged();
}
void StrokeCanvasItem::setPenWidth(float width) {
if (qFuzzyCompare(m_penWidth, width))
return;
m_penWidth = width;
update();
emit penWidthChanged();
}
void StrokeCanvasItem::beginStroke(qreal x, qreal y) {
m_currentStroke.points.clear();
m_currentStroke.points.append({x, y});
m_currentStroke.color = m_penColor;
m_currentStroke.width = m_penWidth;
update();
}
void StrokeCanvasItem::appendPoint(qreal x, qreal y) {
m_currentStroke.points.append({x, y});
update();
}
void StrokeCanvasItem::endStroke() {
if (m_currentStroke.points.isEmpty())
return;
m_strokes.append(m_currentStroke);
m_currentStroke.points.clear();
update();
}
void StrokeCanvasItem::clear() {
m_strokes.clear();
m_currentStroke.points.clear();
update();
}
};
@@ -0,0 +1,59 @@
#pragma once
#include <QCanvasPainterItem>
#include <QColor>
#include <QPointF>
#include <QVector>
#include <qcolor.h>
#include <qcontainerfwd.h>
#include "stroke.hpp"
namespace ZShell::internal {
class StrokeCanvasRenderer;
class StrokeCanvasItem : public QCanvasPainterItem {
Q_OBJECT
QML_NAMED_ELEMENT(StrokeCanvas)
Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor NOTIFY penColorChanged)
Q_PROPERTY(qreal penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
public:
explicit StrokeCanvasItem(QQuickItem *parent = nullptr);
[[nodiscard]] QColor penColor() const {
return m_penColor;
}
[[nodiscard]] float penWidth() const {
return m_penWidth;
}
void setPenColor(const QColor &color);
void setPenWidth(float width);
Q_INVOKABLE void clear();
Q_INVOKABLE void beginStroke(qreal x, qreal y);
Q_INVOKABLE void appendPoint(qreal x, qreal y);
Q_INVOKABLE void endStroke();
signals:
void penColorChanged();
void penWidthChanged();
protected:
[[nodiscard]] QCanvasPainterItemRenderer *createItemRenderer() const override;
private:
friend class StrokeCanvasRenderer;
QColor m_penColor = Qt::white;
float m_penWidth = 4.f;
QVector<Stroke> m_strokes;
Stroke m_currentStroke;
};
};
@@ -0,0 +1,61 @@
#include "strokecanvasrenderer.hpp"
#include "strokecanvasitem.hpp"
namespace ZShell::internal {
static void drawStroke(
QCanvasPainter *painter,
const QVector<QPointF> &points,
const QColor &color,
float width) {
if (points.isEmpty())
return;
painter->setStrokeStyle(color);
painter->setFillStyle(color);
painter->setLineWidth(width);
painter->setLineCap(QCanvasPainter::LineCap::Round);
painter->setLineJoin(QCanvasPainter::LineJoin::Round);
if (points.size() == 1) {
painter->beginPath();
painter->circle(points.front(), width * 0.5f);
painter->fill();
return;
}
painter->beginPath();
painter->moveTo(points[0]);
for (qsizetype i = 1; i < points.size(); ++i)
painter->lineTo(points[i]);
painter->stroke();
}
void StrokeCanvasRenderer::synchronizeData(QCanvasPainterItem *item) {
auto *canvas = static_cast<StrokeCanvasItem *>(item);
m_penColor = canvas->m_penColor;
m_penWidth = canvas->m_penWidth;
m_strokes = canvas->m_strokes;
m_currentStroke = canvas->m_currentStroke;
}
void StrokeCanvasRenderer::paint(QCanvasPainter *painter) {
painter->clearRect(0, 0, width(), height());
for (const auto &stroke : m_strokes)
drawStroke(painter, stroke.points, stroke.color, stroke.width);
drawStroke(
painter,
m_currentStroke.points,
m_currentStroke.color,
m_currentStroke.width
);
}
};
@@ -0,0 +1,23 @@
#pragma once
#include <QCanvasPainterItemRenderer>
#include "stroke.hpp"
namespace ZShell::internal {
class StrokeCanvasRenderer final : public QCanvasPainterItemRenderer {
public:
void synchronizeData(QCanvasPainterItem *item) override;
void paint(QCanvasPainter *painter) override;
private:
QColor m_penColor;
float m_penWidth = 4.f;
QVector<Stroke> m_strokes;
Stroke m_currentStroke;
};
};