Files
johndoe6345789 5456f7eb4c feat(a11y): add Accessible roles, names, objectNames to all core QML components
Core: CButton, CIconButton, CFab, CChip, CListItem — Button roles, activeFocusOnTab
Forms: CTextField, CSelect, CCheckbox, CSwitch, CRadio, CRating — EditableText, CheckBox, ComboBox, Slider
Feedback: CAlert, CDialog, CSnackbar — AlertMessage, Dialog roles
Navigation: CTabBar — PageTabList + PageTab on delegates
Data: CAvatar, CBadge, CTable, CStatBadge, CStatusBadge — Graphic, StaticText, Table, Row
Surfaces: CCard (Pane), CAccordionItem (Button + expanded), CAppBar (ToolBar)
Progress: CProgress (ProgressBar + value), CSpinner (Animation)
Divider: CDivider (Separator)

28 files, 157 lines of a11y properties added. Zero to full coverage on core library.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:00:55 +00:00

101 lines
2.8 KiB
QML

import QtQuick
import QmlComponents 1.0
/**
* CAvatar.qml - Material Design 3 circular avatar
* Displays image or initials in a circular container with tonal surface
*
* Usage:
* CAvatar { initials: "JD" } // Tonal primary with
// initials
* CAvatar { src: "avatar.png" } // Image avatar
* CAvatar { size: "lg"; initials: "AB" } // Large avatar
* CAvatar { size: "sm"; bgColor: Theme.error } // Custom color
*/
Rectangle {
id: root
property string size: "md" // sm, md, lg
property string src: "" // Image source URL
property string initials: "" // Fallback initials (e.g. "JD")
property color bgColor:
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16)
property color textColor: Theme.primary
// MD3 size mapping: sm=32, md=40, lg=56
readonly property int _size: {
switch (size) {
case "sm": return 32
case "lg": return 56
default: return 40
}
}
// MD3 font size: scales with avatar size
readonly property int _fontSize: {
switch (size) {
case "sm": return 13
case "lg": return 22
default: return 16
}
}
Accessible.role: Accessible.Graphic
Accessible.name: initials || "Avatar"
width: _size
height: _size
radius: _size / 2
color: src !== "" ? "transparent" : bgColor
clip: true
// Image avatar
Image {
id: avatarImage
anchors.fill: parent
source: root.src
fillMode: Image.PreserveAspectCrop
visible: root.src !== "" && status === Image.Ready
smooth: true
asynchronous: true
// Circular clipping via layer
layer.enabled: true
layer.effect: Item {
Rectangle {
anchors.fill: parent
radius: width / 2
}
}
}
// Placeholder shown while image loads or on error
Rectangle {
anchors.fill: parent
radius: width / 2
color: root.bgColor
visible: root.src !== "" && avatarImage.status !== Image.Ready
Text {
anchors.centerIn: parent
text: root.initials.toUpperCase()
color: root.textColor
font.pixelSize: root._fontSize
font.weight: Font.Medium
font.family: Theme.fontFamily
visible: root.initials !== ""
}
}
// Initials fallback (no src)
Text {
anchors.centerIn: parent
text: root.initials.toUpperCase()
color: root.textColor
font.pixelSize: root._fontSize
font.weight: Font.Medium
font.family: Theme.fontFamily
visible: root.src === "" && root.initials !== ""
}
}