mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
224 lines
6.8 KiB
QML
224 lines
6.8 KiB
QML
pragma ComponentBehavior: Bound
|
||
|
||
import QtQuick
|
||
import Quickshell
|
||
import Quickshell.Hyprland
|
||
import Quickshell.Io
|
||
import qs.Common
|
||
import qs.Modals.Common
|
||
import qs.Services
|
||
|
||
DankModal {
|
||
id: clipboardHistoryModal
|
||
|
||
layerNamespace: "dms:clipboard"
|
||
|
||
HyprlandFocusGrab {
|
||
windows: [clipboardHistoryModal]
|
||
active: CompositorService.isHyprland && clipboardHistoryModal.shouldHaveFocus
|
||
}
|
||
|
||
property int totalCount: 0
|
||
property var clipboardEntries: []
|
||
property string searchText: ""
|
||
property int selectedIndex: 0
|
||
property bool keyboardNavigationActive: false
|
||
property bool showKeyboardHints: false
|
||
property Component clipboardContent
|
||
property int activeImageLoads: 0
|
||
readonly property int maxConcurrentLoads: 3
|
||
|
||
function updateFilteredModel() {
|
||
filteredClipboardModel.clear();
|
||
for (var i = 0; i < clipboardModel.count; i++) {
|
||
const entry = clipboardModel.get(i).entry;
|
||
if (searchText.trim().length === 0) {
|
||
filteredClipboardModel.append({
|
||
"entry": entry
|
||
});
|
||
} else {
|
||
const content = getEntryPreview(entry).toLowerCase();
|
||
if (content.includes(searchText.toLowerCase())) {
|
||
filteredClipboardModel.append({
|
||
"entry": entry
|
||
});
|
||
}
|
||
}
|
||
}
|
||
clipboardHistoryModal.totalCount = filteredClipboardModel.count;
|
||
if (filteredClipboardModel.count === 0) {
|
||
keyboardNavigationActive = false;
|
||
selectedIndex = 0;
|
||
} else if (selectedIndex >= filteredClipboardModel.count) {
|
||
selectedIndex = filteredClipboardModel.count - 1;
|
||
}
|
||
}
|
||
|
||
function toggle() {
|
||
if (shouldBeVisible) {
|
||
hide();
|
||
} else {
|
||
show();
|
||
}
|
||
}
|
||
|
||
function show() {
|
||
open();
|
||
clipboardHistoryModal.searchText = "";
|
||
clipboardHistoryModal.activeImageLoads = 0;
|
||
clipboardHistoryModal.shouldHaveFocus = true;
|
||
refreshClipboard();
|
||
keyboardController.reset();
|
||
|
||
Qt.callLater(function () {
|
||
if (contentLoader.item && contentLoader.item.searchField) {
|
||
contentLoader.item.searchField.text = "";
|
||
contentLoader.item.searchField.forceActiveFocus();
|
||
}
|
||
});
|
||
}
|
||
|
||
function hide() {
|
||
close();
|
||
clipboardHistoryModal.searchText = "";
|
||
clipboardHistoryModal.activeImageLoads = 0;
|
||
updateFilteredModel();
|
||
keyboardController.reset();
|
||
cleanupTempFiles();
|
||
}
|
||
|
||
function cleanupTempFiles() {
|
||
Quickshell.execDetached(["sh", "-c", "rm -f /tmp/clipboard_*.png"]);
|
||
}
|
||
|
||
function refreshClipboard() {
|
||
clipboardProcesses.refresh();
|
||
}
|
||
|
||
function copyEntry(entry) {
|
||
const entryId = entry.split('\t')[0];
|
||
Quickshell.execDetached(["sh", "-c", `cliphist decode ${entryId} | wl-copy`]);
|
||
ToastService.showInfo(I18n.tr("Copied to clipboard"));
|
||
hide();
|
||
}
|
||
|
||
function deleteEntry(entry) {
|
||
clipboardProcesses.deleteEntry(entry);
|
||
}
|
||
|
||
function clearAll() {
|
||
clipboardProcesses.clearAll();
|
||
}
|
||
|
||
function getEntryPreview(entry) {
|
||
let content = entry.replace(/^\s*\d+\s+/, "");
|
||
if (content.includes("image/") || content.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(content)) {
|
||
const dimensionMatch = content.match(/(\d+)x(\d+)/);
|
||
if (dimensionMatch) {
|
||
return `Image ${dimensionMatch[1]}×${dimensionMatch[2]}`;
|
||
}
|
||
const typeMatch = content.match(/\b(png|jpg|jpeg|gif|bmp|webp)\b/i);
|
||
if (typeMatch) {
|
||
return `Image (${typeMatch[1].toUpperCase()})`;
|
||
}
|
||
return "Image";
|
||
}
|
||
if (content.length > ClipboardConstants.previewLength) {
|
||
return content.substring(0, ClipboardConstants.previewLength) + "...";
|
||
}
|
||
return content;
|
||
}
|
||
|
||
function getEntryType(entry) {
|
||
if (entry.includes("image/") || entry.includes("binary data") || /\.(png|jpg|jpeg|gif|bmp|webp)/i.test(entry) || /\b(png|jpg|jpeg|gif|bmp|webp)\b/i.test(entry)) {
|
||
return "image";
|
||
}
|
||
if (entry.length > ClipboardConstants.longTextThreshold) {
|
||
return "long_text";
|
||
}
|
||
return "text";
|
||
}
|
||
|
||
visible: false
|
||
modalWidth: ClipboardConstants.modalWidth
|
||
modalHeight: ClipboardConstants.modalHeight
|
||
backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||
cornerRadius: Theme.cornerRadius
|
||
borderColor: Theme.outlineMedium
|
||
borderWidth: 1
|
||
enableShadow: true
|
||
onBackgroundClicked: hide()
|
||
modalFocusScope.Keys.onPressed: function (event) {
|
||
keyboardController.handleKey(event);
|
||
}
|
||
content: clipboardContent
|
||
|
||
ClipboardKeyboardController {
|
||
id: keyboardController
|
||
modal: clipboardHistoryModal
|
||
}
|
||
|
||
ConfirmModal {
|
||
id: clearConfirmDialog
|
||
confirmButtonText: I18n.tr("Clear All")
|
||
confirmButtonColor: Theme.primary
|
||
onVisibleChanged: {
|
||
if (visible) {
|
||
clipboardHistoryModal.shouldHaveFocus = false;
|
||
} else if (clipboardHistoryModal.shouldBeVisible) {
|
||
clipboardHistoryModal.shouldHaveFocus = true;
|
||
clipboardHistoryModal.modalFocusScope.forceActiveFocus();
|
||
if (clipboardHistoryModal.contentLoader.item && clipboardHistoryModal.contentLoader.item.searchField) {
|
||
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
property alias filteredClipboardModel: filteredClipboardModel
|
||
property alias clipboardModel: clipboardModel
|
||
property var confirmDialog: clearConfirmDialog
|
||
|
||
ListModel {
|
||
id: clipboardModel
|
||
}
|
||
|
||
ListModel {
|
||
id: filteredClipboardModel
|
||
}
|
||
|
||
ClipboardProcesses {
|
||
id: clipboardProcesses
|
||
modal: clipboardHistoryModal
|
||
clipboardModel: clipboardModel
|
||
filteredClipboardModel: filteredClipboardModel
|
||
}
|
||
|
||
IpcHandler {
|
||
function open(): string {
|
||
clipboardHistoryModal.show();
|
||
return "CLIPBOARD_OPEN_SUCCESS";
|
||
}
|
||
|
||
function close(): string {
|
||
clipboardHistoryModal.hide();
|
||
return "CLIPBOARD_CLOSE_SUCCESS";
|
||
}
|
||
|
||
function toggle(): string {
|
||
clipboardHistoryModal.toggle();
|
||
return "CLIPBOARD_TOGGLE_SUCCESS";
|
||
}
|
||
|
||
target: "clipboard"
|
||
}
|
||
|
||
clipboardContent: Component {
|
||
ClipboardContent {
|
||
modal: clipboardHistoryModal
|
||
filteredModel: filteredClipboardModel
|
||
clearConfirmDialog: clipboardHistoryModal.confirmDialog
|
||
}
|
||
}
|
||
}
|