Skip to content

Commit f1b59f2

Browse files
committed
Alléger l'UI IDE et soumettre toutes les icônes SVG au thème
1 parent fbaa16d commit f1b59f2

4 files changed

Lines changed: 154 additions & 64 deletions

File tree

Core/IdeLikeGui/connections.py

Lines changed: 31 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
QToolButton,
2929
QStatusBar,
3030
QWidget,
31-
QFrame,
3231
QSplitter,
3332
)
3433

@@ -195,57 +194,11 @@ def _find(cls, name: str):
195194
self.status_hint = self.statusbar.findChild(QLabel, "status_hint") if self.statusbar else None
196195
except Exception:
197196
self.status_hint = None
198-
try:
199-
if self.log is not None:
200-
self.log.setReadOnly(True)
201-
self.log.setAcceptRichText(False)
202-
except Exception:
203-
pass
204197
_setup_status_bar(self)
205198

206199

207200
def _tune_ide_like_layout(self) -> None:
208-
"""Relax tight layout constraints in the IDE-like UI for better label fit."""
209-
header = self.ui.findChild(QFrame, "header")
210-
if header is not None:
211-
try:
212-
header.setMinimumHeight(48)
213-
header.setMaximumHeight(56)
214-
except Exception:
215-
pass
216-
217-
for btn_name in ("compile_btn", "cancel_btn"):
218-
btn = getattr(self, btn_name, None)
219-
if btn is None:
220-
continue
221-
try:
222-
btn.setMinimumSize(124, 34)
223-
except Exception:
224-
pass
225-
226-
workspace_panel = self.ui.findChild(QFrame, "workspace_panel")
227-
if workspace_panel is not None:
228-
try:
229-
workspace_panel.setMinimumWidth(280)
230-
workspace_panel.setMaximumWidth(400)
231-
except Exception:
232-
pass
233-
234-
tools_panel = self.ui.findChild(QFrame, "tools_panel")
235-
if tools_panel is not None:
236-
try:
237-
tools_panel.setMinimumWidth(200)
238-
tools_panel.setMaximumWidth(280)
239-
except Exception:
240-
pass
241-
242-
logs_panel = self.ui.findChild(QFrame, "logs_panel")
243-
if logs_panel is not None:
244-
try:
245-
logs_panel.setMinimumHeight(190)
246-
logs_panel.setMaximumHeight(320)
247-
except Exception:
248-
pass
201+
"""Apply runtime splitter ratios; static constraints live in the .ui file."""
249202

250203
main_splitter = self.ui.findChild(QSplitter, "mainSplitter")
251204
if main_splitter is not None:
@@ -507,6 +460,36 @@ def _apply_activity_buttons_theme(self) -> None:
507460
"QToolButton::menu-indicator { image: none; width: 0px; }"
508461
)
509462

463+
# Keep IDE activity icons in sync with current theme-colored SVGs.
464+
try:
465+
from ..UiConnection import themed_svg_icon
466+
467+
base = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
468+
icons_dir = os.path.join(base, "icons")
469+
470+
more_btn = getattr(self, "toolButton_more", None)
471+
if more_btn is not None:
472+
more_icon = themed_svg_icon(
473+
os.path.join(icons_dir, "more-horizontal.svg"), size=18
474+
)
475+
if more_icon is not None and not more_icon.isNull():
476+
more_btn.setIcon(more_icon)
477+
more_btn.setIconSize(more_btn.iconSize())
478+
more_btn.setText("")
479+
480+
deps_btn = getattr(self, "activity_btn_deps", None)
481+
src_btn = getattr(self, "btn_suggest_deps", None)
482+
if deps_btn is not None:
483+
if src_btn is not None and src_btn.icon() is not None and not src_btn.icon().isNull():
484+
deps_btn.setIcon(src_btn.icon())
485+
deps_btn.setIconSize(src_btn.iconSize())
486+
else:
487+
deps_icon = themed_svg_icon(os.path.join(icons_dir, "search.svg"), size=18)
488+
if deps_icon is not None and not deps_icon.isNull():
489+
deps_btn.setIcon(deps_icon)
490+
except Exception:
491+
pass
492+
510493
for attr in ("toolButton_more", "activity_btn_deps"):
511494
btn = getattr(self, attr, None)
512495
if btn is None:

Core/UiConnection.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,78 @@ def _to_rgb(val: str):
734734
return False
735735

736736

737+
def _extract_accent_color_for_icons(css_text: str) -> str | None:
738+
try:
739+
import re
740+
741+
def _block(selector: str) -> str | None:
742+
pattern = re.compile(rf"{re.escape(selector)}\\s*\\{{([^}}]+)\\}}", re.S)
743+
match = pattern.search(css_text)
744+
return match.group(1) if match else None
745+
746+
def _colors(text: str) -> list[str]:
747+
return re.findall(r"#[0-9a-fA-F]{3,6}", text)
748+
749+
for selector in ("QPushButton#compile_btn", "#compile_btn"):
750+
block = _block(selector)
751+
if block:
752+
colors = _colors(block)
753+
if colors:
754+
return colors[0]
755+
756+
match = re.search(r"--accent[^:]*:\\s*(#[0-9a-fA-F]{3,6})", css_text)
757+
if match:
758+
return match.group(1)
759+
except Exception:
760+
return None
761+
return None
762+
763+
764+
def _resolve_theme_icon_color(css: str | None = None) -> str:
765+
if not css:
766+
try:
767+
from PySide6.QtWidgets import QApplication
768+
769+
app = QApplication.instance()
770+
css = app.styleSheet() if app else ""
771+
except Exception:
772+
css = ""
773+
if css:
774+
accent = _extract_accent_color_for_icons(css)
775+
if accent:
776+
return accent
777+
return "#FFFFFF" if _is_qss_dark(css) else "#111111"
778+
return "#FFFFFF" if _detect_system_color_scheme() == "sombre" else "#111111"
779+
780+
781+
def themed_svg_icon(path: str, size: int = 18, css: str | None = None) -> QIcon | None:
782+
"""Render an SVG icon tinted according to current app theme."""
783+
if not os.path.isfile(path):
784+
return None
785+
if QSvgRenderer is None:
786+
return QIcon(path)
787+
try:
788+
with open(path, encoding="utf-8") as f:
789+
svg = f.read()
790+
except Exception:
791+
return None
792+
793+
color = _resolve_theme_icon_color(css)
794+
if "currentColor" in svg:
795+
svg = svg.replace("currentColor", color)
796+
else:
797+
svg = svg.replace("<svg ", f'<svg color="{color}" ', 1)
798+
renderer = QSvgRenderer(QByteArray(svg.encode("utf-8")))
799+
if not renderer.isValid():
800+
return None
801+
pixmap = QPixmap(size, size)
802+
pixmap.fill(Qt.transparent)
803+
painter = QPainter(pixmap)
804+
renderer.render(painter, QRectF(0, 0, size, size))
805+
painter.end()
806+
return QIcon(pixmap)
807+
808+
737809
def _refresh_log_palette(self, css: str | None = None) -> None:
738810
"""Assure une couleur de texte lisible pour le journal, selon le thème."""
739811
if not getattr(self, "log", None):
@@ -813,6 +885,18 @@ def apply_theme(self, pref: str) -> None:
813885
_apply_button_icons(self)
814886
except Exception:
815887
pass
888+
try:
889+
# IDE-like activity-bar buttons are themed separately.
890+
from .IdeLikeGui.connections import _apply_activity_buttons_theme
891+
892+
_apply_activity_buttons_theme(self)
893+
except Exception:
894+
pass
895+
try:
896+
if hasattr(self, "_refresh_entrypoint_marker"):
897+
self._refresh_entrypoint_marker()
898+
except Exception:
899+
pass
816900
self.theme = pref or "System"
817901
# Met à jour le texte du bouton (ne pas recharger i18n)
818902
if hasattr(self, "select_theme") and self.select_theme:

Core/UiFeatures.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
from PySide6.QtCore import Qt
3737
from PySide6.QtGui import QIcon, QPixmap
38-
from PySide6.QtWidgets import QFileDialog, QMessageBox, QMenu
38+
from PySide6.QtWidgets import QApplication, QFileDialog, QMessageBox, QMenu
3939

4040

4141
class UiFeatures:
@@ -180,15 +180,31 @@ def _show_entrypoint_menu(self, pos) -> None:
180180
def _entrypoint_icon(self) -> QIcon | None:
181181
"""Retourne l'icône utilisée pour marquer le point d'entrée."""
182182
icon = getattr(self, "_entrypoint_icon_cache", None)
183-
if isinstance(icon, QIcon) and not icon.isNull():
183+
token = getattr(self, "_entrypoint_icon_theme_token", None)
184+
try:
185+
app = QApplication.instance()
186+
css = app.styleSheet() if app else ""
187+
except Exception:
188+
css = ""
189+
current_token = hash(css or "")
190+
if token == current_token and isinstance(icon, QIcon) and not icon.isNull():
184191
return icon
185192
try:
186193
base = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
187194
path = os.path.join(base, "icons", "check-circle.svg")
188195
if os.path.isfile(path):
189-
icon = QIcon(path)
196+
icon = None
197+
try:
198+
from .UiConnection import themed_svg_icon
199+
200+
icon = themed_svg_icon(path, size=16, css=css)
201+
except Exception:
202+
icon = None
203+
if icon is None or icon.isNull():
204+
icon = QIcon(path)
190205
if not icon.isNull():
191206
self._entrypoint_icon_cache = icon
207+
self._entrypoint_icon_theme_token = current_token
192208
return icon
193209
except Exception:
194210
pass

ui/ui_ide_design2.ui

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,13 @@ QProgressBar::chunk { background: #0e639c; }</string>
9191
<property name="minimumSize">
9292
<size>
9393
<width>0</width>
94-
<height>36</height>
94+
<height>48</height>
9595
</size>
9696
</property>
9797
<property name="maximumSize">
9898
<size>
9999
<width>16777215</width>
100-
<height>36</height>
100+
<height>56</height>
101101
</size>
102102
</property>
103103
<property name="frameShape">
@@ -146,8 +146,8 @@ QProgressBar::chunk { background: #0e639c; }</string>
146146
<widget class="QPushButton" name="compile_btn">
147147
<property name="minimumSize">
148148
<size>
149-
<width>90</width>
150-
<height>28</height>
149+
<width>124</width>
150+
<height>34</height>
151151
</size>
152152
</property>
153153
<property name="text">
@@ -159,8 +159,8 @@ QProgressBar::chunk { background: #0e639c; }</string>
159159
<widget class="QPushButton" name="cancel_btn">
160160
<property name="minimumSize">
161161
<size>
162-
<width>90</width>
163-
<height>28</height>
162+
<width>124</width>
163+
<height>34</height>
164164
</size>
165165
</property>
166166
<property name="text">
@@ -264,13 +264,13 @@ QProgressBar::chunk { background: #0e639c; }</string>
264264
<widget class="QFrame" name="workspace_panel">
265265
<property name="minimumSize">
266266
<size>
267-
<width>260</width>
267+
<width>280</width>
268268
<height>0</height>
269269
</size>
270270
</property>
271271
<property name="maximumSize">
272272
<size>
273-
<width>420</width>
273+
<width>400</width>
274274
<height>16777215</height>
275275
</size>
276276
</property>
@@ -408,13 +408,13 @@ QProgressBar::chunk { background: #0e639c; }</string>
408408
<widget class="QFrame" name="tools_panel">
409409
<property name="minimumSize">
410410
<size>
411-
<width>180</width>
411+
<width>200</width>
412412
<height>0</height>
413413
</size>
414414
</property>
415415
<property name="maximumSize">
416416
<size>
417-
<width>300</width>
417+
<width>280</width>
418418
<height>16777215</height>
419419
</size>
420420
</property>
@@ -537,13 +537,13 @@ QProgressBar::chunk { background: #0e639c; }</string>
537537
<property name="minimumSize">
538538
<size>
539539
<width>0</width>
540-
<height>160</height>
540+
<height>190</height>
541541
</size>
542542
</property>
543543
<property name="maximumSize">
544544
<size>
545545
<width>16777215</width>
546-
<height>240</height>
546+
<height>320</height>
547547
</size>
548548
</property>
549549
<property name="frameShape">
@@ -576,7 +576,14 @@ QProgressBar::chunk { background: #0e639c; }</string>
576576
</widget>
577577
</item>
578578
<item>
579-
<widget class="QTextEdit" name="log"/>
579+
<widget class="QTextEdit" name="log">
580+
<property name="acceptRichText">
581+
<bool>false</bool>
582+
</property>
583+
<property name="readOnly">
584+
<bool>true</bool>
585+
</property>
586+
</widget>
580587
</item>
581588
<item>
582589
<layout class="QHBoxLayout" name="progressRowLayout">

0 commit comments

Comments
 (0)