Files

103 lines
4.0 KiB
Plaintext

import { TypeScale } from "../typography.slint";
// A single avatar primitive for people, accounts, and sender brands. The
// caller owns the image data; this component owns circular clipping, fallback
// initials, sizing, and consistent image sampling across the application.
export component Avatar inherits Rectangle {
in property <image> source;
in property <bool> has-source: false;
in property <string> fallback: "?";
in property <length> size: 40px;
in property <color> background-color: #f1f1ef;
in property <color> foreground-color: #4b4c49;
// Optional stable palette slot for people without stored photos. Callers
// derive the slot from an identity key; Avatar owns the theme-aware muted
// palette so fallback styling is not duplicated across contact surfaces.
in property <int> color-tone: -1;
in property <bool> dark-mode: false;
in property <color> avatar-border-color: transparent;
in property <length> avatar-border-width: 0px;
in property <ImageFit> image-fit: cover;
private property <color> tone-background: root.dark-mode
? (root.color-tone == 0 ? #293631
: root.color-tone == 1 ? #332f3b
: root.color-tone == 2 ? #382f2b
: root.color-tone == 3 ? #29353b
: root.color-tone == 4 ? #383529
: #2e372c)
: (root.color-tone == 0 ? #dfe8e3
: root.color-tone == 1 ? #e5e1ec
: root.color-tone == 2 ? #e9e1dc
: root.color-tone == 3 ? #dce6ea
: root.color-tone == 4 ? #e9e5d9
: #e0e7dd);
private property <color> tone-foreground: root.dark-mode
? (root.color-tone == 0 ? #a8c4b7
: root.color-tone == 1 ? #c0b3d0
: root.color-tone == 2 ? #cdb3a5
: root.color-tone == 3 ? #a9c0ca
: root.color-tone == 4 ? #c9c09e
: #b0c6aa)
: (root.color-tone == 0 ? #455c51
: root.color-tone == 1 ? #594e68
: root.color-tone == 2 ? #675045
: root.color-tone == 3 ? #405a66
: root.color-tone == 4 ? #635b40
: #4a6145);
// Initials keep at least the caption size, but shrink to fit the circle
// where the platform font makes them wider than a small avatar.
private property <length> initials-size: max(TypeScale.caption, root.size * 0.29);
width: root.size;
height: root.size;
border-radius: root.size / 2;
// Downloaded rasters carry their own antialiased circular alpha mask.
// Keeping the source surface transparent prevents opaque square corners
// from showing through on software-rendered views.
background: root.has-source ? transparent
: root.color-tone >= 0 ? root.tone-background
: root.background-color;
clip: true;
if root.has-source : Image {
width: parent.width;
height: parent.height;
source: root.source;
image-fit: root.image-fit;
image-rendering: smooth;
horizontal-alignment: center;
vertical-alignment: center;
accessible-role: none;
}
if !root.has-source : Rectangle {
initials-measure := Text {
visible: false;
text: root.fallback;
font-size: root.initials-size;
font-weight: 700;
}
Text {
width: parent.width;
height: parent.height;
text: root.fallback;
color: root.color-tone >= 0 ? root.tone-foreground : root.foreground-color;
font-size: root.initials-size * min(1, (root.size - 1px) / max(1px, initials-measure.preferred-width));
font-weight: 700;
horizontal-alignment: center;
vertical-alignment: center;
overflow: elide;
}
}
// Draw the outline after the image so full-bleed photos cannot cover it.
Rectangle {
width: parent.width;
height: parent.height;
border-radius: root.size / 2;
border-width: root.avatar-border-width;
border-color: root.avatar-border-color;
background: transparent;
}
}