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
cursorShape: !enabled ? undefined : Qt.PointingHandCursor
enabled: parent.enabled
hoverEnabled: true
Behavior on stateOpacity {
+4 -31
View File
@@ -1,36 +1,9 @@
import QtQuick
import ZShell.Internal
Canvas {
StrokeCanvas {
id: root
property color penColor: "white"
property real 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();
}
penColor: "white"
penWidth: 4
}
-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
property bool dashboardShortcutActive
required property Drawing drawing
required property DrawingInput input
property bool osdShortcutActive
required property Panels panels
required property BarPopouts.Wrapper popouts
@@ -60,6 +59,7 @@ Item {
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
dragThreshold: 0
enabled: !root.visibilities.isDrawing
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
maximumPointCount: 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 {
id: hoverHandler
@@ -139,9 +173,10 @@ Item {
const x = point.position.x;
const y = point.position.y;
if (root.visibilities.isDrawing && !root.inLeftPanel(root.panels.drawing, x, y)) {
root.input.z = 2;
root.panels.drawing.expanded = false;
if (root.visibilities.isDrawing) {
if (root.inLeftPanel(root.panels.drawing, x, y))
root.panels.drawing.expanded = true;
return;
}
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 dock: dock
readonly property alias drawing: drawing
required property Canvas drawingItem
required property var drawingItem
readonly property alias launcher: launcher
readonly property alias notifications: notifications
readonly property alias osd: osd
+4 -19
View File
@@ -195,6 +195,8 @@ CustomWindow {
}
Item {
id: surface
anchors.fill: parent
layer.enabled: true
opacity: root.surfaceColor.a
@@ -364,30 +366,13 @@ CustomWindow {
active: visibilities.isDrawing
anchors.fill: parent
z: 2
sourceComponent: 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 {
id: interactions
@@ -396,7 +381,6 @@ CustomWindow {
borderThickness: root.borderLayoutThickness
drawing: drawingLoader.item
enabled: true
input: inputLoader.item
panels: panels
popouts: panels.popouts
screen: root.screen
@@ -451,6 +435,7 @@ CustomWindow {
anchors.left: parent.left
anchors.right: parent.right
enabled: !visibilities.isDrawing
fullscreen: root.hasFullscreen
popouts: panels.popouts
popoutsWrapper: panels.popoutsWrapper
+1 -1
View File
@@ -9,7 +9,7 @@ Item {
id: root
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
function syncFromPenColor() {
+1 -1
View File
@@ -10,7 +10,7 @@ import qs.Daemons
Item {
id: root
required property Canvas drawing
required property var drawing
property bool expanded: true
property real offsetScale: shouldBeActive ? 0 : 1
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_library(SENSORS_LIBRARY NAMES sensors 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
visualizerbars.hpp visualizerbars.cpp
linearindicatormanager.hpp linearindicatormanager.cpp
strokecanvasitem.hpp strokecanvasitem.cpp
strokecanvasrenderer.hpp strokecanvasrenderer.cpp
LIBRARIES
Qt::Gui
Qt::Quick
@@ -19,4 +21,5 @@ qml_module(ZShell-internal
Qt::Core
Qt::Network
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;
};
};