Feat: LLM Chat #155

Merged
zach merged 67 commits from feat/llm-chat-tab into main 2026-10-18 17:05:05 +02:00
4 changed files with 110 additions and 86 deletions
Showing only changes of commit f1918172c3 - Show all commits
+13 -12
View File
@@ -129,22 +129,22 @@ Item {
Behavior on rsx {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {}
Anim {}
}
Behavior on rsy {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {}
Anim {}
}
Behavior on sh {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {}
Anim {}
}
Behavior on sw {
enabled: !selectArea.pressed && root.mode === "select"
ExAnim {}
Anim {}
}
Behavior on opacity {
Anim {
@@ -236,19 +236,19 @@ Item {
to: 0
}
ExAnim {
Anim {
properties: "rsx,rsy"
target: root
to: 0
}
ExAnim {
Anim {
property: "sw"
target: root
to: root.screen.width
}
ExAnim {
Anim {
property: "sh"
target: root
to: root.screen.height
@@ -286,6 +286,7 @@ Item {
anchors.fill: parent
inkColor: root.inkColor
layer.enabled: true
selection: selectionRect
tool: root.mode === "edit" && root.tool !== "move" ? root.tool : "none"
layer.effect: MultiEffect {
@@ -297,7 +298,7 @@ Item {
}
}
Rectangle {
CustomRect {
id: overlay
anchors.fill: parent
@@ -334,12 +335,11 @@ Item {
}
}
Rectangle {
CustomRect {
id: border
border.color: Colors.palette.m3primary
border.width: root.realBorderWidth
color: "transparent"
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
@@ -355,10 +355,13 @@ Item {
SelectionHandles {
id: handles
border: root.realBorderWidth
selH: root.sh
selW: root.sw
selX: root.rsx
selY: root.rsy
selection: selectionRect
tool: root.tool
visible: root.mode === "edit" && root.tool === "move"
onBoundsChanged: (x, y, w, h) => {
@@ -396,6 +399,4 @@ Item {
onToolSelected: t => root.tool = t
onUndoRequested: annotations.undo()
}
component ExAnim: Anim {}
}
+48 -55
View File
@@ -8,13 +8,13 @@ Item {
property color inkColor: "#ff3b30"
property real inkWidth: 4
required property Rectangle selection
property string tool: "none"
signal changed
function clearAll() {
committedCanvas.clear();
previewCanvas.clear();
canvas.clear();
changed();
}
@@ -54,89 +54,82 @@ Item {
}
function undo() {
committedCanvas.undoLastGroup();
canvas.undoLastGroup();
changed();
}
anchors.fill: parent
StrokeCanvas {
id: committedCanvas
id: canvas
anchors.fill: parent
penColor: root.inkColor
penWidth: root.inkWidth
}
StrokeCanvas {
id: previewCanvas
anchors.fill: parent
penColor: root.inkColor
penWidth: root.inkWidth
}
PointHandler {
MouseArea {
id: drawHandler
property real startX
property real startY
property bool started: false
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent
cursorShape: undefined
enabled: root.tool !== "none"
hoverEnabled: true
onActiveChanged: {
if (active)
onPositionChanged: e => {
const x = e.x;
const y = e.y;
canvas.hoverPoint = Qt.point(x, y);
if (!pressed)
return;
if (root.tool === "pen") {
committedCanvas.endStroke();
} else if (started) {
const pts = root.sampleShape(root.tool, startX, startY, point.position.x, point.position.y);
if (pts.length > 0) {
committedCanvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
committedCanvas.appendPoint(pts[i][0], pts[i][1]);
committedCanvas.endStroke();
}
previewCanvas.clear();
}
started = false;
root.changed();
}
onPointChanged: {
if (!active)
canvas.appendPoint(x, y);
return;
}
if (point.pressedButtons & Qt.RightButton) {
root.clearAll();
return;
const pts = root.sampleShape(root.tool, startX, startY, x, y);
if (pts.length > 0) {
canvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
canvas.appendPoint(pts[i][0], pts[i][1]);
}
}
onPressed: e => {
if (e.button & Qt.LeftButton) {
startX = e.x;
startY = e.y;
canvas.beginStroke(e.x, e.y);
} else if (e.button & Qt.RightButton) {
root.clearAll();
}
}
onPressedChanged: if (!pressed)
canvas.endStroke()
}
HoverHandler {
cursorShape: {
if (root.tool === "none")
return undefined;
const x = point.position.x;
const y = point.position.y;
const withinXWidth = x > root.selection.x && x < root.selection.x + root.selection.implicitWidth;
const withinYHeight = y > root.selection.y && y < root.selection.y + root.selection.implicitHeight;
if (root.tool === "pen") {
if (!started) {
started = true;
committedCanvas.beginStroke(x, y);
return;
}
committedCanvas.appendPoint(x, y);
return;
}
if (!started) {
started = true;
startX = point.pressPosition.x;
startY = point.pressPosition.y;
}
const pts = root.sampleShape(root.tool, startX, startY, x, y);
if (pts.length > 0) {
previewCanvas.beginStroke(pts[0][0], pts[0][1], true);
for (let i = 1; i < pts.length; i++)
previewCanvas.appendPoint(pts[i][0], pts[i][1]);
if (withinXWidth && withinYHeight) {
canvas.showHover(x, y);
return Qt.BlankCursor;
} else {
canvas.hideHover();
return undefined;
}
}
}
+28 -12
View File
@@ -5,6 +5,7 @@ import QtQuick
Item {
id: root
required property int border
property color handleBorder: "#3584e4"
property color handleFill: "white"
property real hs: 12
@@ -13,6 +14,8 @@ Item {
required property real selW
required property real selX
required property real selY
required property Rectangle selection
required property string tool
signal boundsChanged(x: real, y: real, w: real, h: real)
signal dragFinished
@@ -29,6 +32,7 @@ Item {
property real accumX: 0
property real accumY: 0
cursorShape: undefined
target: null
onActiveChanged: {
@@ -45,51 +49,63 @@ Item {
}
}
HoverHandler {
cursorShape: {
if (root.tool !== "move")
return undefined;
if (moveHandler.centroid.pressedButtons & Qt.LeftButton)
return Qt.ClosedHandCursor;
else
return Qt.OpenHandCursor;
}
}
Handle {
edge: "tl"
x: -root.hs / 2
y: -root.hs / 2
x: -(root.hs + root.border) / 2
y: -(root.hs + root.border) / 2
}
Handle {
edge: "t"
x: root.width / 2 - root.hs / 2
y: -root.hs / 2
y: -(root.hs + root.border) / 2
}
Handle {
edge: "tr"
x: root.width - root.hs / 2
y: -root.hs / 2
x: root.width - root.hs / 2 + root.border / 2
y: -(root.hs + root.border) / 2
}
Handle {
edge: "r"
x: root.width - root.hs / 2
x: root.width - root.hs / 2 + root.border / 2
y: root.height / 2 - root.hs / 2
}
Handle {
edge: "br"
x: root.width - root.hs / 2
y: root.height - root.hs / 2
x: root.width - root.hs / 2 + root.border / 2
y: root.height - root.hs / 2 + root.border / 2
}
Handle {
edge: "b"
x: root.width / 2 - root.hs / 2
y: root.height - root.hs / 2
y: root.height - root.hs / 2 + root.border / 2
}
Handle {
edge: "bl"
x: -root.hs / 2
y: root.height - root.hs / 2
x: -(root.hs + root.border) / 2
y: root.height - root.hs / 2 + root.border / 2
}
Handle {
edge: "l"
x: -root.hs / 2
x: -(root.hs + root.border) / 2
y: root.height / 2 - root.hs / 2
}
@@ -1,6 +1,7 @@
#include "strokecanvasrenderer.hpp"
#include "strokecanvasitem.hpp"
#include <qcolor.h>
#include <QSet>
namespace ZShell::internal {
@@ -148,14 +149,27 @@ void StrokeCanvasRenderer::synchronizeData(QCanvasPainterItem* item) {
m_penColor = canvas->m_penColor;
m_penWidth = canvas->m_penWidth;
while (m_strokes.size() < canvas->m_strokes.size())
m_strokes.append(canvas->m_strokes[m_strokes.size()]);
// Detect removed strokes: present in renderer but no longer in canvas
QSet<int> canvasGroupIds;
for (const auto& stroke : canvas->m_strokes)
canvasGroupIds.insert(stroke.groupId);
if (canvas->m_strokes.isEmpty() && !m_strokes.isEmpty()) {
for (const auto& stroke : m_strokes)
if (stroke.groupId >= 0)
m_pendingGroupRemovals.append(stroke.groupId);
m_strokes.clear();
for (int i = m_strokes.size() - 1; i >= 0; --i) {
if (!canvasGroupIds.contains(m_strokes[i].groupId)) {
if (m_strokes[i].groupId >= 0)
m_pendingGroupRemovals.append(m_strokes[i].groupId);
m_strokes.remove(i);
}
}
// Detect added strokes: present in canvas but not yet in renderer
QSet<int> rendererGroupIds;
for (const auto& stroke : m_strokes)
rendererGroupIds.insert(stroke.groupId);
for (const auto& stroke : canvas->m_strokes) {
if (!rendererGroupIds.contains(stroke.groupId))
m_strokes.append(stroke);
}
m_currentStroke = canvas->m_currentStroke;