mirror of
https://github.com/KeksPirates/SoftwareManager.git
synced 2026-08-04 17:59:43 +02:00
feat: Add image position presets and sliders
Introduce image positioning presets and UI improvements for image overlay control. image.py: compute overlay coordinates for top-left/top-right/bottom-left/bottom-right/center positions and respect corner offset and opacity. settings.py: replace spinboxes with horizontal sliders (with value labels and custom style), add Wallpaper Mode checkbox and Position combo, wire controls to state and include them in the settings tab; ensure dialog rollback restores new temp settings. config/config.py & utils/settings.py: persist and load new image_as_wallpaper and image_position settings and extend save_settings to accept them. data/state.py: add new state fields and properties for image_position, custom position flags and x/y coordinates; change default image_as_wallpaper to false. These changes enable preset positioning, improved UX for sizing/offset/opacity, and persistence of new image options.
This commit is contained in:
@@ -163,8 +163,20 @@ class Image(QObject):
|
||||
self.overlay_label.adjustSize()
|
||||
self.overlay_label.raise_()
|
||||
|
||||
x = parent.width() - self.overlay_label.width() - int(state.image_offset)
|
||||
y = parent.height() - self.overlay_label.height() - int(state.image_offset)
|
||||
pos = state.image_position
|
||||
off = int(state.image_offset)
|
||||
w = self.overlay_label.width()
|
||||
h = self.overlay_label.height()
|
||||
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: # bottom-right (default)
|
||||
x, y = parent.width() - w - off, parent.height() - h - off
|
||||
|
||||
self.opacity_effect.setOpacity(state.image_opacity / 100)
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ from PySide6.QtWidgets import (
|
||||
QDialog,
|
||||
QLabel,
|
||||
QHBoxLayout,
|
||||
QSpinBox,
|
||||
QCheckBox,
|
||||
QSpinBox, QSlider, QCheckBox,
|
||||
QFileDialog,
|
||||
QComboBox,
|
||||
)
|
||||
@@ -29,6 +28,8 @@ def settings_dialog(self):
|
||||
temp_image_offset = state.image_offset
|
||||
temp_image_opacity = state.image_opacity
|
||||
temp_image_enabled = state.image_enabled
|
||||
temp_image_as_wallpaper = state.image_as_wallpaper
|
||||
temp_image_position = state.image_position
|
||||
|
||||
def create_widget(widget_type, label_text, **kwargs):
|
||||
container = QWidget()
|
||||
@@ -147,25 +148,80 @@ def settings_dialog(self):
|
||||
enable_image_checkbox.setChecked(state.image_enabled)
|
||||
enable_image_checkbox.toggled.connect(lambda checked: setattr(state, 'image_enabled', checked))
|
||||
|
||||
# Image Width SpinBox
|
||||
image_width_container, image_width = create_widget(QSpinBox, "Image Width: ", width=180, height=30)
|
||||
# Image Mode Checkbox
|
||||
image_mode_container, image_mode_checkbox = create_widget(QCheckBox, "Wallpaper Mode: ")
|
||||
image_mode_checkbox.setChecked(state.image_as_wallpaper)
|
||||
image_mode_checkbox.toggled.connect(lambda checked: setattr(state, 'image_as_wallpaper', checked))
|
||||
|
||||
# Image Position Preset
|
||||
positions = ["bottom-right", "bottom-left", "top-right", "top-left", "center"]
|
||||
image_position_container, image_position_combo = create_widget(QComboBox, "Position: ", width=180, height=30)
|
||||
image_position_combo.addItems(positions)
|
||||
idx = image_position_combo.findText(state.image_position)
|
||||
image_position_combo.setCurrentIndex(idx if idx >= 0 else 0)
|
||||
image_position_combo.currentTextChanged.connect(lambda val: setattr(state, 'image_position', val))
|
||||
|
||||
_slider_style = """
|
||||
QSlider::groove:horizontal {
|
||||
height: 3px;
|
||||
background: palette(mid);
|
||||
border-radius: 1px;
|
||||
}
|
||||
QSlider::handle:horizontal {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: -4px 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
"""
|
||||
|
||||
# Image Width Slider
|
||||
image_width_container = QWidget()
|
||||
image_width_layout = QHBoxLayout(image_width_container)
|
||||
image_width_layout.addWidget(QLabel("Image Width: "))
|
||||
image_width = QSlider(Qt.Orientation.Horizontal)
|
||||
image_width.setFixedWidth(180)
|
||||
image_width.setStyleSheet(_slider_style)
|
||||
image_width.setMinimum(0)
|
||||
image_width.setMaximum(2500)
|
||||
image_width.setValue(state.image_width)
|
||||
image_width.valueChanged.connect(lambda val: setattr(state, 'image_width', val))
|
||||
image_width_val = QLabel(str(state.image_width))
|
||||
image_width_val.setFixedWidth(36)
|
||||
image_width.valueChanged.connect(lambda val: (setattr(state, 'image_width', val), image_width_val.setText(str(val))))
|
||||
image_width_layout.addWidget(image_width_val)
|
||||
image_width_layout.addWidget(image_width)
|
||||
|
||||
# Image Offset SpinBox
|
||||
image_offset_container, image_offset = create_widget(QSpinBox, "Corner Offset: ", width=180, height=30)
|
||||
# Image Offset Slider
|
||||
image_offset_container = QWidget()
|
||||
image_offset_layout = QHBoxLayout(image_offset_container)
|
||||
image_offset_layout.addWidget(QLabel("Corner Offset: "))
|
||||
image_offset = QSlider(Qt.Orientation.Horizontal)
|
||||
image_offset.setFixedWidth(180)
|
||||
image_offset.setStyleSheet(_slider_style)
|
||||
image_offset.setMinimum(0)
|
||||
image_offset.setMaximum(500)
|
||||
image_offset.setValue(state.image_offset)
|
||||
image_offset.valueChanged.connect(lambda val: setattr(state, 'image_offset', val))
|
||||
image_offset_val = QLabel(str(state.image_offset))
|
||||
image_offset_val.setFixedWidth(36)
|
||||
image_offset.valueChanged.connect(lambda val: (setattr(state, 'image_offset', val), image_offset_val.setText(str(val))))
|
||||
image_offset_layout.addWidget(image_offset_val)
|
||||
image_offset_layout.addWidget(image_offset)
|
||||
|
||||
# Image Opacity SpinBox
|
||||
image_opacity_container, image_opacity = create_widget(QSpinBox, "Image Opacity: ", width=180, height=30)
|
||||
# Image Opacity Slider
|
||||
image_opacity_container = QWidget()
|
||||
image_opacity_layout = QHBoxLayout(image_opacity_container)
|
||||
image_opacity_layout.addWidget(QLabel("Image Opacity: "))
|
||||
image_opacity = QSlider(Qt.Orientation.Horizontal)
|
||||
image_opacity.setFixedWidth(180)
|
||||
image_opacity.setStyleSheet(_slider_style)
|
||||
image_opacity.setMinimum(0)
|
||||
image_opacity.setMaximum(100)
|
||||
image_opacity.setValue(state.image_opacity)
|
||||
image_opacity.valueChanged.connect(lambda val: setattr(state, 'image_opacity', val))
|
||||
image_opacity_val = QLabel(str(state.image_opacity))
|
||||
image_opacity_val.setFixedWidth(36)
|
||||
image_opacity.valueChanged.connect(lambda val: (setattr(state, 'image_opacity', val), image_opacity_val.setText(str(val))))
|
||||
image_opacity_layout.addWidget(image_opacity_val)
|
||||
image_opacity_layout.addWidget(image_opacity)
|
||||
|
||||
# Speed Limiting
|
||||
down_speed_limit_container, down_speed_limit = create_widget(QSpinBox, "Max Download Speed (KiB, 0 for unlimited): ", width=180, height=30)
|
||||
@@ -231,7 +287,9 @@ def settings_dialog(self):
|
||||
interface_select.currentText(),
|
||||
image_width.value(),
|
||||
image_offset.value(),
|
||||
image_opacity.value()
|
||||
image_opacity.value(),
|
||||
image_as_wallpaper=image_mode_checkbox.isChecked(),
|
||||
image_position=image_position_combo.currentText()
|
||||
)
|
||||
|
||||
cancel_btn.clicked.connect(dialog.reject)
|
||||
@@ -240,7 +298,7 @@ def settings_dialog(self):
|
||||
|
||||
tabs = QtWidgets.QTabWidget()
|
||||
create_tab("General", [autoresume_container, update_checkbox_container, transparent_window_container], tabs=tabs, stretch=True)
|
||||
create_tab("Image", [enable_image_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("Network", [interface_container, max_connections_container, max_downloads_container, up_speed_limit_container, down_speed_limit_container, api_url_container], tabs=tabs, stretch=True)
|
||||
|
||||
@@ -254,6 +312,8 @@ def settings_dialog(self):
|
||||
state.image_offset = temp_image_offset
|
||||
state.image_opacity = temp_image_opacity
|
||||
state.image_enabled = temp_image_enabled
|
||||
state.image_as_wallpaper = temp_image_as_wallpaper
|
||||
state.image_position = temp_image_position
|
||||
|
||||
dialog.finished.connect(on_dialog_finished)
|
||||
dialog.exec()
|
||||
|
||||
@@ -35,7 +35,9 @@ def create_config():
|
||||
"enable_image": str(state.image_enabled),
|
||||
"image_width": str(state.image_width),
|
||||
"image_offset": str(state.image_offset),
|
||||
"image_opacity": str(state.image_opacity)
|
||||
"image_opacity": str(state.image_opacity),
|
||||
"image_as_wallpaper": str(state.image_as_wallpaper),
|
||||
"image_position": str(state.image_position)
|
||||
}
|
||||
|
||||
if platform.system() == "Windows":
|
||||
@@ -102,6 +104,8 @@ def read_config():
|
||||
state.image_width = config.getint("Image", "image_width", fallback=state.image_width)
|
||||
state.image_offset = config.getint("Image", "image_offset", fallback=state.image_offset)
|
||||
state.image_opacity = config.getint("Image", "image_opacity", fallback=state.image_opacity)
|
||||
state.image_as_wallpaper = config.getboolean("Image", "image_as_wallpaper", fallback=state.image_as_wallpaper)
|
||||
state.image_position = config.get("Image", "image_position", fallback=state.image_position)
|
||||
|
||||
create_config()
|
||||
|
||||
|
||||
@@ -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):
|
||||
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):
|
||||
if apiurl is not None:
|
||||
state.api_url = apiurl
|
||||
if download_path is not None:
|
||||
@@ -30,6 +30,10 @@ def save_settings(close=lambda: None, apiurl=None, download_path=None, down_spee
|
||||
state.image_offset = image_offset
|
||||
if image_opacity is not None:
|
||||
state.image_opacity = image_opacity
|
||||
if image_as_wallpaper is not None:
|
||||
state.image_as_wallpaper = image_as_wallpaper
|
||||
if image_position is not None:
|
||||
state.image_position = image_position
|
||||
|
||||
update_settings() # Update LibTorrent Session Settings
|
||||
consoleLog("Saved Settings")
|
||||
|
||||
+42
-1
@@ -35,7 +35,8 @@ class AppState(QObject):
|
||||
self._image_width: int = 300 # Default to 300px
|
||||
self._image_offset: int = 50
|
||||
self._image_opacity: int = 100
|
||||
self._image_as_wallpaper: bool = True
|
||||
self._image_as_wallpaper: bool = False
|
||||
self._image_position: str = "bottom-right" # top-left, top-right, bottom-left, bottom-right, center
|
||||
|
||||
# Trackers / Scraping
|
||||
self.posts: list[Dict[str,str]] | None = None # titles, urls, author, seeders, leechers
|
||||
@@ -115,4 +116,44 @@ class AppState(QObject):
|
||||
self._image_as_wallpaper = new_state
|
||||
self.image_changed.emit(self._image_path)
|
||||
|
||||
@property
|
||||
def image_position(self) -> str:
|
||||
return self._image_position
|
||||
|
||||
@image_position.setter
|
||||
def image_position(self, new_pos: str):
|
||||
if new_pos != self._image_position:
|
||||
self._image_position = new_pos
|
||||
self.image_changed.emit(self._image_path)
|
||||
|
||||
@property
|
||||
def image_custom_position(self) -> bool:
|
||||
return self._image_custom_position
|
||||
|
||||
@image_custom_position.setter
|
||||
def image_custom_position(self, new_state: bool):
|
||||
if new_state != self._image_custom_position:
|
||||
self._image_custom_position = new_state
|
||||
self.image_changed.emit(self._image_path)
|
||||
|
||||
@property
|
||||
def image_x(self) -> int:
|
||||
return self._image_x
|
||||
|
||||
@image_x.setter
|
||||
def image_x(self, val: int):
|
||||
if val != self._image_x:
|
||||
self._image_x = val
|
||||
self.image_changed.emit(self._image_path)
|
||||
|
||||
@property
|
||||
def image_y(self) -> int:
|
||||
return self._image_y
|
||||
|
||||
@image_y.setter
|
||||
def image_y(self, val: int):
|
||||
if val != self._image_y:
|
||||
self._image_y = val
|
||||
self.image_changed.emit(self._image_path)
|
||||
|
||||
state = AppState()
|
||||
|
||||
Reference in New Issue
Block a user