Files
noctalia-shell/Modules/Panels/Launcher/ClipboardPreview.qml
T
2026-02-20 21:11:06 -05:00

196 lines
5.7 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services.Keyboard
import qs.Widgets
Item {
id: previewPanel
property var currentItem: null
property string fullContent: ""
property string imageDataUrl: ""
property bool loadingFullContent: false
property bool isImageContent: false
implicitHeight: contentArea.implicitHeight + Style.margin2L
function loadContent() {
if (!currentItem || !currentItem.clipboardId)
return;
if (isImageContent) {
// For images, check cache first then decode
imageDataUrl = ClipboardService.getImageData(currentItem.clipboardId) || "";
loadingFullContent = !imageDataUrl;
if (!imageDataUrl && currentItem.mime) {
ClipboardService.decodeToDataUrl(currentItem.clipboardId, currentItem.mime, null);
}
} else {
// For text, check sync cache first
const cached = ClipboardService.getContent(currentItem.clipboardId);
if (cached) {
fullContent = cached;
loadingFullContent = false;
} else {
// Show preview while loading full content
fullContent = currentItem.preview || "";
loadingFullContent = true;
// Async decode as fallback
var requestedId = currentItem.clipboardId;
ClipboardService.decode(requestedId, function (content) {
if (!previewPanel || !previewPanel.currentItem) {
return;
}
if (previewPanel.currentItem.clipboardId === requestedId) {
var trimmed = content ? content.trim() : "";
if (trimmed !== "") {
previewPanel.fullContent = trimmed;
}
previewPanel.loadingFullContent = false;
}
});
}
}
}
Connections {
target: previewPanel
function onCurrentItemChanged() {
fullContent = "";
imageDataUrl = "";
loadingFullContent = false;
isImageContent = currentItem && currentItem.isImage;
if (currentItem && currentItem.clipboardId) {
loadContent();
}
}
}
readonly property int _rev: ClipboardService.revision
on_RevChanged: {
// When cache updates, try to load content if we're still showing loading or preview
if (currentItem && currentItem.clipboardId && !isImageContent && loadingFullContent) {
const cached = ClipboardService.getContent(currentItem.clipboardId);
if (cached) {
fullContent = cached;
loadingFullContent = false;
}
}
}
Timer {
id: imageUpdateTimer
interval: 200
running: currentItem && currentItem.isImage && imageDataUrl === ""
repeat: currentItem && currentItem.isImage && imageDataUrl === ""
onTriggered: {
if (currentItem && currentItem.clipboardId) {
const newData = ClipboardService.getImageData(currentItem.clipboardId) || "";
if (newData !== imageDataUrl) {
imageDataUrl = newData;
if (newData) {
loadingFullContent = false;
}
}
}
}
}
Item {
id: contentArea
anchors.fill: parent
anchors.margins: Style.marginS
BusyIndicator {
anchors.centerIn: parent
running: loadingFullContent
visible: loadingFullContent
width: Style.baseWidgetSize
height: width
}
// Preview for image entry
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
spacing: Style.marginS
visible: isImageContent && !loadingFullContent && imageDataUrl !== ""
NImageRounded {
id: previewImage
Layout.fillWidth: true
Layout.fillHeight: true
radius: Style.marginS
imagePath: imageDataUrl
imageFillMode: Image.PreserveAspectFit
}
NDivider {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginS
}
NText {
Layout.fillWidth: true
text: {
const meta = ClipboardService.parseImageMeta(currentItem?.preview);
if (meta)
return `${meta.fmt} ${meta.w}×${meta.h} ${meta.size}`;
// Fallback to basic info
const format = (currentItem?.mime || "image").split("/")[1]?.toUpperCase() || "Image";
return `${format} ${previewImage.implicitWidth}×${previewImage.implicitHeight}`;
}
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
}
}
// Preview for text entry
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginS
spacing: Style.marginS
visible: !isImageContent && !loadingFullContent
NScrollView {
id: clipboardScrollView
Layout.fillWidth: true
Layout.fillHeight: true
horizontalPolicy: Settings.data.appLauncher.clipboardWrapText ? ScrollBar.AlwaysOff : ScrollBar.AsNeeded
NText {
text: fullContent
width: Settings.data.appLauncher.clipboardWrapText ? clipboardScrollView.availableWidth : implicitWidth
wrapMode: Settings.data.appLauncher.clipboardWrapText ? Text.Wrap : Text.NoWrap
textFormat: Text.PlainText
font.pointSize: Style.fontSizeM
font.family: Settings.data.ui.fontFixed
color: Color.mOnSurface
}
}
NDivider {
Layout.fillWidth: true
Layout.bottomMargin: Style.marginS
}
NText {
Layout.fillWidth: true
visible: fullContent.length > 0
text: {
const chars = fullContent.length;
const words = fullContent.split(/\s+/).filter(w => w.length > 0).length;
const lines = fullContent.split('\n').length;
return `${chars} chars, ${words} words, ${lines} lines`;
}
pointSize: Style.fontSizeS
color: Color.mOnSurfaceVariant
}
}
}
}