Give identity its due: native enrollment, honest deletion, and sign-in that stays home
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user