134 lines
4.5 KiB
QML
134 lines
4.5 KiB
QML
// Crisp, font-independent media transport icons drawn with Canvas2D.
|
|
//
|
|
// Shapes: "prev", "play", "pause", "next", "stop". The icon fills a square
|
|
// bounded by implicitWidth x implicitHeight (default 14 x 14) and is centred
|
|
// inside that square. Color follows the active theme.
|
|
|
|
import QtQuick
|
|
|
|
Item {
|
|
id: root
|
|
property string shape: "play"
|
|
property color color: "#d4be98"
|
|
property real size: 14
|
|
|
|
implicitWidth: size
|
|
implicitHeight: size
|
|
|
|
onShapeChanged: canvas.requestPaint()
|
|
onColorChanged: canvas.requestPaint()
|
|
|
|
Canvas {
|
|
id: canvas
|
|
anchors.fill: parent
|
|
antialiasing: true
|
|
onPaint: {
|
|
const ctx = getContext("2d");
|
|
ctx.reset();
|
|
const w = width, h = height;
|
|
const s = Math.min(w, h);
|
|
// Inset so strokes/triangle tips don't clip on the canvas edge.
|
|
const pad = Math.max(1, Math.round(s * 0.12));
|
|
const x0 = (w - s) / 2 + pad;
|
|
const y0 = (h - s) / 2 + pad;
|
|
const sz = s - pad * 2;
|
|
|
|
ctx.fillStyle = root.color;
|
|
ctx.strokeStyle = root.color;
|
|
ctx.lineJoin = "round";
|
|
ctx.lineCap = "round";
|
|
|
|
switch (root.shape) {
|
|
case "prev": drawPrev(ctx, x0, y0, sz); break;
|
|
case "next": drawNext(ctx, x0, y0, sz); break;
|
|
case "pause": drawPause(ctx, x0, y0, sz); break;
|
|
case "stop": drawStop(ctx, x0, y0, sz); break;
|
|
case "play":
|
|
default: drawPlay(ctx, x0, y0, sz); break;
|
|
}
|
|
}
|
|
|
|
function triangleRight(ctx, x, y, w, h) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, y);
|
|
ctx.lineTo(x + w, y + h / 2);
|
|
ctx.lineTo(x, y + h);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
function triangleLeft(ctx, x, y, w, h) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + w, y);
|
|
ctx.lineTo(x, y + h / 2);
|
|
ctx.lineTo(x + w, y + h);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
|
|
function drawPlay(ctx, x0, y0, sz) {
|
|
// Narrow the triangle to 92% of width for balance; full height, centered.
|
|
const w = sz * 0.92;
|
|
triangleRight(ctx, x0 + (sz - w) / 2, y0, w, sz);
|
|
}
|
|
|
|
function drawPause(ctx, x0, y0, sz) {
|
|
const barW = Math.max(2, Math.round(sz * 0.28));
|
|
const gap = Math.max(2, Math.round(sz * 0.18));
|
|
const totalW = barW * 2 + gap;
|
|
const left = x0 + (sz - totalW) / 2;
|
|
ctx.fillRect(left, y0, barW, sz);
|
|
ctx.fillRect(left + barW + gap, y0, barW, sz);
|
|
}
|
|
|
|
function drawStop(ctx, x0, y0, sz) {
|
|
const side = sz * 0.86;
|
|
const inset = (sz - side) / 2;
|
|
ctx.fillRect(x0 + inset, y0 + inset, side, side);
|
|
}
|
|
|
|
function drawPrev(ctx, x0, y0, sz) {
|
|
// Vertical bar + left-pointing double triangle.
|
|
const barW = Math.max(1.5, sz * 0.14);
|
|
ctx.fillRect(x0, y0, barW, sz);
|
|
// Two stacked triangles forming the doubled arrow.
|
|
const tStart = x0 + barW + Math.max(1, sz * 0.06);
|
|
const tSpan = (x0 + sz) - tStart;
|
|
const tHalf = tSpan / 2;
|
|
ctx.beginPath();
|
|
ctx.moveTo(tStart, y0 + sz / 2);
|
|
ctx.lineTo(tStart + tHalf, y0);
|
|
ctx.lineTo(tStart + tHalf, y0 + sz);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.moveTo(tStart + tHalf, y0 + sz / 2);
|
|
ctx.lineTo(tStart + tSpan, y0);
|
|
ctx.lineTo(tStart + tSpan, y0 + sz);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
|
|
function drawNext(ctx, x0, y0, sz) {
|
|
// Right-pointing double triangle + trailing vertical bar.
|
|
const barW = Math.max(1.5, sz * 0.14);
|
|
const barX = x0 + sz - barW;
|
|
ctx.fillRect(barX, y0, barW, sz);
|
|
const tEnd = barX - Math.max(1, sz * 0.06);
|
|
const tSpan = tEnd - x0;
|
|
const tHalf = tSpan / 2;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x0, y0);
|
|
ctx.lineTo(x0 + tHalf, y0 + sz / 2);
|
|
ctx.lineTo(x0, y0 + sz);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x0 + tHalf, y0);
|
|
ctx.lineTo(tEnd, y0 + sz / 2);
|
|
ctx.lineTo(x0 + tHalf, y0 + sz);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
}
|
|
}
|