diff --git a/src/interface/dialogs/settings.py b/src/interface/dialogs/settings.py index e71d1a4..2fb6652 100644 --- a/src/interface/dialogs/settings.py +++ b/src/interface/dialogs/settings.py @@ -88,6 +88,11 @@ def settings_dialog(self): transparent_window_container, transparent_window_checkbox = create_widget(QCheckBox, "Window Transparency (requires restart) (Linux/MacOS only): ") transparent_window_checkbox.setChecked(state.window_transparency) 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_input.setPlaceholderText("e.g. #fca7d7") + accent_color_input.setText(state.accent_color) # API URL Widget api_url_container, api_url = create_widget(QLineEdit, "API Server URL: ", width=180, height=30) @@ -289,7 +294,8 @@ def settings_dialog(self): image_offset.value(), image_opacity.value(), image_as_wallpaper=image_mode_checkbox.isChecked(), - image_position=image_position_combo.currentText() + image_position=image_position_combo.currentText(), + accent_color=accent_color_input.text().strip() ) cancel_btn.clicked.connect(dialog.reject) @@ -297,7 +303,7 @@ def settings_dialog(self): layout.addWidget(save_btn) tabs = QtWidgets.QTabWidget() - create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container], tabs=tabs, stretch=True) + create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container, accent_color_container], tabs=tabs, stretch=True) create_tab("Image", [enable_image_container, image_mode_container, image_position_container, image_width_container, image_offset_container, image_opacity_container], tabs=tabs, stretch=True) create_tab("Paths", [download_path_container, image_path_container], tabs=tabs, stretch=True) create_tab("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=tabs, stretch=True) diff --git a/src/interface/dialogs/theme.py b/src/interface/dialogs/theme.py index 18011f8..2b23212 100644 --- a/src/interface/dialogs/theme.py +++ b/src/interface/dialogs/theme.py @@ -1,4 +1,6 @@ from PySide6.QtGui import QColor +from utils.logging.logs import consoleLog +from utils.data.state import state import darkdetect def _is_dark_mode(): @@ -27,15 +29,32 @@ def _theme_colors(): } +def _accent_selection_color(alpha: float = 0.35) -> str: + if not state.accent_color: + return None + try: + hex_color = state.accent_color.lstrip('#') + if len(hex_color) != 6: + return None + r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16) + return f"rgba({r}, {g}, {b}, {alpha})" + except ValueError: + consoleLog(f"Invalid Color: {state.accent_color}") + return None + + def _table_stylesheet(view_type="QTableWidget"): c = _theme_colors() dark = _is_dark_mode() color_rule = "" if dark else f"color: {c['text']};" + selected_bg = _accent_selection_color() or c["selected"] + selection_color_rule = f"selection-background-color: {selected_bg};" if state.accent_color else "" return f""" {view_type} {{ border: none; outline: 0; font-size: 13px; + {selection_color_rule} {color_rule} }} {view_type}::item {{ @@ -45,7 +64,7 @@ def _table_stylesheet(view_type="QTableWidget"): {color_rule} }} {view_type}::item:selected {{ - background: {c["selected"]}; + background: {selected_bg}; outline: none; border: none; border-bottom: 1px solid {c["border"]}; diff --git a/src/interface/gui.py b/src/interface/gui.py index 1b048ef..cd1765a 100644 --- a/src/interface/gui.py +++ b/src/interface/gui.py @@ -6,6 +6,7 @@ from interface.assets.base64_icons import settings_white_base64 from interface.assets.base64_icons import settings_black_base64 from interface.dialogs.downloadlist import download_list_update from interface.dialogs.update import get_version, UpdateDialog +from interface.dialogs.theme import _accent_selection_color from utils.logging.logs import consoleLog, flush_log_buffer from interface.dialogs.downloadmodel import DownloadModel from interface.dialogs.settings import settings_dialog @@ -177,6 +178,10 @@ class MainWindow(QtWidgets.QMainWindow, QWidget): self.tracker_list.addItems(list(state.trackers.keys())) self.tracker_list.setCursor(Qt.CursorShape.PointingHandCursor) self.tracker_list.activated.connect(self.set_tracker) + if state.accent_color: + self.tracker_list.setStyleSheet( + f"QComboBox QAbstractItemView::item:selected {{ background: {_accent_selection_color()}; }}" + ) self.corner_layout.addWidget(self.tracker_list) # Settings button diff --git a/src/main.py b/src/main.py index 4b9ed24..5722bca 100644 --- a/src/main.py +++ b/src/main.py @@ -21,13 +21,17 @@ import time import sys def run_gui(app): - qdarktheme.setup_theme("auto") + custom_colors = {} + if state.accent_color: + custom_colors["primary"] = state.accent_color + qdarktheme.setup_theme("auto", custom_colors=custom_colors if custom_colors else None) widget = MainWindow() # Check OS for window transparency compatibility and apply if state.window_transparency and platform.system() != "Windows": widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) - qdarktheme.setup_theme("auto", custom_colors={"background": "#00000000"}) + transparent_colors = {"background": "#00000000", **custom_colors} + qdarktheme.setup_theme("auto", custom_colors=transparent_colors) set_main_window(widget) widget.show() diff --git a/src/utils/config/config.py b/src/utils/config/config.py index 15cf7a4..fc481fe 100644 --- a/src/utils/config/config.py +++ b/src/utils/config/config.py @@ -14,7 +14,8 @@ def create_config(): "debug": str(state.debug), "ignore_updates": str(state.ignore_updates), "autoresume": str(state.autoresume), - "window_transparency": str(state.window_transparency) + "window_transparency": str(state.window_transparency), + "accent_color": str(state.accent_color) } config["Network"] = { @@ -84,6 +85,7 @@ def read_config(): state.ignore_updates = config.getboolean("General", "ignore_updates", fallback=state.ignore_updates) state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume) state.window_transparency = config.getboolean("General", "window_transparency", fallback=state.window_transparency) + state.accent_color = config.get("General", "accent_color", fallback=state.accent_color) # Network state.api_url = config.get("Network", "api_url", fallback=state.api_url) diff --git a/src/utils/config/settings.py b/src/utils/config/settings.py index 2fc94a6..09df0da 100644 --- a/src/utils/config/settings.py +++ b/src/utils/config/settings.py @@ -5,7 +5,7 @@ from utils.data.state import state -def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None, image_width=None, image_offset=None, image_opacity=None, image_as_wallpaper=None, image_position=None): +def save_settings(close=lambda: None, apiurl=None, download_path=None, down_speed_limit=None, up_speed_limit=None, image_path=None, autoresume=None, max_connections=None, max_downloads=None, bound_interface=None, image_width=None, image_offset=None, image_opacity=None, image_as_wallpaper=None, image_position=None, accent_color=None): if apiurl is not None: state.api_url = apiurl if download_path is not None: @@ -34,6 +34,8 @@ def save_settings(close=lambda: None, apiurl=None, download_path=None, down_spee state.image_as_wallpaper = image_as_wallpaper if image_position is not None: state.image_position = image_position + if accent_color is not None: + state.accent_color = accent_color update_settings() # Update LibTorrent Session Settings consoleLog("Saved Settings") diff --git a/src/utils/data/state.py b/src/utils/data/state.py index 75ed4eb..1004d0a 100644 --- a/src/utils/data/state.py +++ b/src/utils/data/state.py @@ -25,6 +25,7 @@ class AppState(QObject): # GUI self.window_transparency: bool = False + self.accent_color: str = "" self.trackertable: QTableWidget self.interfaces: List = [] self.active_interfaces: List = []