mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
DesktopWidgets: better scaling
This commit is contained in:
@@ -245,25 +245,25 @@ Variants {
|
||||
}
|
||||
}
|
||||
|
||||
// Background for edit mode controls
|
||||
// Edit mode controls panel
|
||||
Rectangle {
|
||||
id: editModeControlsBackground
|
||||
id: editModeControlsPanel
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
|
||||
readonly property string barPos: Settings.data.bar.position || "top"
|
||||
readonly property bool barFloating: Settings.data.bar.floating || false
|
||||
// Calculate offset from bar based on position and floating state
|
||||
|
||||
readonly property int barOffsetTop: {
|
||||
if (barPos !== "top")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
return Style.marginM;
|
||||
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
return Style.barHeight + floatMarginV + Style.marginM;
|
||||
}
|
||||
readonly property int barOffsetRight: {
|
||||
if (barPos !== "right")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
return Style.marginM;
|
||||
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
return Style.barHeight + floatMarginH + Style.marginM;
|
||||
}
|
||||
|
||||
anchors {
|
||||
@@ -273,21 +273,8 @@ Variants {
|
||||
rightMargin: barOffsetRight
|
||||
}
|
||||
|
||||
// Calculate width to accommodate all controls
|
||||
width: {
|
||||
var buttonWidth = editModeButton.visible ? editModeButton.implicitWidth : 0;
|
||||
var explanationWidth = controlsExplanation.visible ? controlsExplanation.width : 0;
|
||||
var checkboxWidth = gridSnapCheckbox.visible ? gridSnapCheckbox.implicitWidth : 0;
|
||||
return Math.max(buttonWidth, explanationWidth, checkboxWidth, 200) + (Style.marginXL * 2);
|
||||
}
|
||||
|
||||
// Calculate height to cover all controls with spacing
|
||||
height: {
|
||||
var buttonHeight = editModeButton.visible ? editModeButton.height : 0;
|
||||
var explanationHeight = controlsExplanation.visible ? controlsExplanation.height : 0;
|
||||
var checkboxHeight = gridSnapCheckbox.visible ? gridSnapCheckbox.height : 0;
|
||||
return buttonHeight + Style.marginXL + explanationHeight + Style.marginXL + checkboxHeight + (Style.marginXL * 2);
|
||||
}
|
||||
width: controlsLayout.implicitWidth + (Style.marginXL * 2)
|
||||
height: controlsLayout.implicitHeight + (Style.marginXL * 2)
|
||||
|
||||
color: Qt.rgba(Color.mSurface.r, Color.mSurface.g, Color.mSurface.b, 0.85)
|
||||
radius: Style.radiusL
|
||||
@@ -296,89 +283,50 @@ Variants {
|
||||
color: Qt.alpha(Color.mOutline, 0.2)
|
||||
}
|
||||
z: 9999
|
||||
}
|
||||
|
||||
// Exit edit mode button
|
||||
NButton {
|
||||
id: editModeButton
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
ColumnLayout {
|
||||
id: controlsLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: Style.marginXL
|
||||
}
|
||||
spacing: Style.marginL
|
||||
|
||||
readonly property string barPos: Settings.data.bar.position || "top"
|
||||
readonly property bool barFloating: Settings.data.bar.floating || false
|
||||
// Calculate offset from bar based on position and floating state
|
||||
readonly property int barOffsetTop: {
|
||||
if (barPos !== "top")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
const floatMarginV = barFloating ? Math.ceil(Settings.data.bar.marginVertical * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginV + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
}
|
||||
readonly property int barOffsetRight: {
|
||||
if (barPos !== "right")
|
||||
return Style.marginXL * Style.uiScaleRatio;
|
||||
const floatMarginH = barFloating ? Math.ceil(Settings.data.bar.marginHorizontal * Style.marginXL) : 0;
|
||||
return Style.barHeight + floatMarginH + Style.marginM + (Style.marginXL * Style.uiScaleRatio);
|
||||
}
|
||||
NButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
|
||||
icon: "logout"
|
||||
outlined: false
|
||||
fontSize: Style.fontSizeM * 1.1
|
||||
iconSize: Style.fontSizeL * 1.1
|
||||
onClicked: Settings.data.desktopWidgets.editMode = false
|
||||
}
|
||||
|
||||
anchors {
|
||||
top: editModeControlsBackground.top
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.exit-button")
|
||||
icon: "logout"
|
||||
//backgroundColor: Color.mSurface
|
||||
//textColor: Color.mOnSurface
|
||||
//hoverColor: Color.mSurfaceVariant
|
||||
outlined: false
|
||||
fontSize: Style.fontSizeM * 1.1
|
||||
iconSize: Style.fontSizeL * 1.1
|
||||
z: 10000
|
||||
onClicked: Settings.data.desktopWidgets.editMode = false
|
||||
}
|
||||
NText {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.maximumWidth: 300 * Style.uiScaleRatio
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// Controls explanation text
|
||||
NText {
|
||||
id: controlsExplanation
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
anchors {
|
||||
top: editModeButton.bottom
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.controls-explanation")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
wrapMode: Text.WordWrap
|
||||
width: Math.min(implicitWidth, 300 * Style.uiScaleRatio)
|
||||
z: 10000
|
||||
}
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Style.marginS
|
||||
|
||||
// Grid snap checkbox
|
||||
RowLayout {
|
||||
id: gridSnapCheckbox
|
||||
visible: Settings.data.desktopWidgets.editMode && Settings.data.desktopWidgets.enabled
|
||||
anchors {
|
||||
top: controlsExplanation.bottom
|
||||
right: editModeControlsBackground.right
|
||||
topMargin: Style.marginXL
|
||||
rightMargin: Style.marginXL
|
||||
}
|
||||
spacing: Style.marginS
|
||||
z: 10000
|
||||
NText {
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.desktop-widgets.edit-mode.grid-snap.label")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignRight
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
checked: Settings.data.desktopWidgets.gridSnap
|
||||
onToggled: checked => Settings.data.desktopWidgets.gridSnap = checked
|
||||
NCheckbox {
|
||||
checked: Settings.data.desktopWidgets.gridSnap
|
||||
onToggled: checked => Settings.data.desktopWidgets.gridSnap = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,8 +278,8 @@ Item {
|
||||
anchors.margins: -Style.marginS
|
||||
color: Settings.data.desktopWidgets.editMode ? Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.1) : Color.transparent
|
||||
border.color: (Settings.data.desktopWidgets.editMode || internal.isDragging) ? (internal.isDragging ? Color.mOutline : Color.mPrimary) : Color.transparent
|
||||
border.width: Settings.data.desktopWidgets.editMode ? 3 : (internal.isDragging ? 2 : 0)
|
||||
radius: Style.radiusL + Style.marginS
|
||||
border.width: Settings.data.desktopWidgets.editMode ? 3 : 0
|
||||
radius: Style.radiusL
|
||||
z: -1
|
||||
}
|
||||
|
||||
@@ -459,64 +459,126 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Corner handles for scaling
|
||||
readonly property real cornerHandleSize: 12
|
||||
// Corner handles for scaling - using Repeater to avoid code duplication
|
||||
readonly property real cornerHandleSize: 8
|
||||
readonly property real outlineMargin: Style.marginS
|
||||
readonly property color colorHandle: Color.mSecondary
|
||||
|
||||
// Top-left corner
|
||||
Canvas {
|
||||
id: topLeftHandle
|
||||
visible: Settings.data.desktopWidgets.editMode && !internal.isDragging
|
||||
x: -outlineMargin
|
||||
y: -outlineMargin
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.7);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(cornerHandleSize, 0);
|
||||
ctx.lineTo(0, cornerHandleSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
// Corner handle model: defines position, opposite corner, cursor, and triangle points for each corner
|
||||
// xMult/yMult: multipliers for position (0 = left/top edge, 1 = right/bottom edge)
|
||||
// oppXMult/oppYMult: multipliers for opposite corner calculation
|
||||
// cursor: resize cursor type (FDiag for TL-BR diagonal, BDiag for TR-BL diagonal)
|
||||
// points: triangle vertices as [x, y] pairs normalized to cornerHandleSize
|
||||
readonly property var cornerHandleModel: [
|
||||
{
|
||||
xMult: 0,
|
||||
yMult: 0,
|
||||
oppXMult: 1,
|
||||
oppYMult: 1,
|
||||
cursor: Qt.SizeFDiagCursor,
|
||||
points: [[0, 0], [1, 0], [0, 1]]
|
||||
},
|
||||
{
|
||||
xMult: 1,
|
||||
yMult: 0,
|
||||
oppXMult: 0,
|
||||
oppYMult: 1,
|
||||
cursor: Qt.SizeBDiagCursor,
|
||||
points: [[1, 0], [1, 1], [0, 0]]
|
||||
},
|
||||
{
|
||||
xMult: 0,
|
||||
yMult: 1,
|
||||
oppXMult: 1,
|
||||
oppYMult: 0,
|
||||
cursor: Qt.SizeBDiagCursor,
|
||||
points: [[0, 1], [0, 0], [1, 1]]
|
||||
},
|
||||
{
|
||||
xMult: 1,
|
||||
yMult: 1,
|
||||
oppXMult: 0,
|
||||
oppYMult: 0,
|
||||
cursor: Qt.SizeFDiagCursor,
|
||||
points: [[1, 1], [1, 0], [0, 1]]
|
||||
}
|
||||
]
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
Repeater {
|
||||
model: root.cornerHandleModel
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.SizeFDiagCursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
property real initialScale: 1.0
|
||||
delegate: Canvas {
|
||||
id: cornerHandle
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
visible: Settings.data.desktopWidgets.editMode && !internal.isDragging
|
||||
x: modelData.xMult * (root.width + outlineMargin) - (modelData.xMult === 0 ? outlineMargin : cornerHandleSize)
|
||||
y: modelData.yMult * (root.height + outlineMargin) - (modelData.yMult === 0 ? outlineMargin : cornerHandleSize)
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = colorHandle;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(modelData.points[0][0] * cornerHandleSize, modelData.points[0][1] * cornerHandleSize);
|
||||
ctx.lineTo(modelData.points[1][0] * cornerHandleSize, modelData.points[1][1] * cornerHandleSize);
|
||||
ctx.lineTo(modelData.points[2][0] * cornerHandleSize, modelData.points[2][1] * cornerHandleSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onWidthChanged() {
|
||||
if (cornerHandle.visible)
|
||||
cornerHandle.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
if (cornerHandle.visible)
|
||||
cornerHandle.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: scaleMouseArea
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: cornerHandle.modelData.cursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
// Capture opposite corner at press time to avoid feedback loop during scaling
|
||||
property real oppositeCornerX: 0
|
||||
property real oppositeCornerY: 0
|
||||
property real initialDistance: 0
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate and store opposite corner position using initial scale
|
||||
oppositeCornerX = root.x + cornerHandle.modelData.oppXMult * root.width * root.widgetScale;
|
||||
oppositeCornerY = root.y + cornerHandle.modelData.oppYMult * root.height * root.widgetScale;
|
||||
initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale") {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate diagonal distance from opposite corner (bottom-right)
|
||||
var oppositeCornerX = root.x + root.width * root.widgetScale;
|
||||
var oppositeCornerY = root.y + root.height * root.widgetScale;
|
||||
var initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale" && initialDistance > 0) {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Use the fixed opposite corner position captured at press time
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
|
||||
if (initialDistance > 0) {
|
||||
var scaleRatio = currentDistance / initialDistance;
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
|
||||
|
||||
@@ -526,299 +588,23 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Top-right corner
|
||||
Canvas {
|
||||
id: topRightHandle
|
||||
visible: Settings.data.desktopWidgets.editMode && !internal.isDragging
|
||||
x: root.width + outlineMargin - cornerHandleSize
|
||||
y: -outlineMargin
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.7);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cornerHandleSize, 0);
|
||||
ctx.lineTo(cornerHandleSize, cornerHandleSize);
|
||||
ctx.lineTo(0, 0);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.SizeBDiagCursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
property real initialScale: 1.0
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale") {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate diagonal distance from opposite corner (bottom-left)
|
||||
var oppositeCornerX = root.x;
|
||||
var oppositeCornerY = root.y + root.height * root.widgetScale;
|
||||
var initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
|
||||
if (initialDistance > 0) {
|
||||
var scaleRatio = currentDistance / initialDistance;
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
|
||||
|
||||
if (!isNaN(newScale) && newScale > 0) {
|
||||
root.widgetScale = newScale;
|
||||
internal.lastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom-left corner
|
||||
Canvas {
|
||||
id: bottomLeftHandle
|
||||
visible: Settings.data.desktopWidgets.editMode && !internal.isDragging
|
||||
x: -outlineMargin
|
||||
y: root.height + outlineMargin - cornerHandleSize
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.7);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(0, cornerHandleSize);
|
||||
ctx.lineTo(0, 0);
|
||||
ctx.lineTo(cornerHandleSize, cornerHandleSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onWidthChanged() {
|
||||
if (bottomLeftHandle.visible)
|
||||
bottomLeftHandle.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
if (bottomLeftHandle.visible)
|
||||
bottomLeftHandle.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.SizeBDiagCursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
property real initialScale: 1.0
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale") {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate diagonal distance from opposite corner (top-right)
|
||||
var oppositeCornerX = root.x + root.width * root.widgetScale;
|
||||
var oppositeCornerY = root.y;
|
||||
var initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
|
||||
if (initialDistance > 0) {
|
||||
var scaleRatio = currentDistance / initialDistance;
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
|
||||
|
||||
if (!isNaN(newScale) && newScale > 0) {
|
||||
root.widgetScale = newScale;
|
||||
internal.lastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom-right corner
|
||||
Canvas {
|
||||
id: bottomRightHandle
|
||||
visible: Settings.data.desktopWidgets.editMode && !internal.isDragging
|
||||
x: root.width + outlineMargin - cornerHandleSize
|
||||
y: root.height + outlineMargin - cornerHandleSize
|
||||
width: cornerHandleSize
|
||||
height: cornerHandleSize
|
||||
z: 2000
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.fillStyle = Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.7);
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(cornerHandleSize, cornerHandleSize);
|
||||
ctx.lineTo(cornerHandleSize, 0);
|
||||
ctx.lineTo(0, cornerHandleSize);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onVisibleChanged: if (visible)
|
||||
requestPaint()
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onWidthChanged() {
|
||||
if (bottomRightHandle.visible)
|
||||
bottomRightHandle.requestPaint();
|
||||
}
|
||||
function onHeightChanged() {
|
||||
if (bottomRightHandle.visible)
|
||||
bottomRightHandle.requestPaint();
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
cursorShape: Qt.SizeFDiagCursor
|
||||
property point pressPos: Qt.point(0, 0)
|
||||
property real initialScale: 1.0
|
||||
|
||||
onPressed: mouse => {
|
||||
if (internal.operationType !== "") {
|
||||
return;
|
||||
}
|
||||
pressPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
internal.operationType = "scale";
|
||||
internal.isScaling = true;
|
||||
internal.initialScale = root.widgetScale;
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (internal.isScaling && pressed && internal.operationType === "scale") {
|
||||
var currentPos = mapToItem(root.parent, mouse.x, mouse.y);
|
||||
// Calculate diagonal distance from opposite corner (top-left)
|
||||
var oppositeCornerX = root.x;
|
||||
var oppositeCornerY = root.y;
|
||||
var initialDistance = Math.sqrt(Math.pow(pressPos.x - oppositeCornerX, 2) + Math.pow(pressPos.y - oppositeCornerY, 2));
|
||||
var currentDistance = Math.sqrt(Math.pow(currentPos.x - oppositeCornerX, 2) + Math.pow(currentPos.y - oppositeCornerY, 2));
|
||||
|
||||
if (initialDistance > 0) {
|
||||
var scaleRatio = currentDistance / initialDistance;
|
||||
var newScale = Math.max(minScale, Math.min(maxScale, internal.initialScale * scaleRatio));
|
||||
|
||||
if (!isNaN(newScale) && newScale > 0) {
|
||||
root.widgetScale = newScale;
|
||||
internal.lastScale = newScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: mouse => {
|
||||
if (internal.isScaling && internal.operationType === "scale") {
|
||||
root.updateWidgetData({
|
||||
"scale": root.widgetScale
|
||||
});
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
onCanceled: {
|
||||
internal.isScaling = false;
|
||||
internal.operationType = "";
|
||||
internal.lastScale = root.widgetScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user