update
This commit is contained in:
142
themes/sm1tee/components/Avatar.qml
Normal file
142
themes/sm1tee/components/Avatar.qml
Normal file
@@ -0,0 +1,142 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user