mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-03 17:39:42 +02:00
feat: Add customizable accent color setting
Introduce an accent color preference across the app: add a settings UI input (with restart note), persist it to AppState and config (read/write), and accept it in save_settings. Integrate the value into theming: compute a semi-transparent selection color, apply it to table selection styles, and pass it to qdarktheme as the primary color (including when window transparency is enabled). Also apply the accent to the tracker combo selection styling as a UI polish.
This commit is contained in:
@@ -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_container, transparent_window_checkbox = create_widget(QCheckBox, "Window Transparency (requires restart) (Linux/MacOS only): ")
|
||||||
transparent_window_checkbox.setChecked(state.window_transparency)
|
transparent_window_checkbox.setChecked(state.window_transparency)
|
||||||
transparent_window_checkbox.toggled.connect(lambda checked: setattr(state, 'window_transparency', checked))
|
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 Widget
|
||||||
api_url_container, api_url = create_widget(QLineEdit, "API Server URL: ", width=180, height=30)
|
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_offset.value(),
|
||||||
image_opacity.value(),
|
image_opacity.value(),
|
||||||
image_as_wallpaper=image_mode_checkbox.isChecked(),
|
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)
|
cancel_btn.clicked.connect(dialog.reject)
|
||||||
@@ -297,7 +303,7 @@ def settings_dialog(self):
|
|||||||
layout.addWidget(save_btn)
|
layout.addWidget(save_btn)
|
||||||
|
|
||||||
tabs = QtWidgets.QTabWidget()
|
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("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("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)
|
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)
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from PySide6.QtGui import QColor
|
from PySide6.QtGui import QColor
|
||||||
|
from utils.logging.logs import consoleLog
|
||||||
|
from utils.data.state import state
|
||||||
import darkdetect
|
import darkdetect
|
||||||
|
|
||||||
def _is_dark_mode():
|
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"):
|
def _table_stylesheet(view_type="QTableWidget"):
|
||||||
c = _theme_colors()
|
c = _theme_colors()
|
||||||
dark = _is_dark_mode()
|
dark = _is_dark_mode()
|
||||||
color_rule = "" if dark else f"color: {c['text']};"
|
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"""
|
return f"""
|
||||||
{view_type} {{
|
{view_type} {{
|
||||||
border: none;
|
border: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
{selection_color_rule}
|
||||||
{color_rule}
|
{color_rule}
|
||||||
}}
|
}}
|
||||||
{view_type}::item {{
|
{view_type}::item {{
|
||||||
@@ -45,7 +64,7 @@ def _table_stylesheet(view_type="QTableWidget"):
|
|||||||
{color_rule}
|
{color_rule}
|
||||||
}}
|
}}
|
||||||
{view_type}::item:selected {{
|
{view_type}::item:selected {{
|
||||||
background: {c["selected"]};
|
background: {selected_bg};
|
||||||
outline: none;
|
outline: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 1px solid {c["border"]};
|
border-bottom: 1px solid {c["border"]};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from interface.assets.base64_icons import settings_white_base64
|
|||||||
from interface.assets.base64_icons import settings_black_base64
|
from interface.assets.base64_icons import settings_black_base64
|
||||||
from interface.dialogs.downloadlist import download_list_update
|
from interface.dialogs.downloadlist import download_list_update
|
||||||
from interface.dialogs.update import get_version, UpdateDialog
|
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 utils.logging.logs import consoleLog, flush_log_buffer
|
||||||
from interface.dialogs.downloadmodel import DownloadModel
|
from interface.dialogs.downloadmodel import DownloadModel
|
||||||
from interface.dialogs.settings import settings_dialog
|
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.addItems(list(state.trackers.keys()))
|
||||||
self.tracker_list.setCursor(Qt.CursorShape.PointingHandCursor)
|
self.tracker_list.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||||
self.tracker_list.activated.connect(self.set_tracker)
|
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)
|
self.corner_layout.addWidget(self.tracker_list)
|
||||||
|
|
||||||
# Settings button
|
# Settings button
|
||||||
|
|||||||
+6
-2
@@ -21,13 +21,17 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
def run_gui(app):
|
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()
|
widget = MainWindow()
|
||||||
|
|
||||||
# Check OS for window transparency compatibility and apply
|
# Check OS for window transparency compatibility and apply
|
||||||
if state.window_transparency and platform.system() != "Windows":
|
if state.window_transparency and platform.system() != "Windows":
|
||||||
widget.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
|
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)
|
set_main_window(widget)
|
||||||
widget.show()
|
widget.show()
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ def create_config():
|
|||||||
"debug": str(state.debug),
|
"debug": str(state.debug),
|
||||||
"ignore_updates": str(state.ignore_updates),
|
"ignore_updates": str(state.ignore_updates),
|
||||||
"autoresume": str(state.autoresume),
|
"autoresume": str(state.autoresume),
|
||||||
"window_transparency": str(state.window_transparency)
|
"window_transparency": str(state.window_transparency),
|
||||||
|
"accent_color": str(state.accent_color)
|
||||||
}
|
}
|
||||||
|
|
||||||
config["Network"] = {
|
config["Network"] = {
|
||||||
@@ -84,6 +85,7 @@ def read_config():
|
|||||||
state.ignore_updates = config.getboolean("General", "ignore_updates", fallback=state.ignore_updates)
|
state.ignore_updates = config.getboolean("General", "ignore_updates", fallback=state.ignore_updates)
|
||||||
state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume)
|
state.autoresume = config.getboolean("General", "autoresume", fallback=state.autoresume)
|
||||||
state.window_transparency = config.getboolean("General", "window_transparency", fallback=state.window_transparency)
|
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
|
# Network
|
||||||
state.api_url = config.get("Network", "api_url", fallback=state.api_url)
|
state.api_url = config.get("Network", "api_url", fallback=state.api_url)
|
||||||
|
|||||||
@@ -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:
|
if apiurl is not None:
|
||||||
state.api_url = apiurl
|
state.api_url = apiurl
|
||||||
if download_path is not None:
|
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
|
state.image_as_wallpaper = image_as_wallpaper
|
||||||
if image_position is not None:
|
if image_position is not None:
|
||||||
state.image_position = image_position
|
state.image_position = image_position
|
||||||
|
if accent_color is not None:
|
||||||
|
state.accent_color = accent_color
|
||||||
|
|
||||||
update_settings() # Update LibTorrent Session Settings
|
update_settings() # Update LibTorrent Session Settings
|
||||||
consoleLog("Saved Settings")
|
consoleLog("Saved Settings")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class AppState(QObject):
|
|||||||
|
|
||||||
# GUI
|
# GUI
|
||||||
self.window_transparency: bool = False
|
self.window_transparency: bool = False
|
||||||
|
self.accent_color: str = ""
|
||||||
self.trackertable: QTableWidget
|
self.trackertable: QTableWidget
|
||||||
self.interfaces: List = []
|
self.interfaces: List = []
|
||||||
self.active_interfaces: List = []
|
self.active_interfaces: List = []
|
||||||
|
|||||||
Reference in New Issue
Block a user