feat: drawing popout now clips strokes again + add redo + add undo and redo keybinds
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Python / verify (pull_request) Failing after 1m41s
Rust / build (pull_request) Successful in 1m15s
Rust / fmt (pull_request) Successful in 42s
Rust / clippy (pull_request) Successful in 1m6s
C++ / fmt (pull_request) Failing after 6s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 10s
JS/TS / fmt (pull_request) Successful in 9s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 28s
Python / verify (pull_request) Failing after 1m41s
Rust / build (pull_request) Successful in 1m15s
Rust / fmt (pull_request) Successful in 42s
Rust / clippy (pull_request) Successful in 1m6s
This commit is contained in:
@@ -4,12 +4,16 @@ import Quickshell
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import ZShell.Internal
|
import ZShell.Internal
|
||||||
import qs.Components
|
import qs.Components
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
readonly property alias content: contentLoader.item
|
readonly property alias content: contentLoader.item
|
||||||
|
|
||||||
required property ScreenState screenState
|
required property ScreenState screenState
|
||||||
|
property rect holeRect
|
||||||
|
property real holeRadius: 0
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: contentLoader
|
id: contentLoader
|
||||||
@@ -18,6 +22,9 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
sourceComponent: StrokeCanvas {
|
sourceComponent: StrokeCanvas {
|
||||||
|
anchors.fill: parent
|
||||||
|
hole: root.holeRect
|
||||||
|
holeRadius: root.holeRadius
|
||||||
penColor: root.screenState.penColor
|
penColor: root.screenState.penColor
|
||||||
penWidth: root.screenState.penWidth
|
penWidth: root.screenState.penWidth
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import ZShell.Config
|
||||||
|
import qs.Components
|
||||||
|
|
||||||
|
CustomMouseArea {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property real borderThickness
|
||||||
|
required property Drawing drawing
|
||||||
|
required property Panels panels
|
||||||
|
required property ScreenState screenState
|
||||||
|
required property EdgeGeometry geometry
|
||||||
|
|
||||||
|
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
||||||
|
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withinPanelHeight(panel: Item, x: real, y: real): bool {
|
||||||
|
const panelY = panel.y + geometry.insetTop(borderThickness);
|
||||||
|
return y >= panelY && y <= panelY + panel.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
cursorShape: Qt.BlankCursor
|
||||||
|
enabled: z > 0
|
||||||
|
hoverEnabled: true
|
||||||
|
focus: root.screenState.isDrawing
|
||||||
|
z: 2
|
||||||
|
|
||||||
|
Binding {
|
||||||
|
target: root.drawing.content
|
||||||
|
property: "hoverPoint"
|
||||||
|
value: Qt.point(root.mouseX, root.mouseY)
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: e => {
|
||||||
|
const mod = e.modifiers;
|
||||||
|
const key = e.key;
|
||||||
|
|
||||||
|
if ((key == Qt.Key_Z) && (mod & Qt.ControlModifier) && (mod & Qt.ShiftModifier)) {
|
||||||
|
root.drawing.content.redo();
|
||||||
|
e.accepted = true;
|
||||||
|
} else if ((key == Qt.Key_Z) && (mod & Qt.ControlModifier)) {
|
||||||
|
root.drawing.content.undoLastGroup();
|
||||||
|
e.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPositionChanged: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
|
||||||
|
if (!drawing.content.hoverVisible)
|
||||||
|
drawing.content.showHover();
|
||||||
|
|
||||||
|
if (event.buttons & Qt.LeftButton) {
|
||||||
|
drawing.content.appendPoint(x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(event.buttons & Qt.LeftButton) && inLeftPanel(panels.drawing, x, y)) {
|
||||||
|
z = -2;
|
||||||
|
panels.drawing.expand();
|
||||||
|
drawing.content.hideHover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPressed: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
|
||||||
|
panels.drawing.collapse();
|
||||||
|
|
||||||
|
if (event.buttons & Qt.LeftButton) {
|
||||||
|
panels.drawing.collapse();
|
||||||
|
drawing.content.beginStroke(x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.button & Qt.RightButton) {
|
||||||
|
drawing.content.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onReleased: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
|
||||||
|
if (event.button & Qt.LeftButton) {
|
||||||
|
drawing.content.endStroke();
|
||||||
|
drawing.content.showHover();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+121
-198
@@ -1,25 +1,33 @@
|
|||||||
import Quickshell
|
import Quickshell
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import ZShell.Config
|
import ZShell.Config
|
||||||
|
import ZShell.Internal
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import qs.Modules.Bar.Popouts
|
import qs.Modules.Bar.Popouts
|
||||||
|
|
||||||
Item {
|
CustomMouseArea {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
required property Drawing drawing
|
||||||
|
required property DrawingInput input
|
||||||
|
|
||||||
required property Item bar
|
required property Item bar
|
||||||
required property real borderThickness
|
required property real borderThickness
|
||||||
property bool dashboardShortcutActive
|
|
||||||
required property Drawing drawing
|
|
||||||
required property EdgeGeometry geometry
|
required property EdgeGeometry geometry
|
||||||
property bool osdShortcutActive
|
|
||||||
required property Panels panels
|
required property Panels panels
|
||||||
required property Wrapper popouts
|
required property Wrapper popouts
|
||||||
|
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
|
required property ScreenState screenState
|
||||||
|
|
||||||
|
property bool dashboardShortcutActive
|
||||||
|
property point dragStart
|
||||||
|
property bool osdShortcutActive
|
||||||
|
property bool setInitialPoint: false
|
||||||
property bool singleGestureTriggered: false
|
property bool singleGestureTriggered: false
|
||||||
property bool utilitiesShortcutActive
|
property bool utilitiesShortcutActive
|
||||||
required property ScreenState screenState
|
|
||||||
|
|
||||||
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
||||||
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
|
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
|
||||||
@@ -56,124 +64,116 @@ Item {
|
|||||||
return x >= panelX && x <= panelX + panel.width;
|
return x >= panelX && x <= panelX + panel.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
DragHandler {
|
onContainsMouseChanged: {
|
||||||
id: singleHandler
|
if (!containsMouse) {
|
||||||
|
if (!osdShortcutActive) {
|
||||||
cursorShape: (active && centroid.pressPosition.y < root.bar.implicitHeight) ? Qt.ClosedHandCursor : undefined
|
screenState.osd = false;
|
||||||
dragThreshold: 0
|
panels.osd.hovered = false;
|
||||||
enabled: !root.screenState.isDrawing
|
|
||||||
grabPermissions: PointerHandler.CanTakeOverFromHandlersOfSameType | PointerHandler.ApprovesTakeOverByAnything
|
|
||||||
maximumPointCount: 1
|
|
||||||
minimumPointCount: 1
|
|
||||||
target: null
|
|
||||||
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active)
|
|
||||||
root.singleGestureTriggered = false;
|
|
||||||
}
|
|
||||||
onCentroidChanged: {
|
|
||||||
const x = centroid.position.x;
|
|
||||||
const y = centroid.position.y;
|
|
||||||
const pressX = centroid.pressPosition.x;
|
|
||||||
const pressY = centroid.pressPosition.y;
|
|
||||||
const dragX = x - pressX;
|
|
||||||
const dragY = y - pressY;
|
|
||||||
|
|
||||||
if (pressY >= root.screen.height - Config.bar.border && pressX > root.screen.width / 5 && dragY < -Config.launcher.dragThreshold)
|
|
||||||
root.screenState.launcher = true;
|
|
||||||
|
|
||||||
if (root.singleGestureTriggered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, pressX, pressY);
|
|
||||||
|
|
||||||
if (inDashboard && root.withinPanelWidth(root.panels.dashboardWrapper, x, y)) {
|
|
||||||
if (dragY > Config.dashboard.dragThreshold) {
|
|
||||||
root.screenState.dashboard = true;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
} else if (dragY < -Config.dashboard.dragThreshold) {
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.geometry.insetTop(root.borderThickness) > pressY && (Config.dashboard.showOnHover || !inDashboard)) {
|
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
|
||||||
if (dragY > 20) {
|
popouts.hasCurrent = false;
|
||||||
root.screenState.settings = true;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
} else if (dragY < -20) {
|
|
||||||
root.screenState.settings = false;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pressY > root.screen.height - root.geometry.insetBottom(root.borderThickness) && pressX < root.screen.width / 5 && dragY < -50)
|
if (Config.bar.autoHide || bar.fullscreen)
|
||||||
root.screenState.clipboard = true;
|
bar.isHovered = false;
|
||||||
|
|
||||||
if (!Config.dock.hoverToReveal && pressY > root.screen.height - root.geometry.insetTop(root.borderThickness) && pressX > root.screen.width / 5 && !root.screenState.launcher)
|
if (screenState.dashboard && Config.dashboard.showOnHover)
|
||||||
if (dragY < -10) {
|
screenState.dashboard = false;
|
||||||
root.screenState.dock = true;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressX > root.screen.width - Config.bar.border && pressY < (root.screen.height / 2) && dragX < -Config.sidebar.dragThreshold) {
|
|
||||||
root.screenState.sidebar = true;
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressX >= root.screen.width - Config.bar.border && pressY > (root.screen.height / 2) && dragX < -20) {
|
|
||||||
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressX <= Config.bar.border && dragX > 20) {
|
|
||||||
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
|
|
||||||
root.singleGestureTriggered = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onPositionChanged: event => {
|
||||||
|
const x = event.x;
|
||||||
|
const y = event.y;
|
||||||
|
const dragX = x - dragStart.x;
|
||||||
|
const dragY = y - dragStart.y;
|
||||||
|
|
||||||
PointHandler {
|
if (screenState.isDrawing && !inLeftPanel(panels.drawing, x, y)) {
|
||||||
id: drawingHandler
|
input.z = 2;
|
||||||
|
panels.drawing.collapse();
|
||||||
|
}
|
||||||
|
|
||||||
property bool setInitialPoint: false
|
if (root.bar.fullscreen && !root.bar.isHovered)
|
||||||
|
unhideTimer.start();
|
||||||
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
|
||||||
enabled: root.screenState.isDrawing && (!root.inLeftPanel(root.panels.drawing, hoverHandler.point.position.x, hoverHandler.point.position.y) || !root.panels.drawing.expanded)
|
screenState.launcher = true;
|
||||||
|
|
||||||
onActiveChanged: {
|
const entryAt = bar.entryAt(geometry.axisPos(x, y));
|
||||||
if (!active) {
|
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow");
|
||||||
setInitialPoint = false;
|
|
||||||
root.drawing.content.endStroke();
|
if (!dashboardShortcutActive) {
|
||||||
} else {
|
screenState.dashboard = showDashboard;
|
||||||
root.panels.drawing.collapse();
|
} else if (showDashboard) {
|
||||||
|
dashboardShortcutActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
|
||||||
|
if (dragY > Config.dashboard.dragThreshold)
|
||||||
|
screenState.dashboard = true;
|
||||||
|
else if (dragY < -Config.dashboard.dragThreshold)
|
||||||
|
screenState.dashboard = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
|
||||||
|
if (dragY > 20) {
|
||||||
|
screenState.settings = true;
|
||||||
|
} else if (dragY < -20) {
|
||||||
|
screenState.settings = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPointChanged: {
|
|
||||||
if (!active)
|
|
||||||
return;
|
|
||||||
const x = point.position.x;
|
|
||||||
const y = point.position.y;
|
|
||||||
const origX = point.pressPosition.x;
|
|
||||||
const origY = point.pressPosition.y;
|
|
||||||
|
|
||||||
if (point.pressedButtons & Qt.RightButton) {
|
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
|
||||||
root.drawing.content.clear();
|
screenState.clipboard = true;
|
||||||
return;
|
|
||||||
|
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
|
||||||
|
if (dragY < -10)
|
||||||
|
screenState.dock = true;
|
||||||
|
|
||||||
|
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
|
||||||
|
screenState.sidebar = true;
|
||||||
|
|
||||||
|
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
|
||||||
|
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
|
||||||
|
|
||||||
|
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
|
||||||
|
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
|
||||||
|
|
||||||
|
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
|
||||||
|
bar.isHovered = true;
|
||||||
|
|
||||||
|
if (panels.sidebar.offsetScale === 1) {
|
||||||
|
const showOsd = inRightPanel(panels.osdWrapper, x, y);
|
||||||
|
|
||||||
|
if (showOsd) {
|
||||||
|
osdShortcutActive = false;
|
||||||
|
panels.osd.hovered = true;
|
||||||
}
|
}
|
||||||
if (x === 0 && y === 0 && origX === 0 && origY === 0)
|
} else {
|
||||||
return;
|
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
|
||||||
|
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
|
||||||
|
|
||||||
if (!setInitialPoint) {
|
if (!osdShortcutActive) {
|
||||||
setInitialPoint = true;
|
screenState.osd = showOsd;
|
||||||
root.drawing.content.beginStroke(origX, origY);
|
panels.osd.hovered = showOsd;
|
||||||
return;
|
} else if (showOsd) {
|
||||||
|
osdShortcutActive = false;
|
||||||
|
panels.osd.hovered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.drawing.content.appendPoint(x, y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
|
||||||
|
screenState.dock = true;
|
||||||
|
|
||||||
|
if (geometry.barContains(x, y))
|
||||||
|
bar.checkPopout(geometry.axisPos(x, y));
|
||||||
|
}
|
||||||
|
onPressed: event => {
|
||||||
|
dragStart = Qt.point(event.x, event.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
@@ -184,98 +184,20 @@ Item {
|
|||||||
running: false
|
running: false
|
||||||
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (hoverHandler.hovered)
|
if (root.containsMouse)
|
||||||
root.bar.isHovered = true;
|
root.bar.isHovered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HoverHandler {
|
|
||||||
id: hoverHandler
|
|
||||||
|
|
||||||
cursorShape: root.screenState.isDrawing && !root.inLeftPanel(root.panels.drawing, point.position.x, point.position.y) ? Qt.BlankCursor : undefined
|
|
||||||
|
|
||||||
onHoveredChanged: {
|
|
||||||
if (!hovered) {
|
|
||||||
if (!root.osdShortcutActive) {
|
|
||||||
root.screenState.osd = false;
|
|
||||||
root.panels.osd.hovered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.bar.autoHide || root.bar.fullscreen)
|
|
||||||
root.bar.isHovered = false;
|
|
||||||
|
|
||||||
if (root.screenState.dashboard && Config.dashboard.showOnHover)
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPointChanged: {
|
|
||||||
const x = point.position.x;
|
|
||||||
const y = point.position.y;
|
|
||||||
|
|
||||||
if (root.screenState.isDrawing) {
|
|
||||||
if (root.inLeftPanel(root.panels.drawing, x, y) && !(drawingHandler.point.pressedButtons & Qt.LeftButton)) {
|
|
||||||
root.panels.drawing.expand();
|
|
||||||
root.drawing.content.hideHover();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.drawing.content.showHover(x, y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.screenState.bar && Config.bar.autoHide && root.geometry.barContains(x, y, true))
|
|
||||||
root.bar.isHovered = true;
|
|
||||||
|
|
||||||
if (root.bar.fullscreen && !root.bar.isHovered)
|
|
||||||
unhideTimer.start();
|
|
||||||
|
|
||||||
if (root.panels.sidebar.offsetScale === 1) {
|
|
||||||
const showOsd = root.inRightPanel(root.panels.osdWrapper, x, y);
|
|
||||||
|
|
||||||
if (showOsd) {
|
|
||||||
root.osdShortcutActive = false;
|
|
||||||
root.panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const outOfSidebar = x < root.width - root.panels.sidebar.width * (1 - root.panels.sidebar.offsetScale);
|
|
||||||
const showOsd = outOfSidebar && root.inRightPanel(root.panels.osdWrapper, x, y);
|
|
||||||
|
|
||||||
if (!root.osdShortcutActive) {
|
|
||||||
root.screenState.osd = showOsd;
|
|
||||||
root.panels.osd.hovered = showOsd;
|
|
||||||
} else if (showOsd) {
|
|
||||||
root.osdShortcutActive = false;
|
|
||||||
root.panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.dock.enable && Config.dock.hoverToReveal && !root.screenState.dock && !root.screenState.launcher && root.inBottomPanel(root.panels.dock, x, y))
|
|
||||||
root.screenState.dock = true;
|
|
||||||
|
|
||||||
const inDashboard = root.inTopPanel(root.panels.dashboardWrapper, x, y);
|
|
||||||
|
|
||||||
if (Config.dashboard.showOnHover)
|
|
||||||
root.screenState.dashboard = inDashboard;
|
|
||||||
|
|
||||||
if (root.geometry.barContains(x, y))
|
|
||||||
root.bar.checkPopout(root.geometry.axisPos(x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
function onDashboardChanged() {
|
function onDashboardChanged(): void {
|
||||||
if (root.screenState.dashboard) {
|
if (root.screenState.dashboard) {
|
||||||
const inDashboardArea = root.inTopPanel(root.panels.dashboard, root.mouseX, root.mouseY);
|
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
|
||||||
if (!inDashboardArea) {
|
if (!inDashboardArea) {
|
||||||
root.dashboardShortcutActive = true;
|
root.dashboardShortcutActive = true;
|
||||||
}
|
}
|
||||||
if (root.panels.launcher.x + root.panels.launcher.width > root.panels.dashboardWrapper.x)
|
|
||||||
root.screenState.launcher = false;
|
|
||||||
|
|
||||||
|
root.screenState.launcher = false;
|
||||||
root.screenState.settings = false;
|
root.screenState.settings = false;
|
||||||
root.screenState.sidebar = false;
|
root.screenState.sidebar = false;
|
||||||
root.screenState.resources = false;
|
root.screenState.resources = false;
|
||||||
@@ -285,12 +207,15 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIsDrawingChanged() {
|
function onIsDrawingChanged(): void {
|
||||||
if (!root.screenState.isDrawing)
|
if (root.screenState.isDrawing) {
|
||||||
root.drawing.clear();
|
root.drawing.content.showHover();
|
||||||
|
} else {
|
||||||
|
root.drawing.content.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLauncherChanged() {
|
function onLauncherChanged(): void {
|
||||||
if (!root.screenState.launcher) {
|
if (!root.screenState.launcher) {
|
||||||
root.dashboardShortcutActive = false;
|
root.dashboardShortcutActive = false;
|
||||||
root.osdShortcutActive = false;
|
root.osdShortcutActive = false;
|
||||||
@@ -305,16 +230,13 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (root.screenState.launcher) {
|
if (root.screenState.launcher) {
|
||||||
if (root.panels.dashboardWrapper.x < root.panels.launcher.x + root.panels.launcher.width) {
|
root.screenState.dashboard = false;
|
||||||
root.screenState.dashboard = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.screenState.dock = false;
|
root.screenState.dock = false;
|
||||||
root.screenState.settings = false;
|
root.screenState.settings = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onOsdChanged() {
|
function onOsdChanged(): void {
|
||||||
if (root.screenState.osd) {
|
if (root.screenState.osd) {
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
||||||
if (!inOsdArea) {
|
if (!inOsdArea) {
|
||||||
@@ -325,7 +247,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onResourcesChanged() {
|
function onResourcesChanged(): void {
|
||||||
if (root.screenState.resources) {
|
if (root.screenState.resources) {
|
||||||
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
|
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
|
||||||
root.popouts.hasCurrent = false;
|
root.popouts.hasCurrent = false;
|
||||||
@@ -335,7 +257,7 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSettingsChanged() {
|
function onSettingsChanged(): void {
|
||||||
if (root.screenState.settings) {
|
if (root.screenState.settings) {
|
||||||
root.screenState.resources = false;
|
root.screenState.resources = false;
|
||||||
root.screenState.dashboard = false;
|
root.screenState.dashboard = false;
|
||||||
@@ -345,9 +267,10 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSidebarChanged() {
|
function onSidebarChanged(): void {
|
||||||
if (root.screenState.sidebar) {
|
if (root.screenState.sidebar) {
|
||||||
root.screenState.dashboard = false;
|
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
|
||||||
|
root.screenState.dashboard = false;
|
||||||
root.screenState.settings = false;
|
root.screenState.settings = false;
|
||||||
root.popouts.hasCurrent = false;
|
root.popouts.hasCurrent = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import ZShell.Config
|
|
||||||
import ZShell.Internal
|
|
||||||
import qs.Components
|
|
||||||
import qs.Helpers
|
|
||||||
import qs.Modules.Bar.Popouts
|
|
||||||
|
|
||||||
CustomMouseArea {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
required property Item bar
|
|
||||||
required property real borderThickness
|
|
||||||
property bool dashboardShortcutActive
|
|
||||||
property point dragStart
|
|
||||||
required property Drawing drawing
|
|
||||||
required property EdgeGeometry geometry
|
|
||||||
property bool osdShortcutActive
|
|
||||||
required property Panels panels
|
|
||||||
required property Wrapper popouts
|
|
||||||
required property ShellScreen screen
|
|
||||||
required property ScreenState screenState
|
|
||||||
property bool setInitialPoint: false
|
|
||||||
property bool singleGestureTriggered: false
|
|
||||||
property bool utilitiesShortcutActive
|
|
||||||
|
|
||||||
function inBottomPanel(panel: Item, x: real, y: real): bool {
|
|
||||||
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
|
|
||||||
return y > root.height - (panelHeight + geometry.insetBottom(borderThickness)) && withinPanelWidth(panel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inLeftPanel(panel: Item, x: real, y: real): bool {
|
|
||||||
return x < panel.x + panel.width + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inPopoutArea(x: real, y: real): bool {
|
|
||||||
const panel = panels.popoutsWrapper;
|
|
||||||
if (!geometry.horizontal)
|
|
||||||
return inLeftPanel(panel, x, y);
|
|
||||||
return withinPanelWidth(panel, x, y) && withinPanelHeight(panel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inRightPanel(panel: Item, x: real, y: real): bool {
|
|
||||||
return x > panel.x + geometry.insetLeft(borderThickness) && withinPanelHeight(panel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function inTopPanel(panel: Item, x: real, y: real): bool {
|
|
||||||
const panelHeight = panel.height * (1 - (panel.offsetScale ?? 0));
|
|
||||||
return y < geometry.insetTop(borderThickness) + panelHeight && withinPanelWidth(panel, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
function withinPanelHeight(panel: Item, x: real, y: real): bool {
|
|
||||||
const panelY = panel.y + geometry.insetTop(borderThickness);
|
|
||||||
return y >= panelY && y <= panelY + panel.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
function withinPanelWidth(panel: Item, x: real, y: real): bool {
|
|
||||||
const panelX = panel.x + geometry.insetLeft(borderThickness);
|
|
||||||
return x >= panelX && x <= panelX + panel.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptedButtons: bar.fullscreen ? Qt.NoButton : Qt.AllButtons
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
|
|
||||||
onContainsMouseChanged: {
|
|
||||||
if (!containsMouse) {
|
|
||||||
if (!osdShortcutActive) {
|
|
||||||
screenState.osd = false;
|
|
||||||
panels.osd.hovered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!popouts.currentName.startsWith("traymenu") || Config.bar.tray.showOnHover) {
|
|
||||||
popouts.hasCurrent = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.bar.autoHide || bar.fullscreen)
|
|
||||||
bar.isHovered = false;
|
|
||||||
|
|
||||||
if (screenState.dashboard && Config.dashboard.showOnHover)
|
|
||||||
screenState.dashboard = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPositionChanged: event => {
|
|
||||||
const x = event.x;
|
|
||||||
const y = event.y;
|
|
||||||
const dragX = x - dragStart.x;
|
|
||||||
const dragY = y - dragStart.y;
|
|
||||||
|
|
||||||
if (screenState.isDrawing) {
|
|
||||||
if (pressed) {
|
|
||||||
drawing.content.appendPoint(x, y);
|
|
||||||
} else {
|
|
||||||
if (inLeftPanel(panels.drawing, x, y)) {
|
|
||||||
if (!(pressedButtons & Qt.LeftButton)) {
|
|
||||||
panels.drawing.expand();
|
|
||||||
drawing.content.hideHover();
|
|
||||||
Cursor.clearOverride();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Cursor.setOverride(Qt.BlankCursor);
|
|
||||||
drawing.content.showHover(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (drawing.content.hoverVisible)
|
|
||||||
drawing.content.showHover(x, y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.bar.fullscreen && !root.bar.isHovered)
|
|
||||||
unhideTimer.start();
|
|
||||||
|
|
||||||
if (pressed && dragStart.y >= screen.height - Config.bar.border && dragStart.x > screen.width / 5 && dragY < -Config.launcher.dragThreshold)
|
|
||||||
screenState.launcher = true;
|
|
||||||
|
|
||||||
const entryAt = bar.entryAt(geometry.axisPos(x, y));
|
|
||||||
const showDashboard = Config.dashboard.showOnHover && inTopPanel(panels.dashboardWrapper, x, y) && (!entryAt || entryAt === "activeWindow");
|
|
||||||
|
|
||||||
if (!dashboardShortcutActive) {
|
|
||||||
screenState.dashboard = showDashboard;
|
|
||||||
} else if (showDashboard) {
|
|
||||||
dashboardShortcutActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && withinPanelWidth(panels.dashboardWrapper, x, y)) {
|
|
||||||
if (dragY > Config.dashboard.dragThreshold)
|
|
||||||
screenState.dashboard = true;
|
|
||||||
else if (dragY < -Config.dashboard.dragThreshold)
|
|
||||||
screenState.dashboard = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressed && geometry.insetTop(borderThickness) > dragStart.y && !inTopPanel(panels.dashboardWrapper, dragStart.x, dragStart.y)) {
|
|
||||||
if (dragY > 20) {
|
|
||||||
screenState.settings = true;
|
|
||||||
} else if (dragY < -20) {
|
|
||||||
screenState.settings = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressed && dragStart.y > screen.height - geometry.insetBottom(borderThickness) && dragStart.x < screen.width / 5 && dragY < -50)
|
|
||||||
screenState.clipboard = true;
|
|
||||||
|
|
||||||
if (pressed && !Config.dock.hoverToReveal && dragStart.y > screen.height - geometry.insetTop(borderThickness) && dragStart.x > screen.width / 5 && !screenState.launcher)
|
|
||||||
if (dragY < -10)
|
|
||||||
screenState.dock = true;
|
|
||||||
|
|
||||||
if (pressed && dragStart.x > screen.width - Config.bar.border && dragStart.y < (screen.height / 2) && dragX < -Config.sidebar.dragThreshold)
|
|
||||||
screenState.sidebar = true;
|
|
||||||
|
|
||||||
if (pressed && dragStart.x >= screen.width - Config.bar.border && dragStart.y > (screen.height / 2) && dragX < -20)
|
|
||||||
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r+1', on_current_monitor = true })`);
|
|
||||||
|
|
||||||
if (pressed && dragStart.x <= Config.bar.border && dragX > 20)
|
|
||||||
Hypr.dispatch(`hl.dsp.focus({ workspace = 'r-1', on_current_monitor = true })`);
|
|
||||||
|
|
||||||
if (!screenState.bar && Config.bar.autoHide && geometry.barContains(x, y, true))
|
|
||||||
bar.isHovered = true;
|
|
||||||
|
|
||||||
if (panels.sidebar.offsetScale === 1) {
|
|
||||||
const showOsd = inRightPanel(panels.osdWrapper, x, y);
|
|
||||||
|
|
||||||
if (showOsd) {
|
|
||||||
osdShortcutActive = false;
|
|
||||||
panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const outOfSidebar = x < width - panels.sidebar.width * (1 - panels.sidebar.offsetScale);
|
|
||||||
const showOsd = outOfSidebar && inRightPanel(panels.osdWrapper, x, y);
|
|
||||||
|
|
||||||
if (!osdShortcutActive) {
|
|
||||||
screenState.osd = showOsd;
|
|
||||||
panels.osd.hovered = showOsd;
|
|
||||||
} else if (showOsd) {
|
|
||||||
osdShortcutActive = false;
|
|
||||||
panels.osd.hovered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.dock.enable && Config.dock.hoverToReveal && !screenState.dock && !screenState.launcher && inBottomPanel(panels.dock, x, y))
|
|
||||||
screenState.dock = true;
|
|
||||||
|
|
||||||
if (geometry.barContains(x, y))
|
|
||||||
bar.checkPopout(geometry.axisPos(x, y));
|
|
||||||
}
|
|
||||||
onPressed: event => {
|
|
||||||
dragStart = Qt.point(event.x, event.y);
|
|
||||||
|
|
||||||
if (screenState.isDrawing) {
|
|
||||||
if (event.button & Qt.RightButton) {
|
|
||||||
drawing.content.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
|
|
||||||
drawing.content.beginStroke(event.x, event.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressedChanged: {
|
|
||||||
if (!pressed) {
|
|
||||||
if (screenState.isDrawing) {
|
|
||||||
setInitialPoint = false;
|
|
||||||
drawing.content.endStroke();
|
|
||||||
if (!inLeftPanel(panels.drawing, mouseX, mouseY))
|
|
||||||
drawing.content.showHover(mouseX, mouseY);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (screenState.isDrawing) {
|
|
||||||
panels.drawing.collapse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: unhideTimer
|
|
||||||
|
|
||||||
interval: Config.bar.revealDelay
|
|
||||||
repeat: false
|
|
||||||
running: false
|
|
||||||
|
|
||||||
onTriggered: {
|
|
||||||
if (root.containsMouse)
|
|
||||||
root.bar.isHovered = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onDashboardChanged(): void {
|
|
||||||
if (root.screenState.dashboard) {
|
|
||||||
const inDashboardArea = root.inTopPanel(root.panels.dashboardWrapper, root.mouseX, root.mouseY);
|
|
||||||
if (!inDashboardArea) {
|
|
||||||
root.dashboardShortcutActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.screenState.launcher = false;
|
|
||||||
root.screenState.settings = false;
|
|
||||||
root.screenState.sidebar = false;
|
|
||||||
root.screenState.resources = false;
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
} else {
|
|
||||||
root.dashboardShortcutActive = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onIsDrawingChanged(): void {
|
|
||||||
if (root.screenState.isDrawing) {
|
|
||||||
Cursor.setOverride(Qt.BlankCursor);
|
|
||||||
} else {
|
|
||||||
Cursor.clearOverride();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onLauncherChanged(): void {
|
|
||||||
if (!root.screenState.launcher) {
|
|
||||||
root.dashboardShortcutActive = false;
|
|
||||||
root.osdShortcutActive = false;
|
|
||||||
root.utilitiesShortcutActive = false;
|
|
||||||
|
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
|
||||||
|
|
||||||
if (!inOsdArea) {
|
|
||||||
root.screenState.osd = false;
|
|
||||||
root.panels.osd.hovered = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root.screenState.launcher) {
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
root.screenState.dock = false;
|
|
||||||
root.screenState.settings = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOsdChanged(): void {
|
|
||||||
if (root.screenState.osd) {
|
|
||||||
const inOsdArea = root.inRightPanel(root.panels.osd, root.mouseX, root.mouseY);
|
|
||||||
if (!inOsdArea) {
|
|
||||||
root.osdShortcutActive = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.osdShortcutActive = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onResourcesChanged(): void {
|
|
||||||
if (root.screenState.resources) {
|
|
||||||
if (root.popouts.currentName.startsWith("audio") || root.popouts.currentName.startsWith("updates"))
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
|
|
||||||
root.screenState.settings = false;
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSettingsChanged(): void {
|
|
||||||
if (root.screenState.settings) {
|
|
||||||
root.screenState.resources = false;
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
root.screenState.sidebar = false;
|
|
||||||
root.panels.popouts.hasCurrent = false;
|
|
||||||
root.screenState.launcher = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSidebarChanged(): void {
|
|
||||||
if (root.screenState.sidebar) {
|
|
||||||
if (root.panels.dashboardWrapper.x + root.panels.dashboardWrapper.width > root.panels.sidebar.x)
|
|
||||||
root.screenState.dashboard = false;
|
|
||||||
root.screenState.settings = false;
|
|
||||||
root.popouts.hasCurrent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
target: root.screenState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+24
-2
@@ -56,7 +56,7 @@ CustomWindow {
|
|||||||
property color surfaceColor: Colors.tPalette.m3surface
|
property color surfaceColor: Colors.tPalette.m3surface
|
||||||
|
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
WlrLayershell.keyboardFocus: screenState.screenshot ? WlrKeyboardFocus.Exclusive : screenState.launcher || screenState.settings || screenState.sidebar || screenState.isDrawing ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||||
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
|
WlrLayershell.layer: (fsTransitionProg > 0 && Config.general.showOverFullscreen) || (hasSpecialWorkspace && hasFullscreenOnNormalWs) ? WlrLayer.Overlay : WlrLayer.Top
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
|
mask: screenState.isDrawing || screenState.screenshot ? null : (hasFullscreen && !bar.isHovered ? emptyRegion : regions)
|
||||||
@@ -347,12 +347,31 @@ CustomWindow {
|
|||||||
Drawing {
|
Drawing {
|
||||||
id: drawing
|
id: drawing
|
||||||
|
|
||||||
|
readonly property int extraWidth: drawingBg.radius * 2
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
holeRect: Qt.rect(root.borderThickness + panels.drawing.x - extraWidth, panels.drawing.y + bar.implicitHeight, panels.drawing.implicitWidth + extraWidth, panels.drawing.implicitHeight)
|
||||||
|
holeRadius: drawingBg.radius
|
||||||
screenState: root.screenState
|
screenState: root.screenState
|
||||||
z: 2
|
z: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
Interactions2 {
|
LazyLoader {
|
||||||
|
id: lazyLoader
|
||||||
|
|
||||||
|
activeAsync: root.screenState.isDrawing
|
||||||
|
|
||||||
|
component: DrawingInput {
|
||||||
|
borderThickness: root.borderLayoutThickness
|
||||||
|
drawing: drawing
|
||||||
|
panels: panels
|
||||||
|
screenState: root.screenState
|
||||||
|
geometry: geometry
|
||||||
|
parent: root.contentItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Interactions {
|
||||||
id: interactions
|
id: interactions
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -365,6 +384,8 @@ CustomWindow {
|
|||||||
popouts: panels.popouts
|
popouts: panels.popouts
|
||||||
screen: root.screen
|
screen: root.screen
|
||||||
screenState: root.screenState
|
screenState: root.screenState
|
||||||
|
input: lazyLoader.item
|
||||||
|
z: 1
|
||||||
|
|
||||||
Panels {
|
Panels {
|
||||||
id: panels
|
id: panels
|
||||||
@@ -442,6 +463,7 @@ CustomWindow {
|
|||||||
parent: root.contentItem
|
parent: root.contentItem
|
||||||
screen: root.screen
|
screen: root.screen
|
||||||
screenState: root.screenState
|
screenState: root.screenState
|
||||||
|
z: 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ find_package(Qt6 REQUIRED COMPONENTS
|
|||||||
GuiPrivate
|
GuiPrivate
|
||||||
Quick
|
Quick
|
||||||
CanvasPainter
|
CanvasPainter
|
||||||
|
CanvasPainterTools
|
||||||
QuickPrivate
|
QuickPrivate
|
||||||
QuickLayouts
|
QuickLayouts
|
||||||
QuickLayoutsPrivate
|
QuickLayoutsPrivate
|
||||||
|
|||||||
@@ -29,3 +29,9 @@ qml_module(ZShell-internal
|
|||||||
PkgConfig::GL
|
PkgConfig::GL
|
||||||
ZShell-config
|
ZShell-config
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_add_custom_brush_shaders(ZShell-internal "internal_brush_shaders"
|
||||||
|
PREFIX "/"
|
||||||
|
FILES
|
||||||
|
shaders/strokeholemask.frag
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
QC_INCLUDE "customfrag.glsl"
|
||||||
|
|
||||||
|
|
||||||
|
float sdRoundRect(vec2 p, vec2 b, float r) {
|
||||||
|
vec2 q = abs(p) - b + vec2(r);
|
||||||
|
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float keep = 1.0;
|
||||||
|
|
||||||
|
if (data4.x > 0.5) {
|
||||||
|
vec2 p = fragCoord - data1.xy;
|
||||||
|
float feather = max(data3.y, 0.0001);
|
||||||
|
keep = smoothstep(-feather, feather, sdRoundRect(p, data1.zw, data3.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
float a = keep * data2.w * globalAlpha * antialiasingAlpha();
|
||||||
|
fragColor = vec4(data2.rgb * a, a);
|
||||||
|
}
|
||||||
@@ -71,11 +71,23 @@ static QCanvasPath buildStrokePath(
|
|||||||
|
|
||||||
void StrokeCanvasItem::undoLastGroup() {
|
void StrokeCanvasItem::undoLastGroup() {
|
||||||
if (m_strokes.isEmpty()) return;
|
if (m_strokes.isEmpty()) return;
|
||||||
|
|
||||||
const int lastGroup = m_strokes.last().groupId;
|
const int lastGroup = m_strokes.last().groupId;
|
||||||
|
|
||||||
|
QVector<Stroke> group;
|
||||||
for (int i = static_cast<int>(m_strokes.size()) - 1;
|
for (int i = static_cast<int>(m_strokes.size()) - 1;
|
||||||
i >= 0 && m_strokes[i].groupId == lastGroup;
|
i >= 0 && m_strokes[i].groupId == lastGroup;
|
||||||
--i)
|
--i)
|
||||||
m_strokes.remove(i);
|
group.prepend(m_strokes.takeAt(i));
|
||||||
|
|
||||||
|
m_redoStack.append(group);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokeCanvasItem::redo() {
|
||||||
|
if (m_redoStack.isEmpty()) return;
|
||||||
|
|
||||||
|
m_strokes.append(m_redoStack.takeLast());
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,19 +118,14 @@ void StrokeCanvasItem::setHoverPoint(const QPointF& point) {
|
|||||||
emit hoverPointChanged();
|
emit hoverPointChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StrokeCanvasItem::showHover(qreal x, qreal y) {
|
void StrokeCanvasItem::showHover() {
|
||||||
const QPointF newPoint{x, y};
|
if (m_hoverVisible) return;
|
||||||
const bool pointChanged = (m_hoverPoint != newPoint);
|
|
||||||
const bool visibleChanged = !m_hoverVisible;
|
|
||||||
|
|
||||||
|
|
||||||
if (pointChanged || visibleChanged) {
|
m_hoverVisible = true;
|
||||||
m_hoverVisible = true;
|
update();
|
||||||
m_hoverPoint = newPoint;
|
|
||||||
if (pointChanged) emit hoverPointChanged();
|
emit hoverVisibleChanged();
|
||||||
if (visibleChanged) emit hoverVisibleChanged();
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StrokeCanvasItem::hideHover() {
|
void StrokeCanvasItem::hideHover() {
|
||||||
@@ -139,6 +146,24 @@ void StrokeCanvasItem::setPenWidth(qreal width) {
|
|||||||
emit penWidthChanged();
|
emit penWidthChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StrokeCanvasItem::setHole(const QRectF& hole) {
|
||||||
|
if (m_hole == hole) return;
|
||||||
|
|
||||||
|
m_hole = hole;
|
||||||
|
update();
|
||||||
|
|
||||||
|
emit holeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokeCanvasItem::setHoleRadius(qreal radius) {
|
||||||
|
if (qFuzzyCompare(m_holeRadius, radius)) return;
|
||||||
|
|
||||||
|
m_holeRadius = radius;
|
||||||
|
update();
|
||||||
|
|
||||||
|
emit holeRadiusChanged();
|
||||||
|
}
|
||||||
|
|
||||||
void StrokeCanvasItem::beginStroke(qreal x, qreal y, bool polyline) {
|
void StrokeCanvasItem::beginStroke(qreal x, qreal y, bool polyline) {
|
||||||
m_isDrawing = true;
|
m_isDrawing = true;
|
||||||
m_currentStroke.points.clear();
|
m_currentStroke.points.clear();
|
||||||
@@ -199,6 +224,7 @@ void StrokeCanvasItem::endStroke() {
|
|||||||
m_currentStroke.points.clear();
|
m_currentStroke.points.clear();
|
||||||
m_strokes.append(m_currentStroke);
|
m_strokes.append(m_currentStroke);
|
||||||
m_currentStroke = {};
|
m_currentStroke = {};
|
||||||
|
m_redoStack.clear();
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@@ -206,6 +232,7 @@ void StrokeCanvasItem::endStroke() {
|
|||||||
void StrokeCanvasItem::clear() {
|
void StrokeCanvasItem::clear() {
|
||||||
m_strokes.clear();
|
m_strokes.clear();
|
||||||
m_currentStroke = {};
|
m_currentStroke = {};
|
||||||
|
m_redoStack.clear();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <QCanvasPainterItem>
|
#include <QCanvasPainterItem>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QPointF>
|
#include <QPointF>
|
||||||
|
#include <QRectF>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <qcolor.h>
|
#include <qcolor.h>
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
@@ -27,6 +28,10 @@ class StrokeCanvasItem : public QCanvasPainterItem {
|
|||||||
hoverPointChanged)
|
hoverPointChanged)
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
qreal penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
|
qreal penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
|
||||||
|
Q_PROPERTY(QRectF hole READ hole WRITE setHole NOTIFY holeChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal holeRadius READ holeRadius WRITE setHoleRadius NOTIFY
|
||||||
|
holeRadiusChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit StrokeCanvasItem(QQuickItem* parent = nullptr);
|
explicit StrokeCanvasItem(QQuickItem* parent = nullptr);
|
||||||
@@ -37,15 +42,20 @@ class StrokeCanvasItem : public QCanvasPainterItem {
|
|||||||
void setHoverVisible(bool visible);
|
void setHoverVisible(bool visible);
|
||||||
void setHoverPoint(const QPointF& point);
|
void setHoverPoint(const QPointF& point);
|
||||||
|
|
||||||
Q_INVOKABLE void showHover(qreal x, qreal y);
|
Q_INVOKABLE void showHover();
|
||||||
Q_INVOKABLE void hideHover();
|
Q_INVOKABLE void hideHover();
|
||||||
Q_INVOKABLE void undoLastGroup();
|
Q_INVOKABLE void undoLastGroup();
|
||||||
|
Q_INVOKABLE void redo();
|
||||||
|
|
||||||
[[nodiscard]] QColor penColor() const { return m_penColor; }
|
[[nodiscard]] QColor penColor() const { return m_penColor; }
|
||||||
[[nodiscard]] qreal penWidth() const { return m_penWidth; }
|
[[nodiscard]] qreal penWidth() const { return m_penWidth; }
|
||||||
|
[[nodiscard]] QRectF hole() const { return m_hole; }
|
||||||
|
[[nodiscard]] qreal holeRadius() const { return m_holeRadius; }
|
||||||
|
|
||||||
void setPenColor(const QColor& color);
|
void setPenColor(const QColor& color);
|
||||||
void setPenWidth(qreal width);
|
void setPenWidth(qreal width);
|
||||||
|
void setHole(const QRectF& hole);
|
||||||
|
void setHoleRadius(qreal radius);
|
||||||
|
|
||||||
Q_INVOKABLE void clear();
|
Q_INVOKABLE void clear();
|
||||||
|
|
||||||
@@ -56,6 +66,8 @@ class StrokeCanvasItem : public QCanvasPainterItem {
|
|||||||
signals:
|
signals:
|
||||||
void penColorChanged();
|
void penColorChanged();
|
||||||
void penWidthChanged();
|
void penWidthChanged();
|
||||||
|
void holeChanged();
|
||||||
|
void holeRadiusChanged();
|
||||||
void hoverVisibleChanged();
|
void hoverVisibleChanged();
|
||||||
void hoverPointChanged();
|
void hoverPointChanged();
|
||||||
|
|
||||||
@@ -72,9 +84,12 @@ class StrokeCanvasItem : public QCanvasPainterItem {
|
|||||||
qreal m_penWidth = 4.0;
|
qreal m_penWidth = 4.0;
|
||||||
bool m_isDrawing = false;
|
bool m_isDrawing = false;
|
||||||
|
|
||||||
|
QRectF m_hole;
|
||||||
|
qreal m_holeRadius = 0.0;
|
||||||
int m_nextGroupId = 0;
|
int m_nextGroupId = 0;
|
||||||
QVector<Stroke> m_strokes;
|
QVector<Stroke> m_strokes;
|
||||||
Stroke m_currentStroke;
|
Stroke m_currentStroke;
|
||||||
|
QVector<QVector<Stroke>> m_redoStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace ZShell::internal
|
}; // namespace ZShell::internal
|
||||||
|
|||||||
@@ -1,11 +1,98 @@
|
|||||||
#include "strokecanvasrenderer.hpp"
|
#include "strokecanvasrenderer.hpp"
|
||||||
#include "strokecanvasitem.hpp"
|
#include "strokecanvasitem.hpp"
|
||||||
|
#include <QCanvasPainter>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QVector4D>
|
||||||
#include <qcolor.h>
|
#include <qcolor.h>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
static void drawStroke(
|
using Qt::StringLiterals::operator""_s;
|
||||||
|
|
||||||
|
static const QString kStrokeHoleShader = u":/shaders/strokeholemask.frag.qsb"_s;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct HoleUniforms {
|
||||||
|
QVector4D data1; // center.xy, halfSize.xy
|
||||||
|
QVector4D data3; // x: radius, y: feather in logical px
|
||||||
|
QVector4D data4; // x: hole enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
static HoleUniforms holeUniforms(
|
||||||
|
const QRectF& hole, qreal radius, qreal devicePixelRatio) {
|
||||||
|
const bool active = hole.isValid() && hole.width() > 0 && hole.height() > 0;
|
||||||
|
|
||||||
|
if (!active) return {QVector4D(), QVector4D(), QVector4D()};
|
||||||
|
|
||||||
|
const QPointF center = hole.center();
|
||||||
|
const QPointF half(hole.width() * 0.5, hole.height() * 0.5);
|
||||||
|
const qreal maxRadius = qMin(half.x(), half.y());
|
||||||
|
|
||||||
|
return {
|
||||||
|
QVector4D(
|
||||||
|
static_cast<float>(center.x()),
|
||||||
|
static_cast<float>(center.y()),
|
||||||
|
static_cast<float>(half.x()),
|
||||||
|
static_cast<float>(half.y())),
|
||||||
|
QVector4D(
|
||||||
|
static_cast<float>(qMin(radius, maxRadius)),
|
||||||
|
static_cast<float>(1.0 / devicePixelRatio),
|
||||||
|
0.0f,
|
||||||
|
0.0f),
|
||||||
|
QVector4D(1.0, 0.0, 0.0, 0.0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void applyHole(QCanvasCustomBrush& brush, const HoleUniforms& u) {
|
||||||
|
brush.setData1(u.data1);
|
||||||
|
brush.setData3(u.data3);
|
||||||
|
brush.setData4(u.data4);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
void StrokeCanvasRenderer::updateBrushHole() {
|
||||||
|
const HoleUniforms uniforms =
|
||||||
|
holeUniforms(m_hole, m_holeRadius, m_devicePixelRatio);
|
||||||
|
|
||||||
|
if (uniforms.data1 == m_lastHoleData1 &&
|
||||||
|
uniforms.data3 == m_lastHoleData3 && uniforms.data4 == m_lastHoleData4)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_lastHoleData1 = uniforms.data1;
|
||||||
|
m_lastHoleData3 = uniforms.data3;
|
||||||
|
m_lastHoleData4 = uniforms.data4;
|
||||||
|
++m_holeToken;
|
||||||
|
|
||||||
|
for (auto& entry : m_brushes) {
|
||||||
|
if (entry.holeToken == m_holeToken) continue;
|
||||||
|
|
||||||
|
applyHole(entry.brush, uniforms);
|
||||||
|
entry.holeToken = m_holeToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCanvasCustomBrush& StrokeCanvasRenderer::brushFor(const QColor& color) {
|
||||||
|
const QRgb key = color.rgba();
|
||||||
|
|
||||||
|
if (auto it = m_brushes.find(key); it != m_brushes.end()) return it->brush;
|
||||||
|
|
||||||
|
const HoleUniforms uniforms =
|
||||||
|
holeUniforms(m_hole, m_holeRadius, m_devicePixelRatio);
|
||||||
|
|
||||||
|
BrushEntry entry;
|
||||||
|
entry.brush.setFragmentShader(kStrokeHoleShader);
|
||||||
|
applyHole(entry.brush, uniforms);
|
||||||
|
entry.brush.setData2(
|
||||||
|
QVector4D(color.redF(), color.greenF(), color.blueF(), color.alphaF()));
|
||||||
|
entry.holeToken = m_holeToken;
|
||||||
|
|
||||||
|
return m_brushes.insert(key, entry)->brush;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StrokeCanvasRenderer::drawStroke(
|
||||||
QCanvasPainter* painter,
|
QCanvasPainter* painter,
|
||||||
const QVector<QPointF>& points,
|
const QVector<QPointF>& points,
|
||||||
const QColor& color,
|
const QColor& color,
|
||||||
@@ -13,8 +100,10 @@ static void drawStroke(
|
|||||||
bool polyline) {
|
bool polyline) {
|
||||||
if (points.isEmpty()) return;
|
if (points.isEmpty()) return;
|
||||||
|
|
||||||
painter->setStrokeStyle(color);
|
QCanvasCustomBrush& brush = brushFor(color);
|
||||||
painter->setFillStyle(color);
|
|
||||||
|
painter->setStrokeStyle(brush);
|
||||||
|
painter->setFillStyle(brush);
|
||||||
painter->setLineWidth(width);
|
painter->setLineWidth(width);
|
||||||
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
||||||
painter->setLineJoin(QCanvasPainter::LineJoin::Round);
|
painter->setLineJoin(QCanvasPainter::LineJoin::Round);
|
||||||
@@ -65,7 +154,7 @@ static void drawStroke(
|
|||||||
painter->stroke();
|
painter->stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawHoverCursor(
|
void StrokeCanvasRenderer::drawHoverCursor(
|
||||||
QCanvasPainter* painter,
|
QCanvasPainter* painter,
|
||||||
const QPointF& point,
|
const QPointF& point,
|
||||||
qreal penWidth,
|
qreal penWidth,
|
||||||
@@ -74,7 +163,7 @@ static void drawHoverCursor(
|
|||||||
const qreal radius = penWidth * 0.5;
|
const qreal radius = penWidth * 0.5;
|
||||||
|
|
||||||
if (isDrawing) {
|
if (isDrawing) {
|
||||||
painter->setFillStyle(penColor);
|
painter->setFillStyle(brushFor(penColor));
|
||||||
painter->beginPath();
|
painter->beginPath();
|
||||||
painter->circle(point, radius);
|
painter->circle(point, radius);
|
||||||
painter->fill();
|
painter->fill();
|
||||||
@@ -85,7 +174,7 @@ static void drawHoverCursor(
|
|||||||
const bool useDashes = penWidth > 10.0;
|
const bool useDashes = penWidth > 10.0;
|
||||||
|
|
||||||
auto drawOutline = [&](const QColor& color, qreal width) {
|
auto drawOutline = [&](const QColor& color, qreal width) {
|
||||||
painter->setStrokeStyle(color);
|
painter->setStrokeStyle(brushFor(color));
|
||||||
painter->setLineWidth(width);
|
painter->setLineWidth(width);
|
||||||
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
||||||
|
|
||||||
@@ -117,7 +206,7 @@ static void drawHoverCursor(
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto drawCrosshair = [&](const QColor& color, qreal width) {
|
auto drawCrosshair = [&](const QColor& color, qreal width) {
|
||||||
painter->setStrokeStyle(color);
|
painter->setStrokeStyle(brushFor(color));
|
||||||
painter->setLineWidth(width);
|
painter->setLineWidth(width);
|
||||||
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
||||||
|
|
||||||
@@ -148,8 +237,11 @@ void StrokeCanvasRenderer::synchronizeData(QCanvasPainterItem* item) {
|
|||||||
|
|
||||||
m_penColor = canvas->m_penColor;
|
m_penColor = canvas->m_penColor;
|
||||||
m_penWidth = canvas->m_penWidth;
|
m_penWidth = canvas->m_penWidth;
|
||||||
|
m_hole = canvas->m_hole;
|
||||||
|
m_holeRadius = canvas->m_holeRadius;
|
||||||
|
m_devicePixelRatio =
|
||||||
|
canvas->window() ? canvas->window()->effectiveDevicePixelRatio() : 1.0;
|
||||||
|
|
||||||
// Detect removed strokes: present in renderer but no longer in canvas
|
|
||||||
QSet<int> canvasGroupIds;
|
QSet<int> canvasGroupIds;
|
||||||
for (const auto& stroke : canvas->m_strokes)
|
for (const auto& stroke : canvas->m_strokes)
|
||||||
canvasGroupIds.insert(stroke.groupId);
|
canvasGroupIds.insert(stroke.groupId);
|
||||||
@@ -162,7 +254,6 @@ void StrokeCanvasRenderer::synchronizeData(QCanvasPainterItem* item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect added strokes: present in canvas but not yet in renderer
|
|
||||||
QSet<int> rendererGroupIds;
|
QSet<int> rendererGroupIds;
|
||||||
for (const auto& stroke : m_strokes)
|
for (const auto& stroke : m_strokes)
|
||||||
rendererGroupIds.insert(stroke.groupId);
|
rendererGroupIds.insert(stroke.groupId);
|
||||||
@@ -180,6 +271,8 @@ void StrokeCanvasRenderer::synchronizeData(QCanvasPainterItem* item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void StrokeCanvasRenderer::paint(QCanvasPainter* painter) {
|
void StrokeCanvasRenderer::paint(QCanvasPainter* painter) {
|
||||||
|
updateBrushHole();
|
||||||
|
|
||||||
for (int id : m_pendingGroupRemovals)
|
for (int id : m_pendingGroupRemovals)
|
||||||
painter->removePathGroup(id);
|
painter->removePathGroup(id);
|
||||||
m_pendingGroupRemovals.clear();
|
m_pendingGroupRemovals.clear();
|
||||||
@@ -187,8 +280,8 @@ void StrokeCanvasRenderer::paint(QCanvasPainter* painter) {
|
|||||||
painter->clearRect(0, 0, width(), height());
|
painter->clearRect(0, 0, width(), height());
|
||||||
|
|
||||||
for (const auto& stroke : m_strokes) {
|
for (const auto& stroke : m_strokes) {
|
||||||
painter->setStrokeStyle(stroke.color);
|
painter->setStrokeStyle(brushFor(stroke.color));
|
||||||
painter->setFillStyle(stroke.color);
|
painter->setFillStyle(brushFor(stroke.color));
|
||||||
painter->setLineWidth(stroke.width);
|
painter->setLineWidth(stroke.width);
|
||||||
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
painter->setLineCap(QCanvasPainter::LineCap::Round);
|
||||||
painter->setLineJoin(QCanvasPainter::LineJoin::Round);
|
painter->setLineJoin(QCanvasPainter::LineJoin::Round);
|
||||||
|
|||||||
@@ -1,26 +1,58 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCanvasCustomBrush>
|
||||||
#include <QCanvasPainterItemRenderer>
|
#include <QCanvasPainterItemRenderer>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QRectF>
|
||||||
|
#include <QVector4D>
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include "stroke.hpp"
|
#include "stroke.hpp"
|
||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
|
struct BrushEntry {
|
||||||
|
QCanvasCustomBrush brush;
|
||||||
|
qint64 holeToken = -1; // hole generation this brush was built with
|
||||||
|
};
|
||||||
|
|
||||||
class StrokeCanvasRenderer final : public QCanvasPainterItemRenderer {
|
class StrokeCanvasRenderer final : public QCanvasPainterItemRenderer {
|
||||||
public:
|
public:
|
||||||
void synchronizeData(QCanvasPainterItem* item) override;
|
void synchronizeData(QCanvasPainterItem* item) override;
|
||||||
void paint(QCanvasPainter* painter) override;
|
void paint(QCanvasPainter* painter) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QCanvasCustomBrush& brushFor(const QColor& color);
|
||||||
|
void updateBrushHole();
|
||||||
|
void drawStroke(
|
||||||
|
QCanvasPainter* painter,
|
||||||
|
const QVector<QPointF>& points,
|
||||||
|
const QColor& color,
|
||||||
|
qreal width,
|
||||||
|
bool polyline);
|
||||||
|
void drawHoverCursor(
|
||||||
|
QCanvasPainter* painter,
|
||||||
|
const QPointF& point,
|
||||||
|
qreal penWidth,
|
||||||
|
QColor penColor,
|
||||||
|
bool isDrawing);
|
||||||
|
|
||||||
QColor m_penColor;
|
QColor m_penColor;
|
||||||
qreal m_penWidth = 4.0;
|
qreal m_penWidth = 4.0;
|
||||||
bool m_hoverVisible = false;
|
bool m_hoverVisible = false;
|
||||||
QPointF m_hoverPoint;
|
QPointF m_hoverPoint;
|
||||||
bool m_isDrawing = false;
|
bool m_isDrawing = false;
|
||||||
|
QRectF m_hole;
|
||||||
|
qreal m_holeRadius = 0.0;
|
||||||
|
qreal m_devicePixelRatio = 1.0;
|
||||||
|
|
||||||
|
QVector4D m_lastHoleData1;
|
||||||
|
QVector4D m_lastHoleData3;
|
||||||
|
QVector4D m_lastHoleData4;
|
||||||
|
qint64 m_holeToken = 0;
|
||||||
QVector<Stroke> m_strokes;
|
QVector<Stroke> m_strokes;
|
||||||
Stroke m_currentStroke;
|
Stroke m_currentStroke;
|
||||||
QVector<int> m_pendingGroupRemovals;
|
QVector<int> m_pendingGroupRemovals;
|
||||||
|
QHash<QRgb, BrushEntry> m_brushes;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace ZShell::internal
|
}; // namespace ZShell::internal
|
||||||
|
|||||||
Reference in New Issue
Block a user