nextpytk 0.4.6__tar.gz → 0.4.7__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.
- {nextpytk-0.4.6 → nextpytk-0.4.7}/PKG-INFO +6 -1
- {nextpytk-0.4.6 → nextpytk-0.4.7}/README.md +5 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/ROADMAP.md +50 -1
- {nextpytk-0.4.6 → nextpytk-0.4.7}/pyproject.toml +1 -1
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/app.py +1 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/layout.py +56 -14
- {nextpytk-0.4.6 → nextpytk-0.4.7}/.gitignore +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/LICENSE +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/__init__.py +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/theme.py +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/tokens.py +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/types.py +0 -0
- {nextpytk-0.4.6 → nextpytk-0.4.7}/src/nextpytk/widgets.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nextpytk
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
4
4
|
Summary: Accessible, declarative Tkinter applications from ordinary Python functions.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nishimotz/nextpytk
|
|
6
6
|
Project-URL: Repository, https://github.com/nishimotz/nextpytk
|
|
@@ -495,6 +495,11 @@ panes and both gutters share a **single vertical scrollbar**, so the gutters
|
|
|
495
495
|
stay in lock-step with the content (no drift). Logical line numbers
|
|
496
496
|
(`1..n`) follow the pane content; the right pane's numbers update as you edit.
|
|
497
497
|
|
|
498
|
+
> **Note:** `line_numbers=True` takes precedence over `sync_yscroll`. The
|
|
499
|
+
> shared-scrollbar layout it installs keeps both panes (and both gutters)
|
|
500
|
+
> scrolled in lock-step, so passing `sync_yscroll=False` together with
|
|
501
|
+
> `line_numbers=True` has no effect. If the panes must scroll independently,
|
|
502
|
+
> leave `line_numbers` off.
|
|
498
503
|
|
|
499
504
|
### Wrap layout
|
|
500
505
|
|
|
@@ -471,6 +471,11 @@ panes and both gutters share a **single vertical scrollbar**, so the gutters
|
|
|
471
471
|
stay in lock-step with the content (no drift). Logical line numbers
|
|
472
472
|
(`1..n`) follow the pane content; the right pane's numbers update as you edit.
|
|
473
473
|
|
|
474
|
+
> **Note:** `line_numbers=True` takes precedence over `sync_yscroll`. The
|
|
475
|
+
> shared-scrollbar layout it installs keeps both panes (and both gutters)
|
|
476
|
+
> scrolled in lock-step, so passing `sync_yscroll=False` together with
|
|
477
|
+
> `line_numbers=True` has no effect. If the panes must scroll independently,
|
|
478
|
+
> leave `line_numbers` off.
|
|
474
479
|
|
|
475
480
|
### Wrap layout
|
|
476
481
|
|
|
@@ -299,7 +299,56 @@ visual row-major order). Planned to generalize for `side="bottom"` sections.
|
|
|
299
299
|
|
|
300
300
|
---
|
|
301
301
|
|
|
302
|
-
##
|
|
302
|
+
## Snapshot (v0.4.7) — paired gutter logical-line fix
|
|
303
|
+
|
|
304
|
+
### Bug fix: line-number gutters count logical lines, not display lines
|
|
305
|
+
|
|
306
|
+
- `Layout.paired(..., line_numbers=True)` gutters numbered **physical**
|
|
307
|
+
(wrapped) rows: `_reconcile_gutter` / `_populate_gutters` used
|
|
308
|
+
`text.index("end-1c")`, which counts display rows. With `wrap="word"` /
|
|
309
|
+
`"char"`, a long line that wrapped inflated the gutter count beyond the
|
|
310
|
+
real line count.
|
|
311
|
+
- Added `_logical_line_count(text)`, which counts newline characters (a
|
|
312
|
+
wrapped row adds no newline), adjusted for whether the buffer ends in a
|
|
313
|
+
newline. The gutter now mirrors true logical lines regardless of wrapping.
|
|
314
|
+
|
|
315
|
+
### Bug fix: re-entry recursion in gutter sync
|
|
316
|
+
|
|
317
|
+
- Rewriting a gutter calls `update_idletasks()`, which fires the shared
|
|
318
|
+
scrollbar's `yscrollcommand` and re-enters `_reconcile_gutter` /
|
|
319
|
+
`_populate_gutters` through `_chain_yview` — an infinite recursion that
|
|
320
|
+
surfaced once the gutter logic changed.
|
|
321
|
+
- Added a per-gutter `_syncing_gutter` re-entry guard to both helpers.
|
|
322
|
+
|
|
323
|
+
### Bug fix: `on_text_set` hook leak across re-runs
|
|
324
|
+
|
|
325
|
+
- `app.on_text_set()` hooks were never cleared by `clear_runtime()`, so
|
|
326
|
+
hooks (e.g. gutter line-number sync) accumulated across re-runs — such as
|
|
327
|
+
swap variants rebuilt at runtime — causing the gutter sync to run multiple
|
|
328
|
+
times per `text_set`.
|
|
329
|
+
- `clear_runtime()` now also clears `_text_set_hooks`.
|
|
330
|
+
|
|
331
|
+
### Tests
|
|
332
|
+
|
|
333
|
+
- Added `test_paired_gutters_count_logical_lines_when_wrapped` (a long,
|
|
334
|
+
wrapping line must not inflate the gutter line numbers).
|
|
335
|
+
- Added `test_paired_gutter_sync_does_not_recursively_loop` (multiple
|
|
336
|
+
`text_set` calls do not recurse infinitely through the shared scrollbar).
|
|
337
|
+
- Added `test_clear_runtime_clears_text_set_hooks` (hooks are dropped on
|
|
338
|
+
`clear_runtime()`).
|
|
339
|
+
- `tests/test_paired.py` grew; full suite green.
|
|
340
|
+
|
|
341
|
+
### Docs: `line_numbers=True` takes precedence over `sync_yscroll`
|
|
342
|
+
|
|
343
|
+
- Documented that enabling `line_numbers` installs a shared scrollbar that
|
|
344
|
+
always keeps both panes (and both gutters) in lock-step, so
|
|
345
|
+
`sync_yscroll=False` is ignored when `line_numbers=True`. Callers needing
|
|
346
|
+
independent pane scrolling must leave `line_numbers` off. Clarified in the
|
|
347
|
+
`Layout.paired` docstring and README (EN / JA).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Near term (post-0.4.7)
|
|
303
352
|
|
|
304
353
|
### A11y
|
|
305
354
|
|
|
@@ -783,6 +783,25 @@ def _place_paired_with_gutters(
|
|
|
783
783
|
_bind_gutter_edit_sync(app, text_b, gutter_b)
|
|
784
784
|
|
|
785
785
|
|
|
786
|
+
def _logical_line_count(text: tk.Text) -> int:
|
|
787
|
+
"""Return the number of logical (non-wrapped) lines in *text*.
|
|
788
|
+
|
|
789
|
+
``index("end-1c")`` counts *display* lines: a long line that wraps to
|
|
790
|
+
several rows inflates the count when ``wrap`` is ``word``/``char``. We
|
|
791
|
+
count newline characters instead, which is wrap-independent. The last
|
|
792
|
+
logical line may or may not carry a trailing newline (``text_set`` omits
|
|
793
|
+
it; a hand-inserted buffer may keep it), so the count is adjusted by
|
|
794
|
+
whether the content ends with a newline.
|
|
795
|
+
"""
|
|
796
|
+
try:
|
|
797
|
+
content = text.get("1.0", "end-1c")
|
|
798
|
+
except tk.TclError:
|
|
799
|
+
return 0
|
|
800
|
+
if not content:
|
|
801
|
+
return 0
|
|
802
|
+
return content.count("\n") + (0 if content.endswith("\n") else 1)
|
|
803
|
+
|
|
804
|
+
|
|
786
805
|
def _reconcile_gutter(gutter: tk.Text, text: tk.Text) -> None:
|
|
787
806
|
"""Fill a gutter with logical line numbers, matching the text line count.
|
|
788
807
|
|
|
@@ -790,17 +809,26 @@ def _reconcile_gutter(gutter: tk.Text, text: tk.Text) -> None:
|
|
|
790
809
|
gutter is rewritten so its scroll range matches the pane. Idempotent:
|
|
791
810
|
rewriting only happens when the count differs.
|
|
792
811
|
"""
|
|
793
|
-
|
|
812
|
+
# Re-entry guard: rewriting the gutter fires yscrollcommand on the shared
|
|
813
|
+
# scrollbar (via update_idletasks below), which re-enters this function
|
|
814
|
+
# through _chain_yview. Without a guard that recursion never terminates.
|
|
815
|
+
if getattr(gutter, "_syncing_gutter", False):
|
|
816
|
+
return
|
|
817
|
+
n = _logical_line_count(text)
|
|
794
818
|
gutter_lines = int(gutter.index("end-1c").split(".")[0])
|
|
795
819
|
if gutter_lines == n:
|
|
796
820
|
return
|
|
797
821
|
lines = "\n".join(str(i) for i in range(1, n + 1))
|
|
798
|
-
gutter.
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
822
|
+
gutter._syncing_gutter = True # type: ignore[attr-defined]
|
|
823
|
+
try:
|
|
824
|
+
gutter.configure(state="normal")
|
|
825
|
+
gutter.delete("1.0", "end")
|
|
826
|
+
gutter.insert("1.0", lines)
|
|
827
|
+
gutter.configure(state="disabled")
|
|
828
|
+
# Ensure the new content is reflected in the shared scrollbar range.
|
|
829
|
+
gutter.update_idletasks()
|
|
830
|
+
finally:
|
|
831
|
+
gutter._syncing_gutter = False # type: ignore[attr-defined]
|
|
804
832
|
|
|
805
833
|
|
|
806
834
|
def _chain_yview(
|
|
@@ -857,14 +885,20 @@ def _make_gutter(parent: tk.Misc, name: str) -> tk.Text:
|
|
|
857
885
|
|
|
858
886
|
def _populate_gutters(gutter: tk.Text, text: tk.Text) -> None:
|
|
859
887
|
"""Fill a gutter with logical line numbers (1..n)."""
|
|
860
|
-
|
|
888
|
+
if getattr(gutter, "_syncing_gutter", False):
|
|
889
|
+
return
|
|
890
|
+
n = _logical_line_count(text)
|
|
861
891
|
lines = "\n".join(str(i) for i in range(1, n + 1))
|
|
862
|
-
gutter.
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
892
|
+
gutter._syncing_gutter = True # type: ignore[attr-defined]
|
|
893
|
+
try:
|
|
894
|
+
gutter.configure(state="normal")
|
|
895
|
+
gutter.delete("1.0", "end")
|
|
896
|
+
gutter.insert("1.0", lines)
|
|
897
|
+
gutter.configure(state="disabled")
|
|
898
|
+
# Ensure the new content is reflected in the shared scrollbar range.
|
|
899
|
+
gutter.update_idletasks()
|
|
900
|
+
finally:
|
|
901
|
+
gutter._syncing_gutter = False # type: ignore[attr-defined]
|
|
868
902
|
|
|
869
903
|
|
|
870
904
|
def _bind_gutter_edit_sync(app: TkApp, text: tk.Text, gutter: tk.Text) -> None:
|
|
@@ -1347,6 +1381,14 @@ class Layout:
|
|
|
1347
1381
|
the gutters always stay in lock-step with the content (no drift). Line
|
|
1348
1382
|
numbers are logical rows of the text widget (``1..n``).
|
|
1349
1383
|
|
|
1384
|
+
.. note::
|
|
1385
|
+
``line_numbers=True`` takes precedence over ``sync_yscroll``: the
|
|
1386
|
+
shared-scrollbar layout it installs always keeps the two panes (and
|
|
1387
|
+
both gutters) scrolled in lock-step, so passing
|
|
1388
|
+
``sync_yscroll=False`` with ``line_numbers=True`` has no effect. If
|
|
1389
|
+
you need the panes to scroll independently, do **not** enable
|
|
1390
|
+
``line_numbers``.
|
|
1391
|
+
|
|
1350
1392
|
Example::
|
|
1351
1393
|
|
|
1352
1394
|
app.text("left", readonly=True, sync_yscroll_with="right")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|