synodic-client 0.0.1.dev37__tar.gz → 0.0.1.dev39__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/PKG-INFO +3 -3
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/pyproject.toml +4 -4
- synodic_client-0.0.1.dev39/synodic_client/_version.py +1 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/action_card.py +17 -121
- synodic_client-0.0.1.dev39/synodic_client/application/screen/install.py +1403 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/log_panel.py +26 -20
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/screen.py +136 -227
- synodic_client-0.0.1.dev39/synodic_client/application/screen/sidebar.py +330 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/tray.py +45 -33
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/theme.py +92 -1
- synodic_client-0.0.1.dev39/synodic_client/application/workers.py +100 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/client.py +0 -11
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/updater.py +24 -53
- synodic_client-0.0.1.dev39/tests/unit/qt/conftest.py +24 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_action_card.py +2 -66
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_install_preview.py +182 -299
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_log_panel.py +54 -41
- synodic_client-0.0.1.dev39/tests/unit/qt/test_preview_model.py +195 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_settings.py +0 -6
- synodic_client-0.0.1.dev39/tests/unit/qt/test_sidebar.py +308 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_update_banner.py +0 -6
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_client_updater.py +0 -7
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_config.py +0 -16
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_updater.py +34 -24
- synodic_client-0.0.1.dev37/synodic_client/_version.py +0 -1
- synodic_client-0.0.1.dev37/synodic_client/application/screen/install.py +0 -1151
- synodic_client-0.0.1.dev37/synodic_client/application/workers.py +0 -112
- synodic_client-0.0.1.dev37/tests/unit/qt/conftest.py +0 -10
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/LICENSE.md +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/README.md +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/__main__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/bootstrap.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/icon.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/instance.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/qt.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/card.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/settings.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/spinner.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/screen/update_banner.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/application/uri.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/cli.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/config.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/logging.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/protocol.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/py.typed +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/resolution.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/synodic_client/startup.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/conftest.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/qt/test_logging.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_cli.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_client_version.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_examples.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_install.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_resolution.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/test_uri.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/windows/__init__.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/windows/conftest.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/windows/test_protocol.py +0 -0
- {synodic_client-0.0.1.dev37 → synodic_client-0.0.1.dev39}/tests/unit/windows/test_startup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: synodic_client
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev39
|
|
4
4
|
Author-Email: Synodic Software <contact@synodic.software>
|
|
5
5
|
License: LGPL-3.0-or-later
|
|
6
6
|
Project-URL: homepage, https://github.com/synodic/synodic-client
|
|
@@ -8,9 +8,9 @@ Project-URL: repository, https://github.com/synodic/synodic-client
|
|
|
8
8
|
Requires-Python: <3.15,>=3.14
|
|
9
9
|
Requires-Dist: pyside6>=6.10.2
|
|
10
10
|
Requires-Dist: packaging>=26.0
|
|
11
|
-
Requires-Dist: porringer>=0.2.1.
|
|
11
|
+
Requires-Dist: porringer>=0.2.1.dev56
|
|
12
12
|
Requires-Dist: qasync>=0.28.0
|
|
13
|
-
Requires-Dist: velopack>=0.0.
|
|
13
|
+
Requires-Dist: velopack>=0.0.1444.dev49733
|
|
14
14
|
Requires-Dist: typer>=0.24.1
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
|
|
@@ -10,12 +10,12 @@ requires-python = ">=3.14, <3.15"
|
|
|
10
10
|
dependencies = [
|
|
11
11
|
"pyside6>=6.10.2",
|
|
12
12
|
"packaging>=26.0",
|
|
13
|
-
"porringer>=0.2.1.
|
|
13
|
+
"porringer>=0.2.1.dev56",
|
|
14
14
|
"qasync>=0.28.0",
|
|
15
|
-
"velopack>=0.0.
|
|
15
|
+
"velopack>=0.0.1444.dev49733",
|
|
16
16
|
"typer>=0.24.1",
|
|
17
17
|
]
|
|
18
|
-
version = "0.0.1.
|
|
18
|
+
version = "0.0.1.dev39"
|
|
19
19
|
|
|
20
20
|
[project.license]
|
|
21
21
|
text = "LGPL-3.0-or-later"
|
|
@@ -35,7 +35,7 @@ build = [
|
|
|
35
35
|
"pyinstaller>=6.19.0",
|
|
36
36
|
]
|
|
37
37
|
lint = [
|
|
38
|
-
"ruff>=0.15.
|
|
38
|
+
"ruff>=0.15.4",
|
|
39
39
|
"pyrefly>=0.54.0",
|
|
40
40
|
]
|
|
41
41
|
test = [
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.1.dev39'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Action card widgets for the install preview screen.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
Each card shows essential information (package name, type, version,
|
|
4
|
+
status badge). During install, execution output is routed to the
|
|
5
|
+
unified :class:`~synodic_client.application.screen.log_panel.ExecutionLogPanel`
|
|
6
|
+
rather than displayed inline.
|
|
7
7
|
|
|
8
8
|
:class:`ActionCard` is the per-action widget.
|
|
9
9
|
:class:`ActionCardList` is the scrollable container that holds them.
|
|
@@ -11,23 +11,19 @@ expands inline to display execution output during install.
|
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
|
-
import html as html_mod
|
|
15
14
|
import logging
|
|
16
15
|
|
|
17
16
|
from porringer.backend.command.core.action_builder import PHASE_ORDER
|
|
18
17
|
from porringer.schema import SetupAction, SetupActionResult, SkipReason
|
|
19
18
|
from porringer.schema.plugin import PluginKind
|
|
20
19
|
from PySide6.QtCore import QRect, Qt, QTimer, Signal
|
|
21
|
-
from PySide6.QtGui import QColor,
|
|
20
|
+
from PySide6.QtGui import QColor, QPainter, QPen
|
|
22
21
|
from PySide6.QtWidgets import (
|
|
23
22
|
QApplication,
|
|
24
23
|
QCheckBox,
|
|
25
24
|
QFrame,
|
|
26
25
|
QHBoxLayout,
|
|
27
26
|
QLabel,
|
|
28
|
-
QScrollArea,
|
|
29
|
-
QSizePolicy,
|
|
30
|
-
QTextEdit,
|
|
31
27
|
QToolButton,
|
|
32
28
|
QVBoxLayout,
|
|
33
29
|
QWidget,
|
|
@@ -38,7 +34,6 @@ from synodic_client.application.theme import (
|
|
|
38
34
|
ACTION_CARD_COMMAND_STYLE,
|
|
39
35
|
ACTION_CARD_DESC_STYLE,
|
|
40
36
|
ACTION_CARD_EXECUTING_STYLE,
|
|
41
|
-
ACTION_CARD_LOG_STYLE,
|
|
42
37
|
ACTION_CARD_PACKAGE_STYLE,
|
|
43
38
|
ACTION_CARD_SKELETON_BAR_STYLE,
|
|
44
39
|
ACTION_CARD_SKELETON_STYLE,
|
|
@@ -61,13 +56,6 @@ from synodic_client.application.theme import (
|
|
|
61
56
|
COPY_BTN_STYLE,
|
|
62
57
|
COPY_FEEDBACK_MS,
|
|
63
58
|
COPY_ICON,
|
|
64
|
-
LOG_COLOR_ERROR,
|
|
65
|
-
LOG_COLOR_PHASE,
|
|
66
|
-
LOG_COLOR_STDERR,
|
|
67
|
-
LOG_COLOR_STDOUT,
|
|
68
|
-
LOG_COLOR_SUCCESS,
|
|
69
|
-
MONOSPACE_FAMILY,
|
|
70
|
-
MONOSPACE_SIZE,
|
|
71
59
|
)
|
|
72
60
|
|
|
73
61
|
logger = logging.getLogger(__name__)
|
|
@@ -214,7 +202,6 @@ class ActionCard(QFrame):
|
|
|
214
202
|
self.setObjectName('actionCard')
|
|
215
203
|
self._action: SetupAction | None = None
|
|
216
204
|
self._is_skeleton = skeleton
|
|
217
|
-
self._log_expanded = False
|
|
218
205
|
self._checking = False
|
|
219
206
|
self._check_available_version: str | None = None
|
|
220
207
|
|
|
@@ -278,7 +265,6 @@ class ActionCard(QFrame):
|
|
|
278
265
|
def _init_real_ui(self) -> None:
|
|
279
266
|
"""Build the action card layout."""
|
|
280
267
|
self.setStyleSheet(ACTION_CARD_STYLE)
|
|
281
|
-
self.setCursor(Qt.CursorShape.PointingHandCursor)
|
|
282
268
|
|
|
283
269
|
outer = QVBoxLayout(self)
|
|
284
270
|
outer.setContentsMargins(6, 6, 6, 6)
|
|
@@ -287,7 +273,6 @@ class ActionCard(QFrame):
|
|
|
287
273
|
outer.addLayout(self._build_top_row())
|
|
288
274
|
outer.addWidget(self._build_description_row())
|
|
289
275
|
outer.addWidget(self._build_command_row())
|
|
290
|
-
outer.addWidget(self._build_log_output())
|
|
291
276
|
|
|
292
277
|
def _build_top_row(self) -> QHBoxLayout:
|
|
293
278
|
"""Build the top row: type badge | package name ... version | status/spinner | prerelease."""
|
|
@@ -366,40 +351,10 @@ class ActionCard(QFrame):
|
|
|
366
351
|
self._command_row.hide()
|
|
367
352
|
return self._command_row
|
|
368
353
|
|
|
369
|
-
def _build_log_output(self) -> QTextEdit:
|
|
370
|
-
"""Build the inline log body (hidden by default)."""
|
|
371
|
-
self._log_output = QTextEdit()
|
|
372
|
-
self._log_output.setReadOnly(True)
|
|
373
|
-
self._log_output.setFont(QFont(MONOSPACE_FAMILY, MONOSPACE_SIZE))
|
|
374
|
-
self._log_output.setStyleSheet(ACTION_CARD_LOG_STYLE)
|
|
375
|
-
self._log_output.setMinimumHeight(40)
|
|
376
|
-
self._log_output.setMaximumHeight(250)
|
|
377
|
-
self._log_output.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
|
378
|
-
self._log_output.hide()
|
|
379
|
-
return self._log_output
|
|
380
|
-
|
|
381
354
|
# ------------------------------------------------------------------
|
|
382
|
-
# Mouse events (
|
|
355
|
+
# Mouse events (copy button)
|
|
383
356
|
# ------------------------------------------------------------------
|
|
384
357
|
|
|
385
|
-
def mousePressEvent(self, event: object) -> None:
|
|
386
|
-
"""Toggle the inline log body on click."""
|
|
387
|
-
if self._is_skeleton or not hasattr(self, '_log_output'):
|
|
388
|
-
return
|
|
389
|
-
# Don't toggle the log when clicking interactive child widgets
|
|
390
|
-
if hasattr(self, '_copy_btn') and self._copy_btn.underMouse():
|
|
391
|
-
return
|
|
392
|
-
if hasattr(self, '_package_label') and self._package_label.underMouse():
|
|
393
|
-
return
|
|
394
|
-
if hasattr(self, '_desc_label') and self._desc_label.underMouse():
|
|
395
|
-
return
|
|
396
|
-
self._toggle_log()
|
|
397
|
-
|
|
398
|
-
def _toggle_log(self) -> None:
|
|
399
|
-
"""Expand or collapse the inline log body."""
|
|
400
|
-
self._log_expanded = not self._log_expanded
|
|
401
|
-
self._log_output.setVisible(self._log_expanded)
|
|
402
|
-
|
|
403
358
|
def _copy_command(self) -> None:
|
|
404
359
|
"""Copy the command label text to the clipboard with brief feedback."""
|
|
405
360
|
clipboard = QApplication.clipboard()
|
|
@@ -644,7 +599,8 @@ class ActionCard(QFrame):
|
|
|
644
599
|
def set_executing(self) -> None:
|
|
645
600
|
"""Transition the card into the *executing* state.
|
|
646
601
|
|
|
647
|
-
|
|
602
|
+
Updates the status badge. Execution output is routed to the
|
|
603
|
+
unified :class:`~synodic_client.application.screen.log_panel.ExecutionLogPanel`.
|
|
648
604
|
"""
|
|
649
605
|
if self._is_skeleton:
|
|
650
606
|
return
|
|
@@ -652,37 +608,13 @@ class ActionCard(QFrame):
|
|
|
652
608
|
self.setStyleSheet(ACTION_CARD_EXECUTING_STYLE)
|
|
653
609
|
self._status_label.setText('Running\u2026')
|
|
654
610
|
self._status_label.setStyleSheet(ACTION_CARD_STATUS_RUNNING)
|
|
655
|
-
self._log_expanded = True
|
|
656
|
-
self._log_output.setVisible(True)
|
|
657
|
-
|
|
658
|
-
def append_output(self, text: str, stream: str | None = None) -> None:
|
|
659
|
-
"""Append a line of output to the inline log.
|
|
660
|
-
|
|
661
|
-
Args:
|
|
662
|
-
text: The output line.
|
|
663
|
-
stream: ``'stdout'``, ``'stderr'``, or ``None`` for phase messages.
|
|
664
|
-
"""
|
|
665
|
-
if self._is_skeleton or not hasattr(self, '_log_output'):
|
|
666
|
-
return
|
|
667
|
-
|
|
668
|
-
colour = LOG_COLOR_STDOUT
|
|
669
|
-
if stream == 'stderr':
|
|
670
|
-
colour = LOG_COLOR_STDERR
|
|
671
|
-
elif stream is None:
|
|
672
|
-
colour = LOG_COLOR_PHASE
|
|
673
|
-
|
|
674
|
-
escaped = html_mod.escape(text)
|
|
675
|
-
self._log_output.append(f'<span style="color: {colour};">{escaped}</span>')
|
|
676
|
-
|
|
677
|
-
cursor = self._log_output.textCursor()
|
|
678
|
-
cursor.movePosition(QTextCursor.MoveOperation.End)
|
|
679
|
-
self._log_output.setTextCursor(cursor)
|
|
680
611
|
|
|
681
612
|
def set_result(self, result: SetupActionResult) -> None:
|
|
682
613
|
"""Update the card with the final execution result.
|
|
683
614
|
|
|
684
|
-
The card returns to the default border style.
|
|
685
|
-
|
|
615
|
+
The card returns to the default border style. Detailed output
|
|
616
|
+
is displayed in the unified
|
|
617
|
+
:class:`~synodic_client.application.screen.log_panel.ExecutionLogPanel`.
|
|
686
618
|
|
|
687
619
|
Args:
|
|
688
620
|
result: The action execution result.
|
|
@@ -696,14 +628,9 @@ class ActionCard(QFrame):
|
|
|
696
628
|
label = skip_reason_label(result.skip_reason)
|
|
697
629
|
self._status_label.setText(label)
|
|
698
630
|
self._status_label.setStyleSheet(ACTION_CARD_STATUS_SKIPPED)
|
|
699
|
-
self.append_output(f'\u23ed Skipped: {label}', None)
|
|
700
631
|
elif result.success:
|
|
701
632
|
self._status_label.setText('Done')
|
|
702
633
|
self._status_label.setStyleSheet(ACTION_CARD_STATUS_DONE)
|
|
703
|
-
msg = result.message or 'Completed successfully'
|
|
704
|
-
self._log_output.append(
|
|
705
|
-
f'<span style="color: {LOG_COLOR_SUCCESS};">\u2713 {html_mod.escape(msg)}</span>',
|
|
706
|
-
)
|
|
707
634
|
# Update version if an upgrade completed
|
|
708
635
|
new_version = result.available_version or self._check_available_version
|
|
709
636
|
if new_version:
|
|
@@ -712,10 +639,6 @@ class ActionCard(QFrame):
|
|
|
712
639
|
else:
|
|
713
640
|
self._status_label.setText('Failed')
|
|
714
641
|
self._status_label.setStyleSheet(ACTION_CARD_STATUS_FAILED)
|
|
715
|
-
msg = result.message or 'Unknown error'
|
|
716
|
-
self._log_output.append(
|
|
717
|
-
f'<span style="color: {LOG_COLOR_ERROR};">\u2717 {html_mod.escape(msg)}</span>',
|
|
718
|
-
)
|
|
719
642
|
|
|
720
643
|
# ------------------------------------------------------------------
|
|
721
644
|
# Public API — status text accessors (for counting)
|
|
@@ -735,15 +658,12 @@ class ActionCard(QFrame):
|
|
|
735
658
|
|
|
736
659
|
|
|
737
660
|
# ---------------------------------------------------------------------------
|
|
738
|
-
# ActionCardList —
|
|
661
|
+
# ActionCardList — card container
|
|
739
662
|
# ---------------------------------------------------------------------------
|
|
740
663
|
|
|
741
664
|
|
|
742
|
-
class ActionCardList(
|
|
743
|
-
"""
|
|
744
|
-
|
|
745
|
-
Replaces both the ``QTableWidget`` and the ``ExecutionLogPanel`` from
|
|
746
|
-
the previous install screen layout. One scrollbar, no nesting.
|
|
665
|
+
class ActionCardList(QWidget):
|
|
666
|
+
"""Container of :class:`ActionCard` widgets.
|
|
747
667
|
|
|
748
668
|
Cards are keyed by :func:`action_key` (content-based) so that
|
|
749
669
|
look-ups work across different ``execute_stream`` runs where the
|
|
@@ -756,18 +676,12 @@ class ActionCardList(QScrollArea):
|
|
|
756
676
|
def __init__(self, parent: QWidget | None = None) -> None:
|
|
757
677
|
"""Initialise the card list."""
|
|
758
678
|
super().__init__(parent)
|
|
759
|
-
self.setWidgetResizable(True)
|
|
760
|
-
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
|
761
|
-
self.setFrameShape(QFrame.Shape.NoFrame)
|
|
762
679
|
|
|
763
|
-
self.
|
|
764
|
-
self._layout = QVBoxLayout(self._container)
|
|
680
|
+
self._layout = QVBoxLayout(self)
|
|
765
681
|
self._layout.setContentsMargins(0, 0, 0, 0)
|
|
766
682
|
self._layout.setSpacing(ACTION_CARD_SPACING)
|
|
767
683
|
self._layout.addStretch()
|
|
768
684
|
|
|
769
|
-
self.setWidget(self._container)
|
|
770
|
-
|
|
771
685
|
self._cards: list[ActionCard] = []
|
|
772
686
|
self._action_map: dict[tuple[object, ...], ActionCard] = {}
|
|
773
687
|
|
|
@@ -785,7 +699,7 @@ class ActionCardList(QScrollArea):
|
|
|
785
699
|
"""
|
|
786
700
|
self.clear()
|
|
787
701
|
for _ in range(count):
|
|
788
|
-
card = ActionCard(self
|
|
702
|
+
card = ActionCard(self, skeleton=True)
|
|
789
703
|
self._layout.insertWidget(self._layout.count() - 1, card)
|
|
790
704
|
self._cards.append(card)
|
|
791
705
|
|
|
@@ -810,7 +724,7 @@ class ActionCardList(QScrollArea):
|
|
|
810
724
|
self.clear()
|
|
811
725
|
sorted_actions = sorted(actions, key=action_sort_key)
|
|
812
726
|
for act in sorted_actions:
|
|
813
|
-
card = ActionCard(self
|
|
727
|
+
card = ActionCard(self)
|
|
814
728
|
card.populate(
|
|
815
729
|
act,
|
|
816
730
|
plugin_installed=plugin_installed,
|
|
@@ -866,21 +780,3 @@ class ActionCardList(QScrollArea):
|
|
|
866
780
|
card.deleteLater()
|
|
867
781
|
self._cards.clear()
|
|
868
782
|
self._action_map.clear()
|
|
869
|
-
|
|
870
|
-
# ------------------------------------------------------------------
|
|
871
|
-
# Scroll helpers
|
|
872
|
-
# ------------------------------------------------------------------
|
|
873
|
-
|
|
874
|
-
def scroll_to_card(self, card: ActionCard) -> None:
|
|
875
|
-
"""Ensure *card* is visible in the scroll area."""
|
|
876
|
-
self.ensureWidgetVisible(card)
|
|
877
|
-
|
|
878
|
-
def scroll_to_card_bottom(self, card: ActionCard) -> None:
|
|
879
|
-
"""Scroll so the bottom of *card* is visible.
|
|
880
|
-
|
|
881
|
-
Used during execution to follow the growing inline log output.
|
|
882
|
-
Unlike :meth:`scroll_to_card` (which may show the top of a tall
|
|
883
|
-
card), this always brings the bottom edge into view.
|
|
884
|
-
"""
|
|
885
|
-
bottom_y = card.geometry().bottom()
|
|
886
|
-
self.ensureVisible(0, bottom_y, 0, 50)
|