Launcher: wip image preview

This commit is contained in:
LemmyCook 2025-09-03 09:22:27 -04:00
parent ded133d164
commit 132dbce3a3
3 changed files with 55 additions and 53 deletions

View file

@ -231,9 +231,6 @@ NPanel {
clip: true
cacheBuffer: resultsList.height * 2
//boundsBehavior: Flickable.StopAtBounds
// maximumFlickVelocity: 2500
// flickDeceleration: 2000
onCurrentIndexChanged: {
cancelFlick()
if (currentIndex >= 0) {
@ -245,15 +242,26 @@ NPanel {
policy: ScrollBar.AsNeeded
}
// Replace the delegate in Launcher.qml's ListView with this enhanced version:
delegate: Rectangle {
id: entry
property bool isSelected: mouseArea.containsMouse || (index === selectedIndex)
property int badgeSize: Style.baseWidgetSize * 1.75 * scaling
property int badgeSize: Style.baseWidgetSize * 1.75 * scaling
// Property to reliably track the current item's ID.
// This changes whenever the delegate is recycled for a new item.
property var currentClipboardId: modelData.isImage ? modelData.clipboardId : ""
// When this delegate is assigned a new image item, trigger the decode.
onCurrentClipboardIdChanged: {
// Check if it's a valid ID and if the data isn't already cached.
if (currentClipboardId && !CliphistService.getImageData(currentClipboardId)) {
CliphistService.decodeToDataUrl(currentClipboardId, modelData.mime, null)
}
}
width: resultsList.width - Style.marginS * scaling
height: badgeSize + Style.marginM * 2 *scaling
height: badgeSize + Style.marginM * 2 * scaling
radius: Style.radiusM * scaling
color: entry.isSelected ? Color.mTertiary : Color.mSurface
@ -282,8 +290,19 @@ NPanel {
id: imagePreview
anchors.fill: parent
anchors.margins: 2 * scaling
visible: modelData.isImage && modelData.imageSource
source: modelData.imageSource || ""
visible: modelData.isImage
// This property creates a dependency on the service's revision counter
readonly property int _rev: CliphistService.revision
// Fetches from the service's cache.
// The dependency on `_rev` ensures this binding is re-evaluated
// when the cache is updated by the service.
source: {
_rev
return CliphistService.getImageData(modelData.clipboardId) || ""
}
fillMode: Image.PreserveAspectFit
smooth: true
mipmap: true
@ -307,7 +326,6 @@ NPanel {
// Error fallback
onStatusChanged: {
if (status === Image.Error) {
// Fall back to icon
iconLoader.visible = true
imagePreview.visible = false
}
@ -319,7 +337,8 @@ NPanel {
id: iconLoader
anchors.fill: parent
anchors.margins: Style.marginXS * scaling
visible: !modelData.isImage || !modelData.imageSource || imagePreview.status === Image.Error
visible: !modelData.isImage || imagePreview.status === Image.Error
active: visible
sourceComponent: Component {
@ -391,23 +410,6 @@ NPanel {
Layout.fillWidth: true
visible: text !== ""
}
// // Show text preview for text items if space allows
// NText {
// visible: !modelData.isImage && modelData.fullText && modelData.fullText.length > 100
// text: {
// if (!modelData.fullText) return ""
// const preview = modelData.fullText.substring(0, 150).replace(/\n/g, " ")
// return preview + (modelData.fullText.length > 150 ? "..." : "")
// }
// font.pointSize: Style.fontSizeXS * scaling
// color: entry.isSelected ? Color.mOnTertiary : Color.mOnSurfaceVariant
// opacity: 0.7
// elide: Text.ElideRight
// maximumLineCount: 2
// wrapMode: Text.WordWrap
// Layout.fillWidth: true
// }
}
}

View file

@ -13,6 +13,17 @@ QtObject {
// Plugin capabilities
property bool handleSearch: false // Don't handle regular search
// Connections {
// target: CliphistService
// // Use the function syntax for on<SignalName>
// function onListCompleted() {
// // Only refresh if the clipboard plugin is active
// if (launcher && launcher.activePlugin === root) {
// launcher.updateResults()
// }
// }
// }
// Initialize plugin
function init() {
Logger.log("ClipboardPlugin", "Initialized")
@ -121,40 +132,22 @@ QtObject {
return results
}
// Helper: Format image clipboard entry with actual image data
// Helper: Format image clipboard entry
function formatImageEntry(item) {
const meta = parseImageMeta(item.preview)
// Get the actual image data/path from the clipboard service
// This assumes CliphistService provides either a path or base64 data
let imageData = null
// Try to get image data from the service
// Method 1: If the service provides a file path
if (item.imagePath) {
imageData = "file://" + item.imagePath
} // Method 2: If the service provides base64 data
else if (item.imageData) {
imageData = ClipHistService.getImageData(item.id)
// "data:" + (item.mime || "image/png") + ";base64," + item.imageData
} // Method 3: If we need to fetch it from the service
// else if (item.id) {
// // Some clipboard services might require fetching the image separately
// // This would depend on your CliphistService implementation
// imageData = CliphistService.getImageData ? CliphistService.getImageData(item.id) : null
// }
// The launcher's delegate will now be responsible for fetching the image data.
// This function's role is to provide the necessary metadata for that request.
return {
"name": meta ? `Image ${meta.w}×${meta.h}` : "Image",
"description": meta ? `${meta.fmt} ${meta.size}` : item.mime || "Image data",
"icon": "image",
"isImage": true,
"imageSource": imageData,
"imageWidth": meta ? meta.w : 0,
"imageHeight": meta ? meta.h : 0,
"clipboardId"// Add clipboard item ID for potential async loading
: item.id
"clipboardId"// Provide the ID and mime type for the delegate to make an async request
: item.id,
"mime": item.mime
}
}
@ -185,7 +178,7 @@ QtObject {
return {
"name": title,
"description": description,
"icon": "description",
"icon": "text-x-generic",
"isImage": false
}
}