Skip to content

Commit 3246709

Browse files
committed
style: amélioration de l'interface BcaslDialog et du splash screen
- BcaslDialog : passage aux thèmes dynamiques et icônes SVG thémées (chevrons, sauvegarde, annuler). - BcaslDialog : refactorisation des sections en QGroupBox pour une intégration native optimale. - BcaslDialog : suppression du bouton de sauvegarde redondant et correction de l'état des boutons de réordonnancement. - Launchers : amélioration du cadrage du splash screen (centrage, mise à l'échelle à 50% et priorité d'affichage). - .gitignore : ajout de .codex.
1 parent 7a922ad commit 3246709

4 files changed

Lines changed: 119 additions & 157 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ __pycache__/
55

66
# C extensions
77
*.so
8-
8+
.codex
99
# Swap/backup files
1010
*.swp
1111
*.bak

Ui/Cli/launchers.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,28 @@ def launch_main_application(
8484
if os.path.isfile(path):
8585
pix = QPixmap(path)
8686
if not pix.isNull():
87-
splash = QSplashScreen(pix)
87+
# Ajustement du cadrage : limiter la taille à 80% de l'écran
88+
screen = app.primaryScreen()
89+
if screen:
90+
sgeo = screen.availableGeometry()
91+
max_w, max_h = int(sgeo.width() * 0.5), int(sgeo.height() * 0.5)
92+
if pix.width() > max_w or pix.height() > max_h:
93+
pix = pix.scaled(
94+
max_w, max_h,
95+
Qt.KeepAspectRatio,
96+
Qt.SmoothTransformation
97+
)
98+
99+
splash = QSplashScreen(pix, Qt.WindowStaysOnTopHint)
100+
101+
# Centrage explicite
102+
if screen:
103+
sgeo = screen.availableGeometry()
104+
splash.move(
105+
sgeo.x() + (sgeo.width() - pix.width()) // 2,
106+
sgeo.y() + (sgeo.height() - pix.height()) // 2
107+
)
108+
88109
splash.show()
89110
app.processEvents()
90111
break

Ui/Gui/Dialogs/BcaslDialog.py

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,11 @@
3737
from PySide6.QtGui import QColor, QShortcut, QKeySequence, QPalette
3838
from PySide6.QtWidgets import (
3939
QAbstractItemView,
40+
QApplication,
4041
QCheckBox,
4142
QDialog,
4243
QFrame,
44+
QGroupBox,
4345
QHBoxLayout,
4446
QLabel,
4547
QListWidget,
@@ -54,6 +56,60 @@
5456
QWidget,
5557
)
5658

59+
# ---------------------------------------------------------------------------
60+
# Helpers Thème
61+
# ---------------------------------------------------------------------------
62+
63+
def _is_dark() -> bool:
64+
"""Détermine si le thème actuel est sombre."""
65+
try:
66+
from Ui.Gui.UiConnection import _is_qss_dark
67+
app = QApplication.instance()
68+
return _is_qss_dark(app.styleSheet() if app else "")
69+
except Exception:
70+
return False
71+
72+
73+
def _get_bcasl_colors() -> dict[str, str]:
74+
"""Retourne les couleurs adaptées au thème (soumis au thème)."""
75+
app = QApplication.instance()
76+
pal = app.palette() if app else QPalette()
77+
78+
# Récupérer les couleurs système pour une intégration parfaite
79+
# PlaceholderText est excellent pour les labels secondaires (gris dynamique)
80+
secondary_text = pal.color(QPalette.PlaceholderText).name()
81+
# AlternateBase est fait pour le contraste de fond dans les listes/groupes
82+
alt_bg = pal.color(QPalette.AlternateBase).name()
83+
84+
if _is_dark():
85+
return {
86+
"warn_bg": "#504010", # Ambre sombre mais distinct du fond ("noir")
87+
"warn_border": "#D4A017", # Orange/Or
88+
"section_bg": alt_bg,
89+
"section_label": secondary_text,
90+
}
91+
return {
92+
"warn_bg": "#FFF3CD", # Jaune pâle
93+
"warn_border": "orange",
94+
"section_bg": alt_bg,
95+
"section_label": secondary_text,
96+
}
97+
98+
99+
def _apply_themed_icon(widget: QPushButton, icon_name: str, size: int = 18) -> None:
100+
"""Applique une icône SVG thémée au widget."""
101+
try:
102+
from Ui.Gui.UiConnection import themed_svg_icon
103+
from PySide6.QtCore import QSize
104+
icon_path = str(Path(__file__).parent.parent.parent.parent / "icons" / icon_name)
105+
icon = themed_svg_icon(icon_path, size=size)
106+
if icon:
107+
widget.setIcon(icon)
108+
widget.setIconSize(QSize(size, size))
109+
except Exception:
110+
pass
111+
112+
57113
# ---------------------------------------------------------------------------
58114
# Constantes
59115
# ---------------------------------------------------------------------------
@@ -69,10 +125,6 @@
69125
100: ("Défaut", 60, 199),
70126
}
71127

72-
_WARN_BG = "#FFF3CD" # fond jaune pâle
73-
_WARN_BORDER = "orange" # bordure spinbox hors-plage
74-
_SECTION_BG = "#F5F5F5" # fond section (clair)
75-
76128
# Tag → score de phase
77129
_TAG_PRIORITY_MAP: dict[str, int] = {}
78130
try:
@@ -209,15 +261,17 @@ def __init__(
209261
row.addWidget(self.spin)
210262

211263
# Boutons ↑ ↓
212-
self.btn_up = QPushButton("↑")
213-
self.btn_up.setFixedWidth(28)
264+
self.btn_up = QPushButton()
265+
self.btn_up.setFixedWidth(32)
214266
self.btn_up.setToolTip("Monter dans la section")
267+
_apply_themed_icon(self.btn_up, "chevron-up.svg", size=18)
215268
self.btn_up.clicked.connect(lambda: self.sig_move_up.emit(self.pid))
216269
row.addWidget(self.btn_up)
217270

218-
self.btn_down = QPushButton("↓")
219-
self.btn_down.setFixedWidth(28)
271+
self.btn_down = QPushButton()
272+
self.btn_down.setFixedWidth(32)
220273
self.btn_down.setToolTip("Descendre dans la section")
274+
_apply_themed_icon(self.btn_down, "chevron-down.svg", size=18)
221275
self.btn_down.clicked.connect(lambda: self.sig_move_down.emit(self.pid))
222276
row.addWidget(self.btn_down)
223277

@@ -237,7 +291,8 @@ def _refresh_warning(self) -> None:
237291

238292
if expert or not out_of_range:
239293
self.lbl_warn.setVisible(False)
240-
self.setStyleSheet("")
294+
# Utilise un fond transparent pour laisser voir le fond de la section
295+
self.setStyleSheet("QFrame#PluginRow { background: transparent; border: none; }")
241296
self.spin.setStyleSheet("")
242297
else:
243298
self.lbl_warn.setVisible(True)
@@ -246,8 +301,9 @@ def _refresh_warning(self) -> None:
246301
"L'ordre d'exécution peut être inattendu.\n"
247302
"Utilisez le mode Expert pour désactiver les avertissements."
248303
)
249-
self.setStyleSheet(f"QFrame#PluginRow {{ background: {_WARN_BG}; }}")
250-
self.spin.setStyleSheet(f"QSpinBox {{ border: 2px solid {_WARN_BORDER}; }}")
304+
colors = _get_bcasl_colors()
305+
self.setStyleSheet(f"QFrame#PluginRow {{ background: {colors['warn_bg']}; border: 1px solid {colors['warn_border']}; border-radius: 4px; }}")
306+
self.spin.setStyleSheet(f"QSpinBox {{ border: 2px solid {colors['warn_border']}; }}")
251307

252308
def refresh_expert(self) -> None:
253309
"""Appelé quand expert mode change."""
@@ -281,8 +337,8 @@ def restore(self, snap: dict[str, Any]) -> None:
281337
# Widget : section collapsible
282338
# ---------------------------------------------------------------------------
283339

284-
class _SectionWidget(QFrame):
285-
"""Section collapsible représentant une catégorie de plugins."""
340+
class _SectionWidget(QGroupBox):
341+
"""Section collapsible utilisant QGroupBox pour une meilleure intégration thématique."""
286342

287343
sig_changed = Signal()
288344

@@ -302,45 +358,27 @@ def __init__(
302358
self._max = max_prio
303359
self._expert_ref = expert_ref
304360
self._rows: list[_PluginRow] = []
305-
self._collapsed = False
306361

307-
self.setFrameShape(QFrame.StyledPanel)
308-
self.setStyleSheet(f"QFrame {{ background: {_SECTION_BG}; border-radius: 4px; }}")
309-
310-
outer = QVBoxLayout(self)
311-
outer.setContentsMargins(4, 4, 4, 4)
312-
outer.setSpacing(2)
313-
314-
# En-tête
315-
header = QFrame()
316-
header_lay = QHBoxLayout(header)
317-
header_lay.setContentsMargins(4, 2, 4, 2)
318-
319-
self._toggle_btn = QPushButton("▼")
320-
self._toggle_btn.setFlat(True)
321-
self._toggle_btn.setFixedWidth(24)
322-
self._toggle_btn.clicked.connect(self._toggle_collapse)
323-
header_lay.addWidget(self._toggle_btn)
324-
325-
self._title = QLabel(f"<b>{name}</b> <small style='color:#888'>({min_prio}{max_prio})</small>")
326-
self._title.setTextFormat(Qt.RichText)
327-
header_lay.addWidget(self._title)
328-
header_lay.addStretch(1)
329-
outer.addWidget(header)
330-
331-
# Zone de contenu (plugins)
332-
self._content = QWidget()
333-
self._content_lay = QVBoxLayout(self._content)
334-
self._content_lay.setContentsMargins(8, 2, 4, 4)
362+
# Titre natif du GroupBox
363+
self.setTitle(f"{name} ({min_prio}{max_prio})")
364+
365+
# Rendre le GroupBox collapsible via la checkbox native
366+
self.setCheckable(True)
367+
self.setChecked(True)
368+
self.toggled.connect(self._on_toggled)
369+
370+
self._content_lay = QVBoxLayout(self)
371+
self._content_lay.setContentsMargins(8, 12, 8, 8)
335372
self._content_lay.setSpacing(3)
336-
outer.addWidget(self._content)
337373

338374
# ------------------------------------------------------------------
339375

340-
def _toggle_collapse(self) -> None:
341-
self._collapsed = not self._collapsed
342-
self._content.setVisible(not self._collapsed)
343-
self._toggle_btn.setText("▶" if self._collapsed else "▼")
376+
def _on_toggled(self, checked: bool) -> None:
377+
"""Cache/affiche les lignes de plugins quand on toggle le GroupBox."""
378+
for i in range(self._content_lay.count()):
379+
item = self._content_lay.itemAt(i)
380+
if item and item.widget():
381+
item.widget().setVisible(checked)
344382

345383
def add_row(self, row: _PluginRow) -> None:
346384
self._rows.append(row)
@@ -349,6 +387,10 @@ def add_row(self, row: _PluginRow) -> None:
349387
row.sig_move_down.connect(self._on_move_down)
350388
row.sig_enabled.connect(lambda _pid, _v: self.sig_changed.emit())
351389
row.sig_priority.connect(lambda _pid, _v: self.sig_changed.emit())
390+
# S'assurer que la visibilité suit l'état actuel du toggle
391+
row.setVisible(self.isChecked())
392+
# Mettre à jour l'état des boutons ↑/↓ pour toute la section
393+
self._refresh_arrow_buttons()
352394

353395
def _on_move_up(self, pid: str) -> None:
354396
idx = self._find_idx(pid)
@@ -479,9 +521,6 @@ def _build_ui(self) -> None:
479521
lbl.setTextFormat(Qt.RichText)
480522
title_row.addWidget(lbl)
481523
title_row.addStretch(1)
482-
btn_save_top = QPushButton(self._gui.tr("Enregistrer", "Save"))
483-
btn_save_top.clicked.connect(self._do_save)
484-
title_row.addWidget(btn_save_top)
485524
main.addLayout(title_row)
486525

487526
# Tabs : Pipeline + onglets plugins
@@ -496,7 +535,12 @@ def _build_ui(self) -> None:
496535
scroll = QScrollArea()
497536
scroll.setWidgetResizable(True)
498537
scroll.setFrameShape(QFrame.NoFrame)
538+
scroll.setStyleSheet("QScrollArea { background: transparent; }")
539+
499540
container = QWidget()
541+
container.setObjectName("PipelineContainer")
542+
container.setStyleSheet("QWidget#PipelineContainer { background: transparent; }")
543+
500544
self._pipeline_lay = QVBoxLayout(container)
501545
self._pipeline_lay.setSpacing(8)
502546
self._pipeline_lay.setContentsMargins(4, 4, 4, 4)
@@ -521,10 +565,12 @@ def _build_ui(self) -> None:
521565
bottom.addStretch(1)
522566

523567
btn_cancel = QPushButton(self._gui.tr("Annuler", "Cancel"))
568+
_apply_themed_icon(btn_cancel, "x-circle.svg")
524569
btn_cancel.clicked.connect(self.reject)
525570
bottom.addWidget(btn_cancel)
526571

527572
btn_save = QPushButton(self._gui.tr("Enregistrer dans bcasl.yml", "Save to bcasl.yml"))
573+
_apply_themed_icon(btn_save, "save.svg")
528574
btn_save.setDefault(True)
529575
btn_save.clicked.connect(self._do_save)
530576
bottom.addWidget(btn_save)

0 commit comments

Comments
 (0)