Compare commits
3 Commits
perf/load-
...
de32a524da
| Author | SHA1 | Date | |
|---|---|---|---|
| de32a524da | |||
| 2515213b14 | |||
| 0376b77494 |
16
README.md
16
README.md
@@ -129,6 +129,22 @@ A VSCode / VSCodium client extension (`testium_assist`) wraps `testium lsp`;
|
|||||||
the schema is built from testium itself, so new item types and parameters
|
the schema is built from testium itself, so new item types and parameters
|
||||||
appear in the editor on the next testium upgrade with no client change.
|
appear in the editor on the next testium upgrade with no client change.
|
||||||
|
|
||||||
|
It is published on [Open VSX](https://open-vsx.org/extension/testium/testium-assist),
|
||||||
|
so in **VSCodium, Cursor, Windsurf, Theia and code-server** it installs from the
|
||||||
|
Extensions view (search `testium-assist`) or with
|
||||||
|
`codium --install-extension testium.testium-assist`.
|
||||||
|
|
||||||
|
**Microsoft VSCode** does not list Open VSX extensions, so install the `.vsix`
|
||||||
|
by hand — download it from the Open VSX page above, then *Extensions → ⋯ →
|
||||||
|
Install from VSIX…* or:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
code --install-extension testium-assist-0.1.0.vsix
|
||||||
|
```
|
||||||
|
|
||||||
|
The extension runs `testium lsp`, so `testium` must be on the `PATH` (otherwise
|
||||||
|
point the `testium.serverPath` setting at the binary/AppImage).
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### `wl_proxy_marshal_flags` symbol error
|
### `wl_proxy_marshal_flags` symbol error
|
||||||
|
|||||||
@@ -67,3 +67,36 @@ dependencies:
|
|||||||
:caption: enable the language server for a wheel / source install
|
:caption: enable the language server for a wheel / source install
|
||||||
|
|
||||||
pip install 'testium[lsp]'
|
pip install 'testium[lsp]'
|
||||||
|
|
||||||
|
Installing the VSCode / VSCodium extension
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The *testium_assist* client extension is published on `Open VSX
|
||||||
|
<https://open-vsx.org/extension/testium/testium-assist>`_, the registry used by
|
||||||
|
VSCodium, Cursor, Windsurf, Eclipse Theia and code-server. In those editors,
|
||||||
|
open the Extensions view and search ``testium-assist``, or install it from the
|
||||||
|
command line:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:caption: install in VSCodium and other Open VSX editors
|
||||||
|
|
||||||
|
codium --install-extension testium.testium-assist
|
||||||
|
|
||||||
|
Microsoft *VSCode* uses a different marketplace that does not list Open VSX
|
||||||
|
extensions, so install the packaged ``.vsix`` by hand. Download it from the
|
||||||
|
Open VSX page linked above, then either choose *Extensions* → *⋯* →
|
||||||
|
*Install from VSIX…* in the UI, or run:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:caption: install the .vsix in Microsoft VSCode
|
||||||
|
|
||||||
|
code --install-extension testium-assist-0.1.0.vsix
|
||||||
|
|
||||||
|
The extension launches ``testium lsp``, so the ``testium`` command must be on
|
||||||
|
the ``PATH``. If *testium* is installed elsewhere — a specific binary or an
|
||||||
|
AppImage — point the ``testium.serverPath`` setting at it instead.
|
||||||
|
|
||||||
|
Once installed, open a ``.tum`` file: completion of item types, hover
|
||||||
|
documentation and the outline view become available. If nothing happens, check
|
||||||
|
that no ``files.associations`` entry forces ``*.tum`` to another language (it
|
||||||
|
must stay the ``tum`` language the extension provides).
|
||||||
|
|||||||
Binary file not shown.
@@ -1 +1 @@
|
|||||||
0.2
|
0.2.1
|
||||||
|
|||||||
95
src/testium/main_win/desktop_integration.py
Normal file
95
src/testium/main_win/desktop_integration.py
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
"""Install a desktop entry + icon under the user's data dir so desktop shells
|
||||||
|
show the testium icon in the task bar / dock.
|
||||||
|
|
||||||
|
On a native Wayland session GNOME takes a window's task-bar icon from the
|
||||||
|
``.desktop`` file whose name (or ``StartupWMClass``) matches the window
|
||||||
|
``app_id`` — ``QGuiApplication.setWindowIcon`` is ignored there. The portable
|
||||||
|
channels (source checkout, PyInstaller binary, AppImage) install no system
|
||||||
|
desktop file, so we drop an idempotent one in ``~/.local/share``. The window
|
||||||
|
``app_id`` is set to ``testium`` (see ``QApplication.setDesktopFileName`` in
|
||||||
|
``testium_win``), which is exactly this file's base name.
|
||||||
|
|
||||||
|
Flatpak ships its own ``org.testium.Testium.desktop`` and keeps its own app id,
|
||||||
|
so the caller skips this integration there.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
from PySide6.QtGui import QPixmap
|
||||||
|
|
||||||
|
# Must match QApplication.setDesktopFileName(...) for the GUI, and is used as
|
||||||
|
# both the desktop-file base name and the StartupWMClass.
|
||||||
|
APP_ID = "testium"
|
||||||
|
|
||||||
|
|
||||||
|
def _launch_command():
|
||||||
|
"""Best-effort Exec= for the menu entry. Not needed for icon matching, but
|
||||||
|
makes the entry actually launchable when possible."""
|
||||||
|
appimage = os.environ.get("APPIMAGE")
|
||||||
|
if appimage:
|
||||||
|
return f'"{appimage}"'
|
||||||
|
if getattr(sys, "frozen", False):
|
||||||
|
return f'"{os.path.abspath(sys.executable)}"'
|
||||||
|
argv0 = os.path.abspath(sys.argv[0]) if sys.argv and sys.argv[0] else ""
|
||||||
|
if argv0 and os.path.exists(argv0):
|
||||||
|
return f'"{os.path.abspath(sys.executable)}" "{argv0}"'
|
||||||
|
return f'"{os.path.abspath(sys.executable)}" -m testium'
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_desktop_entry():
|
||||||
|
"""Create (or refresh) ~/.local/share icon + desktop entry. Best-effort:
|
||||||
|
any failure is swallowed so it can never take the GUI down.
|
||||||
|
|
||||||
|
Freedesktop-only: a no-op off Linux (Windows / macOS use the window icon)."""
|
||||||
|
if not sys.platform.startswith("linux"):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
data_home = os.environ.get("XDG_DATA_HOME") or os.path.join(
|
||||||
|
os.path.expanduser("~"), ".local", "share"
|
||||||
|
)
|
||||||
|
icon_dir = os.path.join(data_home, "icons", "hicolor", "256x256", "apps")
|
||||||
|
app_dir = os.path.join(data_home, "applications")
|
||||||
|
icon_path = os.path.join(icon_dir, f"{APP_ID}.png")
|
||||||
|
desktop_path = os.path.join(app_dir, f"{APP_ID}.desktop")
|
||||||
|
|
||||||
|
os.makedirs(icon_dir, exist_ok=True)
|
||||||
|
os.makedirs(app_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Icon: render the bundled Qt resource to a PNG once. Requires a live
|
||||||
|
# QGuiApplication (the caller creates it before calling us).
|
||||||
|
if not os.path.isfile(icon_path):
|
||||||
|
pixmap = QPixmap(u":/black/testium_logo.png")
|
||||||
|
if not pixmap.isNull():
|
||||||
|
pixmap = pixmap.scaled(
|
||||||
|
256, 256,
|
||||||
|
Qt.AspectRatioMode.KeepAspectRatio,
|
||||||
|
Qt.TransformationMode.SmoothTransformation,
|
||||||
|
)
|
||||||
|
pixmap.save(icon_path, "PNG")
|
||||||
|
|
||||||
|
# Absolute Icon= path so the shell resolves it without an icon-cache
|
||||||
|
# refresh; StartupWMClass lets X11 / XWayland match too.
|
||||||
|
desktop = (
|
||||||
|
"[Desktop Entry]\n"
|
||||||
|
"Type=Application\n"
|
||||||
|
"Name=Testium\n"
|
||||||
|
"Comment=Test sequencer\n"
|
||||||
|
f"Icon={icon_path}\n"
|
||||||
|
f"Exec={_launch_command()} %f\n"
|
||||||
|
"Terminal=false\n"
|
||||||
|
f"StartupWMClass={APP_ID}\n"
|
||||||
|
"Categories=Utility;Development;\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Write only when missing or changed, to avoid needless menu churn.
|
||||||
|
current = None
|
||||||
|
if os.path.isfile(desktop_path):
|
||||||
|
with open(desktop_path, "r") as fh:
|
||||||
|
current = fh.read()
|
||||||
|
if current != desktop:
|
||||||
|
with open(desktop_path, "w") as fh:
|
||||||
|
fh.write(desktop)
|
||||||
|
except Exception:
|
||||||
|
# Desktop integration is a nicety, never a hard requirement.
|
||||||
|
pass
|
||||||
@@ -678,6 +678,24 @@ def MainWin(
|
|||||||
debug=False,
|
debug=False,
|
||||||
):
|
):
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
# Application identity so desktop shells (GNOME, ...) show the testium
|
||||||
|
# icon in the task bar / dock instead of a generic one. On Wayland this
|
||||||
|
# sets the surface app_id; on X11/XWayland it sets WM_CLASS, so the window
|
||||||
|
# stops inheriting the launcher's class (e.g. "python3" under the AppImage,
|
||||||
|
# which is what GNOME was keying the wrong icon off) and the window icon
|
||||||
|
# below is used as the fallback. In Flatpak the id must be the Flatpak app
|
||||||
|
# id so it matches the installed desktop file.
|
||||||
|
app.setApplicationName("Testium")
|
||||||
|
app.setApplicationDisplayName("Testium")
|
||||||
|
app.setDesktopFileName(os.environ.get("FLATPAK_ID", "testium"))
|
||||||
|
app.setWindowIcon(QIcon(u":/black/testium_logo.png"))
|
||||||
|
# On native Wayland the task-bar icon comes from an installed desktop file
|
||||||
|
# matched to the app_id, not from setWindowIcon(). Flatpak ships its own;
|
||||||
|
# for the other Linux channels drop an idempotent one under ~/.local/share.
|
||||||
|
# Windows / macOS use the window icon set above, so this is Linux-only.
|
||||||
|
if sys.platform.startswith("linux") and not os.environ.get("FLATPAK_ID"):
|
||||||
|
from main_win.desktop_integration import ensure_desktop_entry
|
||||||
|
ensure_desktop_entry()
|
||||||
ui = MainWindow(
|
ui = MainWindow(
|
||||||
test_file,
|
test_file,
|
||||||
config_files,
|
config_files,
|
||||||
|
|||||||
Reference in New Issue
Block a user