// One theme, drawn as the desktop it produces. // // A row of flat swatches would name a theme's colors without showing what any // of them does. So the card is a miniature of the real thing: the ground, a // window sitting on it wearing the accent as a border, the two text weights // the shell actually reads with, and only then the four remaining colors as // dots. Clicking applies it immediately -- there is no preview mode here, // because the whole shell is the preview. import QtQuick import qs.config import qs.services Rectangle { id: root // A catalog record or a saved profile -- the same shape either way. required property var theme signal chosen signal removed // A custom profile saved before palettes existed carries none of its own // and inherits its scheme's default, exactly as ThemeProfiles resolves it. readonly property var palette: root.theme.palette ?? ThemeCatalog.defaultPalette(root.theme.scheme) readonly property bool active: root.theme.id === ThemeProfiles.activeId readonly property bool shipped: root.theme.shipped === true readonly property bool isDefault: root.theme.id === (root.theme.scheme === "light" ? ThemeCatalog.defaultLight : ThemeCatalog.defaultDark) implicitHeight: preview.height + caption.height + 4 radius: Theme.cardRadius + 2 color: Theme.alpha(Theme.fg, 0.04) border.width: 2 border.color: root.activeFocus ? Theme.accentSecondary : (root.active ? Theme.accent : Theme.alpha(Theme.fg, 0.12)) clip: true activeFocusOnTab: true Accessible.role: Accessible.Button Accessible.name: root.theme.name Accessible.description: (root.theme.scheme === "light" ? "Light theme" : "Dark theme") + (root.active ? ", selected" : "") Accessible.focusable: true Accessible.focused: root.activeFocus Keys.onReturnPressed: root.chosen() Keys.onSpacePressed: root.chosen() Rectangle { id: preview anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: 2 height: 84 color: root.palette.bg border.width: 0 Rectangle { anchors.fill: parent anchors.margins: 11 radius: 7 color: root.palette.bgPanel border.width: 2 border.color: root.theme.accent Column { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.margins: 8 spacing: 7 Rectangle { width: parent.width * 0.64 height: 4 radius: 2 color: root.palette.fg border.width: 0 } Row { spacing: 4 Repeater { model: [root.theme.accent, root.theme.secondary, root.palette.fgDim, root.palette.fgMuted] Rectangle { required property var modelData width: 9 height: 9 radius: 5 color: modelData border.width: 0 } } } } } } Item { id: caption anchors.left: parent.left anchors.right: parent.right anchors.top: preview.bottom anchors.leftMargin: 12 anchors.rightMargin: 10 height: 30 Text { id: name anchors.left: parent.left anchors.right: marks.left anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter text: root.theme.name + (root.isDefault ? " · today's default" : "") color: Theme.fg font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall font.weight: Font.DemiBold elide: Text.ElideRight } Row { id: marks anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: 6 Text { anchors.verticalCenter: parent.verticalCenter visible: root.active text: "✓" color: Theme.accent font.family: Theme.fontFamily font.pixelSize: Theme.fontSize font.weight: Font.DemiBold } // Only saved themes can be removed, and the affordance is a // separate focus stop so Tab never lands on "delete" when the // person meant "select". Rectangle { id: remove anchors.verticalCenter: parent.verticalCenter visible: !root.shipped width: 22 height: 22 radius: 7 color: removeHover.hovered ? Theme.alpha(Theme.danger, 0.22) : Theme.alpha(Theme.fg, 0.07) border.width: remove.activeFocus ? 2 : 1 border.color: remove.activeFocus ? Theme.danger : Theme.alpha(Theme.fg, 0.08) activeFocusOnTab: visible Accessible.role: Accessible.Button Accessible.name: "Delete " + root.theme.name Accessible.focusable: true Accessible.focused: remove.activeFocus Keys.onReturnPressed: root.removed() Keys.onSpacePressed: root.removed() Text { anchors.centerIn: parent text: "✕" color: Theme.danger font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall } HoverHandler { id: removeHover cursorShape: Qt.PointingHandCursor } // An exclusive grab, so the delete press does not also reach // the card's own tap handler underneath and select the theme // on its way out. TapHandler { gesturePolicy: TapHandler.ReleaseWithinBounds onTapped: root.removed() } } } } HoverHandler { id: cardHover cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: root.chosen() } }