Files
Ваше Имя ed9ba99412 update
2025-06-30 23:19:29 +03:00

143 lines
4.4 KiB
QML

import QtQuick
import QtQuick.Controls
Canvas {
id: avatar
signal clicked
signal clickedOutside
property bool active: false
property string source: ""
property string shape: Config.avatarShape
property int squareRadius: Config.avatarBorderRadius === 0 ? 1 : Config.avatarBorderRadius // min: 1
property bool drawStroke: (active && Config.avatarActiveBorderSize > 0) || (!active && Config.avatarInactiveBorderSize > 0)
property color strokeColor: active ? Config.avatarActiveBorderColor : Config.avatarInactiveBorderColor
property int strokeSize: active ? Config.avatarActiveBorderSize : Config.avatarInactiveBorderSize
property string tooltipText: ""
property bool showTooltip: false
onSourceChanged: delayPaintTimer.running = true
onPaint: {
// FIX: Canvas zero dimension protection
if (width <= 0 || height <= 0)
return;
var ctx = getContext("2d");
ctx.reset(); // Clear previous drawing
ctx.beginPath();
if (shape === "square") {
// Squircle, actually
var r = width * squareRadius / 100;
ctx.moveTo(width - r, 0);
ctx.arcTo(width, 0, width, height, r);
ctx.arcTo(width, height, 0, height, r);
ctx.arcTo(0, height, 0, 0, r);
ctx.arcTo(0, 0, width, 0, r);
ctx.closePath();
} else {
// Circle
ctx.ellipse(0, 0, width, height);
}
ctx.clip();
if (source === "")
source = "../icons/user-default.png";
ctx.drawImage(source, 0, 0, width, height);
// Border
if (drawStroke) {
ctx.strokeStyle = strokeColor;
ctx.lineWidth = strokeSize;
ctx.stroke();
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.ArrowCursor
function isCursorInsideAvatar() {
if (!mouseArea.containsMouse)
return false;
if (avatar.shape === "square")
return true;
// Ellipse center and radius
var centerX = width / 2;
var centerY = height / 2;
var radiusX = centerX;
var radiusY = centerY;
// Distance from center
var dx = (mouseArea.mouseX - centerX) / radiusX;
var dy = (mouseArea.mouseY - centerY) / radiusY;
// Check if pointer is inside the ellipse
return (dx * dx + dy * dy) <= 1.0;
}
onReleased: function (mouse) {
var isInside = isCursorInsideAvatar();
if (isInside) {
avatar.clicked();
} else {
avatar.clickedOutside();
}
mouse.accepted = isInside;
}
function updateHover() {
if (isCursorInsideAvatar()) {
cursorShape = Qt.PointingHandCursor;
} else {
cursorShape = Qt.ArrowCursor;
}
}
onMouseXChanged: updateHover()
onMouseYChanged: updateHover()
ToolTip {
parent: mouseArea
enabled: Config.tooltipsEnable && !Config.tooltipsDisableUser
property bool shouldShow: enabled && avatar.showTooltip || (enabled && mouseArea.isCursorInsideAvatar() && avatar.tooltipText !== "")
visible: shouldShow
delay: 300
contentItem: Text {
font.family: Config.tooltipsFontFamily
font.pixelSize: Config.tooltipsFontSize
text: avatar.tooltipText
color: Config.tooltipsContentColor
}
background: Rectangle {
color: Config.tooltipsBackgroundColor
opacity: Config.tooltipsBackgroundOpacity
border.width: 0
radius: Config.tooltipsBorderRadius
}
}
}
// FIX: paint() not affect event if source is not empty in initialization
Timer {
id: delayPaintTimer
repeat: false
interval: 150
onTriggered: avatar.requestPaint()
running: true
}
// FIX: Critical timer memory leak prevention
// Overkill, but fine...
Component.onDestruction: {
if (delayPaintTimer) {
delayPaintTimer.running = false;
delayPaintTimer.stop();
}
}
}