Quality of life notification enhancement

This commit is contained in:
2026-08-11 22:44:55 +02:00
parent 90bbcd1072
commit da16a66d41

88
gui.py
View File

@@ -23,8 +23,8 @@ def normalize_file_path(path_str: str) -> str:
else: else:
path_str = urllib.parse.unquote(path_str[7:]) path_str = urllib.parse.unquote(path_str[7:])
return os.path.abspath(path_str) return os.path.abspath(path_str)
from PySide6.QtCore import Qt, QThread, QObject, Signal, QUrl, QFile, QIODevice from PySide6.QtCore import Qt, QThread, QObject, Signal, QUrl, QFile, QIODevice, QTimer, QMimeData
from PySide6.QtGui import QIcon, QAction, QPixmap, QPainter, QColor, QFont, QPen, QImage, QDesktopServices from PySide6.QtGui import QIcon, QAction, QPixmap, QPainter, QColor, QFont, QPen, QImage, QDesktopServices, QDrag
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QHttpMultiPart, QHttpPart, QNetworkReply, QLocalServer, QLocalSocket from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QHttpMultiPart, QHttpPart, QNetworkReply, QLocalServer, QLocalSocket
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QApplication, QSystemTrayIcon, QMenu, QDialog, QVBoxLayout, QHBoxLayout, QApplication, QSystemTrayIcon, QMenu, QDialog, QVBoxLayout, QHBoxLayout,
@@ -533,6 +533,39 @@ class SettingsDialog(QDialog):
else: else:
QMessageBox.critical(self, "Error", "Failed to save configuration file.") QMessageBox.critical(self, "Error", "Failed to save configuration file.")
# ==========================================
# Draggable File Label Widget
# ==========================================
class DraggableImageLabel(QLabel):
def __init__(self, file_path, parent=None):
super().__init__(parent)
self.file_path = file_path
self.drag_start_pos = None
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.drag_start_pos = event.pos()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if not (event.buttons() & Qt.MouseButton.LeftButton):
return
if self.drag_start_pos is None:
return
if (event.pos() - self.drag_start_pos).manhattanLength() < QApplication.startDragDistance():
return
if self.file_path and os.path.exists(self.file_path):
drag = QDrag(self)
mime_data = QMimeData()
mime_data.setUrls([QUrl.fromLocalFile(self.file_path)])
drag.setMimeData(mime_data)
if self.pixmap() and not self.pixmap().isNull():
drag.setPixmap(self.pixmap().scaled(80, 80, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation))
drag.exec(Qt.DropAction.CopyAction)
# ========================================== # ==========================================
# Upload Gallery Window & Components # Upload Gallery Window & Components
# ========================================== # ==========================================
@@ -559,10 +592,13 @@ class GalleryCard(QFrame):
layout.setContentsMargins(8, 8, 8, 8) layout.setContentsMargins(8, 8, 8, 8)
layout.setSpacing(6) layout.setSpacing(6)
# Thumbnail # Thumbnail with Drag & Drop support
self.thumb_label = QLabel() file_path = item.get("file_path", "")
self.thumb_label = DraggableImageLabel(file_path)
self.thumb_label.setFixedSize(160, 100) self.thumb_label.setFixedSize(160, 100)
self.thumb_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.thumb_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.thumb_label.setCursor(Qt.CursorShape.OpenHandCursor)
self.thumb_label.setToolTip("Click & Drag to drop file into other apps")
self.thumb_label.setStyleSheet("background-color: #121216; border-radius: 4px;") self.thumb_label.setStyleSheet("background-color: #121216; border-radius: 4px;")
thumb_path = item.get("thumbnail_path", "") thumb_path = item.get("thumbnail_path", "")
@@ -1238,14 +1274,48 @@ class SystemTrayApp(QObject):
if not config.get("enable_notifications", True): if not config.get("enable_notifications", True):
return return
thumb_path = generate_thumbnail(file_path) abs_path = os.path.abspath(file_path)
if thumb_path and os.path.exists(thumb_path): ext = os.path.splitext(abs_path)[1].lower()
body_text = f'<div><a href="{item_url}"><img src="file://{thumb_path}" height="80" style="height:80px; max-width:100%; object-fit:cover;" /></a></div><br/><br/><a href="{item_url}">{item_url}</a>' if ext in [".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".avif"]:
img_path = abs_path
else: else:
body_text = f'Upload Successful: <a href="{item_url}">{item_url}</a>' img_path = generate_thumbnail(abs_path) or abs_path
file_url = "file://" + abs_path
body_text = f'<a href="{item_url}">{item_url}</a>'
# Direct DBus Notification for Spectacle-style centered image preview & file drag-and-drop
try: try:
subprocess.run(["notify-send", "-a", "f0ckm", "Upload Successful", body_text], check=False) import dbus
bus = dbus.SessionBus()
notify_obj = bus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications')
notify_iface = dbus.Interface(notify_obj, 'org.freedesktop.Notifications')
hints = {
'urls': dbus.Array([dbus.String(file_url)], signature='s', variant_level=1),
'x-kde-urls': dbus.Array([dbus.String(file_url)], signature='s', variant_level=1),
'desktop-entry': dbus.String('', variant_level=1),
'x-kde-app-icon': dbus.String('', variant_level=1)
}
notify_iface.Notify('f0ckm Uploader', dbus.UInt32(0), '', '', body_text, [], hints, -1)
return
except Exception as e:
print(f"DBus notification error: {e}")
# Fallback to notify-send
try:
cmd = [
"notify-send",
"-a", "f0ckm Uploader",
"-h", f"string:x-kde-urls:{file_url}",
"-h", f"string:urls:{file_url}",
"-h", "string:desktop-entry:",
"-h", "string:x-kde-app-icon:",
"",
body_text
]
subprocess.run(cmd, check=False)
except Exception as e: except Exception as e:
print(f"Notification error: {e}") print(f"Notification error: {e}")