vinsynlib 0.4.2__tar.gz → 0.5.0__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.
Files changed (63) hide show
  1. {vinsynlib-0.4.2/src/vinsynlib.egg-info → vinsynlib-0.5.0}/PKG-INFO +3 -3
  2. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/README.md +2 -2
  3. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/__init__.py +1 -1
  4. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/modals.py +14 -17
  5. {vinsynlib-0.4.2 → vinsynlib-0.5.0/src/vinsynlib.egg-info}/PKG-INFO +3 -3
  6. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_modals.py +48 -0
  7. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/COPYING +0 -0
  8. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/DISCLAIMER.md +0 -0
  9. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/LICENSE +0 -0
  10. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/MANIFEST.in +0 -0
  11. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/Makefile +0 -0
  12. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/docs/UX-SPEC.md +0 -0
  13. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/docs/unify2.md +0 -0
  14. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/pyproject.toml +0 -0
  15. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/setup.cfg +0 -0
  16. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/backup.py +0 -0
  17. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/build.py +0 -0
  18. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/catalog.py +0 -0
  19. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/cli.py +0 -0
  20. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/config.py +0 -0
  21. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/conformance.py +0 -0
  22. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/devchecks.py +0 -0
  23. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/favorites.py +0 -0
  24. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/keys.py +0 -0
  25. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/livenames.py +0 -0
  26. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/midi.py +0 -0
  27. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/py.typed +0 -0
  28. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/render.py +0 -0
  29. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/spec.py +0 -0
  30. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/terms.py +0 -0
  31. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/__init__.py +0 -0
  32. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/browser.py +0 -0
  33. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/confirm.py +0 -0
  34. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/hints.py +0 -0
  35. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/prompt.py +0 -0
  36. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/protocol.py +0 -0
  37. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/report.py +0 -0
  38. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/ui/work.py +0 -0
  39. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib/undo.py +0 -0
  40. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib.egg-info/SOURCES.txt +0 -0
  41. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib.egg-info/dependency_links.txt +0 -0
  42. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib.egg-info/requires.txt +0 -0
  43. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/src/vinsynlib.egg-info/top_level.txt +0 -0
  44. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_app_contract.py +0 -0
  45. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_backup.py +0 -0
  46. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_build.py +0 -0
  47. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_catalog.py +0 -0
  48. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_config.py +0 -0
  49. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_confirm_screen.py +0 -0
  50. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_favorites.py +0 -0
  51. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_hints.py +0 -0
  52. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_identity.py +0 -0
  53. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_midi_and_checks.py +0 -0
  54. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_prompt_screen.py +0 -0
  55. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_rating_cell.py +0 -0
  56. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_readme_modules.py +0 -0
  57. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_render.py +0 -0
  58. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_report.py +0 -0
  59. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_transport.py +0 -0
  60. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_undo.py +0 -0
  61. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_ux.py +0 -0
  62. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_version.py +0 -0
  63. {vinsynlib-0.4.2 → vinsynlib-0.5.0}/tests/test_work.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vinsynlib
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Shared base for ROMpler instrument browsers: settings, favourites, banks, dumps, CLI and key conventions
5
5
  License-Expression: GPL-2.0-or-later
6
6
  Project-URL: Homepage, https://github.com/lentferj/vinsynlib
@@ -133,11 +133,11 @@ unit or emulator **before** connecting irreplaceable equipment.
133
133
  ## Installing it, and who depends on it
134
134
 
135
135
  This is a library, not a program. It is published on PyPI — the current
136
- release is 0.4.2 — so the ordinary way to depend on it is a dependency
136
+ release is 0.5.0 — so the ordinary way to depend on it is a dependency
137
137
  line:
138
138
 
139
139
  ```toml
140
- vinsynlib>=0.4.2
140
+ vinsynlib>=0.5.0
141
141
  ```
142
142
 
143
143
  ```sh
@@ -107,11 +107,11 @@ unit or emulator **before** connecting irreplaceable equipment.
107
107
  ## Installing it, and who depends on it
108
108
 
109
109
  This is a library, not a program. It is published on PyPI — the current
110
- release is 0.4.2 — so the ordinary way to depend on it is a dependency
110
+ release is 0.5.0 — so the ordinary way to depend on it is a dependency
111
111
  line:
112
112
 
113
113
  ```toml
114
- vinsynlib>=0.4.2
114
+ vinsynlib>=0.5.0
115
115
  ```
116
116
 
117
117
  ```sh
@@ -56,7 +56,7 @@ __all__ = [
56
56
  "terms",
57
57
  ]
58
58
 
59
- __version__ = "0.4.2"
59
+ __version__ = "0.5.0"
60
60
 
61
61
 
62
62
  def _release_parts(version_string: str, width: int) -> tuple[int, ...] | None:
@@ -90,9 +90,11 @@ class ReportScreen(ModalScreen[None]):
90
90
  class ChoiceScreen(ModalScreen[int | None]):
91
91
  """A modal list where the user picks one index.
92
92
 
93
- The currently-selected item is marked with ``[x]``. ``enter`` confirms
94
- with ``priority=True`` because a ``DataTable`` would otherwise consume it.
95
- ``escape`` cancels and returns ``None``.
93
+ The currently-selected item is marked with ``[x]``. The focused
94
+ ``DataTable`` owns row navigation; ``_current`` follows the table's
95
+ highlighted row. ``enter`` confirms with ``priority=True`` because a
96
+ ``DataTable`` would otherwise consume it. ``escape`` cancels and
97
+ returns ``None``.
96
98
  """
97
99
 
98
100
  BINDINGS: ClassVar[list[Any]] = [
@@ -138,23 +140,18 @@ class ChoiceScreen(ModalScreen[int | None]):
138
140
  def on_mount(self) -> None:
139
141
  if self._table and self._choices:
140
142
  self._table.move_cursor(row=self._current)
143
+ self._table.focus()
141
144
  if self._status_message:
142
145
  self._notify(self._status_message)
143
146
 
144
- def on_key(self, event: Any) -> None:
145
- key = event.key if hasattr(event, "key") else ""
146
- if key == "up" and self._current > 0:
147
- self._current -= 1
148
- if self._table:
149
- self._table.move_cursor(row=self._current)
150
- self._refresh_marker()
151
- event.prevent_default()
152
- elif key == "down" and self._current < len(self._choices) - 1:
153
- self._current += 1
154
- if self._table:
155
- self._table.move_cursor(row=self._current)
156
- self._refresh_marker()
157
- event.prevent_default()
147
+ def on_data_table_row_highlighted(
148
+ self,
149
+ event: DataTable.RowHighlighted,
150
+ ) -> None:
151
+ if not self._table or event.data_table is not self._table:
152
+ return
153
+ self._current = event.cursor_row
154
+ self._refresh_marker()
158
155
 
159
156
  def action_confirm(self) -> None:
160
157
  if self._choices:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vinsynlib
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: Shared base for ROMpler instrument browsers: settings, favourites, banks, dumps, CLI and key conventions
5
5
  License-Expression: GPL-2.0-or-later
6
6
  Project-URL: Homepage, https://github.com/lentferj/vinsynlib
@@ -133,11 +133,11 @@ unit or emulator **before** connecting irreplaceable equipment.
133
133
  ## Installing it, and who depends on it
134
134
 
135
135
  This is a library, not a program. It is published on PyPI — the current
136
- release is 0.4.2 — so the ordinary way to depend on it is a dependency
136
+ release is 0.5.0 — so the ordinary way to depend on it is a dependency
137
137
  line:
138
138
 
139
139
  ```toml
140
- vinsynlib>=0.4.2
140
+ vinsynlib>=0.5.0
141
141
  ```
142
142
 
143
143
  ```sh
@@ -20,6 +20,7 @@ from __future__ import annotations
20
20
  from typing import Any, cast
21
21
 
22
22
  from textual.app import App, ComposeResult
23
+ from textual.coordinate import Coordinate
23
24
 
24
25
  from vinsynlib.catalog import make_key, split_key
25
26
  from vinsynlib.ui.modals import (
@@ -161,6 +162,53 @@ async def test_choice_screen_enter_priority_over_datatable() -> None:
161
162
  assert results == [1]
162
163
 
163
164
 
165
+ async def test_choice_screen_table_owns_navigation() -> None:
166
+ """The table cursor, markers and result must agree after arrow keys."""
167
+ screen = ChoiceScreen("Pick", ["a", "b", "c"], current=1)
168
+ results: list[Any] = []
169
+
170
+ def collect(result: Any) -> None:
171
+ results.append(result)
172
+
173
+ def markers() -> list[str]:
174
+ table = screen._table
175
+ assert table is not None
176
+ return [
177
+ str(table.get_cell_at(Coordinate(row, 0)))
178
+ for row in range(len(screen._choices))
179
+ ]
180
+
181
+ async with ModalApp().run_test() as pilot:
182
+ await pilot.app.push_screen(screen, callback=collect)
183
+ await pilot.pause()
184
+ table = screen._table
185
+ assert table is not None
186
+ assert (table.cursor_row, screen._current) == (1, 1)
187
+ assert markers() == ["[ ]", "[x]", "[ ]"]
188
+
189
+ await pilot.press("down")
190
+ await pilot.pause()
191
+ assert (table.cursor_row, screen._current) == (2, 2)
192
+ assert markers() == ["[ ]", "[ ]", "[x]"]
193
+
194
+ # Returning to the previous row must not leave the cursor one row
195
+ # beyond it, which is what the old on_key handler did.
196
+ await pilot.press("up")
197
+ await pilot.pause()
198
+ assert (table.cursor_row, screen._current) == (1, 1)
199
+ assert markers() == ["[ ]", "[x]", "[ ]"]
200
+
201
+ await pilot.press("up")
202
+ await pilot.pause()
203
+ assert (table.cursor_row, screen._current) == (0, 0)
204
+ assert markers() == ["[x]", "[ ]", "[ ]"]
205
+
206
+ await pilot.press("enter")
207
+ await pilot.pause()
208
+
209
+ assert results == [0]
210
+
211
+
164
212
  async def test_choice_screen_sends_status() -> None:
165
213
  screen = ChoiceScreen("Pick", ["a", "b"], status_message="Choose one")
166
214
  async with ModalApp().run_test() as pilot:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes