Make clipboard picker use a master/detail layout

This commit is contained in:
David Heinemeier Hansson
2026-05-17 10:49:53 +02:00
parent 32527e9201
commit 2381d015e0
@@ -30,8 +30,7 @@ Item {
property int contentSpacing: 6 property int contentSpacing: 6
property int cardWidth: 800 property int cardWidth: 800
property int cardHeight: 600 property int cardHeight: 600
property int rowHeight: 60 property int rowHeight: 50
property int cellWidth: (cardWidth - contentMargin * 2 - contentSpacing) / 2
function open(payloadJson) { function open(payloadJson) {
root.opened = true root.opened = true
@@ -211,15 +210,9 @@ Item {
if (root.filterText.length > 0) root.setFilter(root.filterText.slice(0, -1)) if (root.filterText.length > 0) root.setFilter(root.filterText.slice(0, -1))
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Up) { } else if (event.key === Qt.Key_Up) {
root.select(-2)
event.accepted = true
} else if (event.key === Qt.Key_Down) {
root.select(2)
event.accepted = true
} else if (event.key === Qt.Key_Left) {
root.select(-1) root.select(-1)
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_Right) { } else if (event.key === Qt.Key_Down) {
root.select(1) root.select(1)
event.accepted = true event.accepted = true
} else if (event.key === Qt.Key_PageUp) { } else if (event.key === Qt.Key_PageUp) {
@@ -267,96 +260,106 @@ Item {
width: parent.width width: parent.width
height: parent.height - root.headerHeight - root.contentSpacing height: parent.height - root.headerHeight - root.contentSpacing
GridView { Row {
id: resultList
anchors.fill: parent anchors.fill: parent
model: displayModel spacing: root.contentSpacing
clip: true
cellWidth: root.cellWidth
cellHeight: root.rowHeight
boundsBehavior: Flickable.StopAtBounds
delegate: Rectangle { ListView {
required property int index id: resultList
required property string identifier width: parent.width / 2 - root.contentSpacing / 2
required property string previewText height: parent.height
required property string previewImage model: displayModel
required property string previewType clip: true
spacing: 4
boundsBehavior: Flickable.StopAtBounds
width: root.cellWidth - 4 delegate: Rectangle {
height: root.rowHeight - 4 required property int index
radius: root.cornerRadius required property string identifier
color: index === root.selectedIndex ? root.withAlpha(root.foreground, 0.08) : root.withAlpha(root.foreground, mouseArea.containsMouse ? 0.045 : 0) required property string previewText
required property string previewType
Rectangle { width: ListView.view.width
visible: false height: root.rowHeight
width: 4 radius: root.cornerRadius
height: parent.height - 18 color: index === root.selectedIndex ? root.withAlpha(root.foreground, 0.08) : root.withAlpha(root.foreground, mouseArea.containsMouse ? 0.045 : 0)
radius: Math.min(root.cornerRadius, 4)
color: root.accent
anchors.left: parent.left
anchors.leftMargin: 8
anchors.verticalCenter: parent.verticalCenter
}
Item { Rectangle {
anchors.fill: parent visible: false
anchors.leftMargin: 12 width: 4
anchors.rightMargin: 12 height: parent.height - 18
anchors.topMargin: 8 radius: Math.min(root.cornerRadius, 4)
anchors.bottomMargin: 8 color: root.accent
anchors.left: parent.left
Text { anchors.leftMargin: 8
visible: parent.parent.previewType === "text" anchors.verticalCenter: parent.verticalCenter
width: parent.width
height: parent.height
text: parent.parent.previewText
color: index === root.selectedIndex ? root.accent : root.foreground
font.family: root.fontFamily
font.pixelSize: 14
elide: Text.ElideRight
wrapMode: Text.NoWrap
verticalAlignment: Text.AlignVCenter
} }
Item { Item {
visible: parent.parent.previewType === "file" anchors.fill: parent
width: parent.width anchors.leftMargin: 12
height: parent.height anchors.rightMargin: 12
anchors.topMargin: 8
Image { anchors.bottomMargin: 8
source: parent.parent.parent.previewImage
anchors.left: parent.left
anchors.top: parent.top
anchors.bottom: parent.bottom
width: height * 1.5
fillMode: Image.PreserveAspectFit
}
Text { Text {
anchors.left: parent.children[0].right width: parent.width
anchors.leftMargin: 12 height: parent.height
anchors.right: parent.right text: parent.parent.previewType === "text" ? parent.parent.previewText : "Image"
anchors.verticalCenter: parent.verticalCenter color: index === root.selectedIndex ? root.accent : root.foreground
text: "Image"
color: root.foreground
opacity: 0.6
font.family: root.fontFamily font.family: root.fontFamily
font.pixelSize: 14 font.pixelSize: 14
font.italic: true font.italic: parent.parent.previewType === "file"
opacity: parent.parent.previewType === "file" ? 0.6 : 1.0
elide: Text.ElideRight
wrapMode: Text.NoWrap
verticalAlignment: Text.AlignVCenter
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedIndex = index
root.activateIndex(index)
} }
} }
} }
}
MouseArea { Rectangle {
id: mouseArea width: parent.width / 2 - root.contentSpacing / 2
height: parent.height
radius: root.cornerRadius
color: root.withAlpha(root.background, 0.5)
border.color: root.withAlpha(root.border, 0.1)
border.width: 1
clip: true
property var activeRow: displayModel.count > 0 && root.selectedIndex >= 0 && root.selectedIndex < displayModel.count ? displayModel.get(root.selectedIndex) : null
Text {
visible: parent.activeRow && parent.activeRow.previewType === "text"
anchors.fill: parent anchors.fill: parent
hoverEnabled: true anchors.margins: 16
cursorShape: Qt.PointingHandCursor text: parent.activeRow ? parent.activeRow.previewText : ""
onClicked: { color: root.foreground
root.selectedIndex = index font.family: root.fontFamily
root.activateIndex(index) font.pixelSize: 14
} wrapMode: Text.WrapAnywhere
elide: Text.ElideRight
verticalAlignment: Text.AlignTop
}
Image {
visible: parent.activeRow && parent.activeRow.previewType === "file"
anchors.fill: parent
anchors.margins: 16
source: parent.activeRow ? parent.activeRow.previewImage : ""
fillMode: Image.PreserveAspectFit
} }
} }
} }