// The pictures already on the machine, offered before the file chooser. // // /usr/share/pixmaps/faces is what GNOME's Users panel shows first, and it is // the answer for most people: a picture that is already the right shape, needs // no cropping, and costs one click. The file chooser stays right beside it for // everyone else, so choosing a photo is not a second-class path -- it is simply // the one that needs a crop step afterwards. // // An empty gallery is a normal state on a machine with no faces package // installed, and says so rather than rendering an empty strip. import QtQuick import Quickshell.Widgets import qs.config Column { id: root // [{ name, path }] -- whatever the helper found, in the order it found it. property var avatars: [] property bool enabled: true signal picked(path: string) signal fileRequested width: parent ? parent.width : 620 spacing: 10 bottomPadding: 6 Flow { width: parent.width spacing: 8 Repeater { model: root.avatars delegate: Item { id: tile required property var modelData readonly property string path: String(tile.modelData?.path ?? "") width: 46 height: 46 ClippingRectangle { anchors.fill: parent anchors.margins: 2 radius: width / 2 color: Theme.alpha(Theme.fg, 0.08) Image { anchors.fill: parent source: tile.path === "" ? "" : "file://" + tile.path fillMode: Image.PreserveAspectCrop asynchronous: true sourceSize.width: 96 sourceSize.height: 96 } } // The ring is its own item rather than a border on the clipping // rectangle: that one clips its children and does not carry a // border group of its own. Rectangle { anchors.fill: parent radius: width / 2 color: "transparent" border.width: tileHover.hovered ? 2 : 1 border.color: tileHover.hovered ? Theme.alpha(Theme.accent, 0.7) : Theme.alpha(Theme.fg, 0.1) } HoverHandler { id: tileHover enabled: root.enabled cursorShape: Qt.PointingHandCursor } TapHandler { enabled: root.enabled && tile.path !== "" onTapped: root.picked(tile.path) } } } // Wrapped so it sits on the tiles' centre line rather than on their top // edge: Flow lays children out by their own height, and the button is // fifteen pixels shorter than a picture. Item { width: chooseFile.implicitWidth height: 46 SettingsButton { id: chooseFile anchors.verticalCenter: parent.verticalCenter text: "Choose a file…" enabled: root.enabled onClicked: root.fileRequested() } } } Text { width: parent.width visible: root.avatars.length === 0 text: "No pictures are installed on this machine, so a file of your own is the only choice here." color: Theme.fgDim font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall wrapMode: Text.WordWrap } }