Merge branch 'main' into feat/contextmenu

This commit is contained in:
shayaa
2026-04-10 00:10:36 +02:00
committed by GitHub
24 changed files with 425 additions and 188 deletions
+11 -7
View File
@@ -53,10 +53,12 @@ class ContextMenu_Downloads:
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
if row < 0 or row >= len(state.active_downloads):
return
magnet_link = list(state.active_downloads.keys())[row]
magnetdl = state.active_downloads[magnet_link]
with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads):
return
keys = list(state.active_downloads.keys())
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link]
download_path = magnetdl.save_path()
if download_path and os.path.exists(download_path):
if platform.system() == "Windows":
@@ -70,9 +72,11 @@ class ContextMenu_Downloads:
if not hasattr(self, '_context_menu_row'):
return
row = self._context_menu_row
if row < 0 or row >= len(state.active_downloads):
return
magnet_link = list(state.active_downloads.keys())[row]
with state.downloads_lock:
if row < 0 or row >= len(state.active_downloads):
return
keys = list(state.active_downloads.keys())
magnet_link = keys[row]
clipboard = QtWidgets.QApplication.clipboard()
clipboard.setText(magnet_link)
+4 -2
View File
@@ -34,7 +34,8 @@ class DownloadModel(QAbstractTableModel):
return None
try:
magnet_link = list(state.active_downloads.keys())[index.row()]
keys = list(state.active_downloads.keys())
magnet_link = keys[index.row()]
magnetdl = state.active_downloads[magnet_link]
status = magnetdl.status()
except (IndexError, KeyError, RuntimeError):
@@ -101,7 +102,8 @@ class DownloadModel(QAbstractTableModel):
if row >= len(state.active_downloads) or row < 0:
return
try:
magnet_link = list(state.active_downloads.keys())[row]
keys = list(state.active_downloads.keys())
magnet_link = keys[row]
magnetdl = state.active_downloads[magnet_link]
status = magnetdl.status()
except (IndexError, KeyError, RuntimeError):
+68 -4
View File
@@ -1,5 +1,5 @@
from PySide6.QtWidgets import QLabel, QGraphicsOpacityEffect, QStackedWidget
from PySide6.QtCore import Qt, QSize, QEvent, QObject
from PySide6.QtCore import Qt, QSize, QEvent, QObject, QTimer
from PySide6.QtGui import QImage, QPixmap
from utils.data.state import state
import darkdetect
@@ -17,8 +17,15 @@ class Image(QObject):
self.overlay_label.setGraphicsEffect(self.opacity_effect)
self._current_image_path = None
self._cached_qimage = None
self._wallpaper_active = False
self._original_stylesheets = {}
self._resize_timer = QTimer(self)
self._resize_timer.setSingleShot(True)
self._resize_timer.setInterval(100)
self._resize_timer.timeout.connect(self._on_resize_finished)
parent.installEventFilter(self)
state.image_changed.connect(self.update_image_overlay)
@@ -28,9 +35,14 @@ class Image(QObject):
def eventFilter(self, obj, event):
if obj == self.application and event.type() == QEvent.Type.Resize:
if self._current_image_path:
self._load_and_display(self._current_image_path)
self._apply_layout_fast()
self._resize_timer.start()
return False
def _on_resize_finished(self):
if self._current_image_path:
self._apply_layout()
def _set_wallpaper_transparency(self, enabled):
if self._wallpaper_active == enabled:
return
@@ -117,12 +129,62 @@ class Image(QObject):
self._set_wallpaper_transparency(False)
return
self._current_image_path = image_path
# Only reload from disk when the path actually changed
if self._cached_qimage is None or image_path != getattr(self, '_cached_path', None):
image = QImage(image_path)
if image.isNull():
self.overlay_label.hide()
self._cached_qimage = None
return
self._cached_qimage = image
self._cached_path = image_path
self._apply_layout()
def _apply_layout_fast(self):
if self._cached_qimage is None or not state.image_enabled:
return
parent = self.application
image = QImage(image_path)
if image.isNull():
image = self._cached_qimage
if getattr(state, "image_as_wallpaper", False):
scaled = image.scaled(
parent.size(),
Qt.AspectRatioMode.KeepAspectRatioByExpanding,
Qt.TransformationMode.FastTransformation
)
cx = (scaled.width() - parent.width()) // 2
cy = (scaled.height() - parent.height()) // 2
cropped = scaled.copy(cx, cy, parent.width(), parent.height())
self.overlay_label.setPixmap(QPixmap.fromImage(cropped))
self.overlay_label.setGeometry(0, 0, parent.width(), parent.height())
else:
w = self.overlay_label.width()
h = self.overlay_label.height()
pos = state.image_position
off = int(state.image_offset)
if pos == "top-left":
x, y = off, off
elif pos == "top-right":
x, y = parent.width() - w - off, off
elif pos == "bottom-left":
x, y = off, parent.height() - h - off
elif pos == "center":
x, y = (parent.width() - w) // 2, (parent.height() - h) // 2
else:
x, y = parent.width() - w - off, parent.height() - h - off
self.overlay_label.move(x, y)
def _apply_layout(self):
if self._cached_qimage is None or not state.image_enabled:
self.overlay_label.hide()
return
parent = self.application
image = self._cached_qimage
if getattr(state, "image_as_wallpaper", False):
scaled_image = image.scaled(
parent.size(),
@@ -184,4 +246,6 @@ class Image(QObject):
self.overlay_label.show()
def update_image_overlay(self, new_image_path):
if new_image_path != getattr(self, '_cached_path', None):
self._cached_qimage = None
self._load_and_display(new_image_path)
+34 -2
View File
@@ -1,9 +1,11 @@
from interface.utils.tabhelper import create_tab
from interface.dialogs.theme import _accent_selection_color
from utils.config.settings import save_settings
from interface.utils.svghelper import svg_icon
from utils.logging.logs import consoleLog
from PySide6.QtCore import Qt, QSize
from utils.data.state import state
from PySide6.QtGui import QColor
from PySide6 import QtWidgets
from PySide6.QtWidgets import (
QLineEdit,
@@ -13,9 +15,10 @@ from PySide6.QtWidgets import (
QDialog,
QLabel,
QHBoxLayout,
QSpinBox, QSlider, QCheckBox,
QSpinBox, QSlider, QCheckBox,
QFileDialog,
QComboBox,
QColorDialog
)
import platform
@@ -91,9 +94,31 @@ def settings_dialog(self):
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
# Accent Color
accent_color_container, accent_color_input = create_widget(QLineEdit, "Accent Color (requires restart): ", width=180, height=30)
accent_color_container, accent_color_input = create_widget(QLineEdit, "Accent Color: ", width=180, height=30)
accent_color_button = QPushButton("")
accent_color_button.setFixedSize(21, 21)
accent_color_button.setCursor(Qt.CursorShape.PointingHandCursor)
def update_accent_button_color(color_str):
c = color_str.strip()
accent_color_button.setStyleSheet(
f"QPushButton {{ background-color: {c if c else 'transparent'}; border: 1px solid palette(mid); border-radius: 3px; }}"
)
def browse_accent_color():
current = accent_color_input.text().strip()
initial = QColor(current) if current else QColor()
color = QColorDialog.getColor(initial, dialog, "Select Accent Color")
if color.isValid():
accent_color_input.setText(color.name())
accent_color_input.textChanged.connect(update_accent_button_color)
accent_color_button.clicked.connect(browse_accent_color)
accent_color_input.setPlaceholderText("e.g. #fca7d7")
accent_color_input.setText(state.accent_color)
update_accent_button_color(state.accent_color)
accent_color_container.layout().insertWidget(1, accent_color_button)
# API URL Widget
api_url_container, api_url = create_widget(QLineEdit, "API Server URL: ", width=180, height=30)
@@ -305,6 +330,13 @@ def settings_dialog(self):
accent_color=accent_color_input.text().strip(),
close_to_tray=close_to_tray_checkbox.isChecked()
)
color = _accent_selection_color()
if color:
self.tracker_list.setStyleSheet(
f"QComboBox QAbstractItemView::item:selected {{ background: {color}; }}"
)
else:
self.tracker_list.setStyleSheet("")
cancel_btn.clicked.connect(dialog.reject)
layout.addWidget(cancel_btn)
+9
View File
@@ -317,6 +317,15 @@ class MainWindow(QtWidgets.QMainWindow, QWidget):
def _on_log_signal(self, text):
if hasattr(self, 'consoleLog'):
self.consoleLog.append(text)
# Delete lines if exceeding 500
doc = self.consoleLog.document()
if doc.blockCount() > 500:
cursor = self.consoleLog.textCursor()
cursor.movePosition(cursor.MoveOperation.Start)
cursor.movePosition(cursor.MoveOperation.Down, cursor.MoveMode.KeepAnchor, doc.blockCount() - 500)
cursor.removeSelectedText()
cursor.deleteChar()
self.consoleLog.verticalScrollBar().setValue(
self.consoleLog.verticalScrollBar().maximum()
)