1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00
Files
DankMaterialShell/quickshell/Modals/Clipboard/ClipboardHistoryModal.qml

213 lines
6.6 KiB
QML

pragma ComponentBehavior: Bound
import QtQuick
import Quickshell.Hyprland
import qs.Common
import qs.Modals.Common
import qs.Services
DankModal {
id: clipboardHistoryModal
layerNamespace: "dms:clipboard"
HyprlandFocusGrab {
windows: [clipboardHistoryModal.contentWindow]
active: clipboardHistoryModal.useHyprlandFocusGrab && 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
readonly property bool clipboardAvailable: DMSService.isConnected && DMSService.capabilities.includes("clipboard")
function updateFilteredModel() {
const query = searchText.trim();
if (query.length === 0) {
clipboardEntries = internalEntries;
} else {
const lowerQuery = query.toLowerCase();
clipboardEntries = internalEntries.filter(entry => entry.preview.toLowerCase().includes(lowerQuery));
}
totalCount = clipboardEntries.length;
if (clipboardEntries.length === 0) {
keyboardNavigationActive = false;
selectedIndex = 0;
} else if (selectedIndex >= clipboardEntries.length) {
selectedIndex = clipboardEntries.length - 1;
}
}
property var internalEntries: []
function toggle() {
if (shouldBeVisible) {
hide();
} else {
show();
}
}
function show() {
if (!clipboardAvailable) {
ToastService.showError(I18n.tr("Clipboard service not available"));
return;
}
open();
searchText = "";
activeImageLoads = 0;
shouldHaveFocus = true;
refreshClipboard();
keyboardController.reset();
Qt.callLater(function () {
if (contentLoader.item?.searchField) {
contentLoader.item.searchField.text = "";
contentLoader.item.searchField.forceActiveFocus();
}
});
}
function hide() {
close();
searchText = "";
activeImageLoads = 0;
internalEntries = [];
clipboardEntries = [];
keyboardController.reset();
}
function refreshClipboard() {
DMSService.sendRequest("clipboard.getHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to get history:", response.error);
return;
}
internalEntries = response.result || [];
updateFilteredModel();
});
}
function copyEntry(entry) {
DMSService.sendRequest("clipboard.getEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
ToastService.showError(I18n.tr("Failed to copy entry"));
return;
}
const fullEntry = response.result;
if (fullEntry.isImage) {
ToastService.showInfo(I18n.tr("Image copied to clipboard"));
} else {
DMSService.sendRequest("clipboard.copy", {
"text": fullEntry.data
}, function (copyResponse) {
if (copyResponse.error) {
ToastService.showError(I18n.tr("Failed to copy"));
return;
}
ToastService.showInfo(I18n.tr("Copied to clipboard"));
});
}
hide();
});
}
function deleteEntry(entry) {
DMSService.sendRequest("clipboard.deleteEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to delete entry:", response.error);
return;
}
internalEntries = internalEntries.filter(e => e.id !== entry.id);
updateFilteredModel();
if (clipboardEntries.length === 0) {
keyboardNavigationActive = false;
selectedIndex = 0;
} else if (selectedIndex >= clipboardEntries.length) {
selectedIndex = clipboardEntries.length - 1;
}
});
}
function clearAll() {
DMSService.sendRequest("clipboard.clearHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to clear history:", response.error);
return;
}
internalEntries = [];
clipboardEntries = [];
totalCount = 0;
});
}
function getEntryPreview(entry) {
return entry.preview || "";
}
function getEntryType(entry) {
if (entry.isImage) {
return "image";
}
if (entry.size > 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?.searchField) {
clipboardHistoryModal.contentLoader.item.searchField.forceActiveFocus();
}
}
}
}
property var confirmDialog: clearConfirmDialog
clipboardContent: Component {
ClipboardContent {
modal: clipboardHistoryModal
clearConfirmDialog: clipboardHistoryModal.confirmDialog
}
}
}