urwid 3.0.4__tar.gz → 4.0.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.
- {urwid-3.0.4 → urwid-4.0.0}/.github/workflows/documentation.yml +1 -1
- {urwid-3.0.4 → urwid-4.0.0}/.github/workflows/pythonpackage.yml +4 -4
- {urwid-3.0.4 → urwid-4.0.0}/PKG-INFO +2 -2
- {urwid-3.0.4 → urwid-4.0.0}/docs/changelog.rst +22 -1
- urwid-4.0.0/docs/examples/edit_text.txt +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/real_edit.py +2 -1
- {urwid-3.0.4 → urwid-4.0.0}/examples/edit.py +2 -1
- {urwid-3.0.4 → urwid-4.0.0}/pyproject.toml +3 -8
- urwid-4.0.0/requirements.txt +1 -0
- urwid-4.0.0/ruff-requirements.txt +1 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_event_loops.py +1 -0
- urwid-4.0.0/tests/test_grapheme_clusters.py +210 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_scrollable.py +2 -1
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_text_layout.py +15 -3
- {urwid-3.0.4 → urwid-4.0.0}/urwid/__init__.py +1 -9
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_posix_raw_display.py +4 -1
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/common.py +2 -1
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/main_loop.py +0 -21
- {urwid-3.0.4 → urwid-4.0.0}/urwid/font.py +7 -22
- {urwid-3.0.4 → urwid-4.0.0}/urwid/signals.py +9 -18
- {urwid-3.0.4 → urwid-4.0.0}/urwid/str_util.py +90 -42
- {urwid-3.0.4 → urwid-4.0.0}/urwid/text_layout.py +8 -6
- urwid-4.0.0/urwid/version.py +24 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/vterm.py +2 -2
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/attr_wrap.py +0 -16
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/columns.py +0 -20
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/filler.py +0 -17
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/listbox.py +1 -1
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/monitored_list.py +0 -21
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/pile.py +0 -24
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/text.py +16 -2
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/wimp.py +10 -13
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/PKG-INFO +2 -2
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/SOURCES.txt +1 -4
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/requires.txt +1 -1
- urwid-3.0.4/docs/examples/edit_text.txt +0 -11
- urwid-3.0.4/requirements.txt +0 -1
- urwid-3.0.4/ruff-requirements.txt +0 -1
- urwid-3.0.4/tests/test_moved_imports.py +0 -19
- urwid-3.0.4/urwid/container.py +0 -59
- urwid-3.0.4/urwid/decoration.py +0 -65
- urwid-3.0.4/urwid/version.py +0 -34
- urwid-3.0.4/urwid/wimp.py +0 -23
- {urwid-3.0.4 → urwid-4.0.0}/.coveralls.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.devcontainer/dev.dockerfile +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.devcontainer/devcontainer.json +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.dockerignore +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.editorconfig +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/CODE_OF_CONDUCT.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/ISSUE_TEMPLATE/docs.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/dependabot.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/labeler.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/release.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/workflows/isolated_static_check.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/workflows/labels.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.github/workflows/yamllint.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.gitignore +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.pre-commit-config.yaml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/.yamllint.yml +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/COPYING +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/MANIFEST.in +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/README.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/classifiers.txt +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/Makefile +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/conf.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/bigtext.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/bigtext.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/bigtext1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/bigtext2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/bigtext3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/browse.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/browse.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/browse1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/browse2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/edit.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/edit.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/edit1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/edit2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/graph.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/graph.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/graph1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/graph2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/index.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/palette_test.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/palette_test.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/palette_test1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/palette_test2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/pop_up.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/pop_up.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/pop_up1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/pop_up2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/real_browse.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/subproc.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/subproc.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/subproc1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/subproc2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/subproc2.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/tour.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/tour.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/tour1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/examples/tour2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/index.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/bright_combinations.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/bright_combinations.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/bright_combinations1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/canvascache.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/displayattributes.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/displaymodules.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/encodings.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/display_modules.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/introduction.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/urwid_widgets.svgz +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/urwid_widgets_1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/urwid_widgets_1.xcf +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/urwid_widgets_2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/urwid_widgets_2.xcf +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/images/widget_layout.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/index.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/mainloop.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/overview.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/safe_combinations.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/safe_combinations.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/safe_combinations1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/textlayout.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/userinput.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wanat.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wanat_multi.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wcur1.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wcur2.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/widgets.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wmod.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/manual/wsel.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/attrspec.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/canvas.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/command_map.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/constants.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/deprecated.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/display_modules.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/exceptions.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/global_settings.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/index.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/list_walkers.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/main_loop.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/meta.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/signals.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/text_layout.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/reference/widget.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/compile_pngs.sh +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/screenshots.sh +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/static/.placeholder +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/templates/indexcontent.html +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/templates/indexsidebar.html +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tools/templates/localtoc.html +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/adventure4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/cmenu4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/highcolors.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/highcolors.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/highcolors1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/hmenu4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/index.rst +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/input5.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/menu25.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/minimal.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/minimal.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/minimal1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/multiple4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/qa.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/qa.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/qa1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/qa2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/qa3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/sig4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/smenu.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/smenu.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/smenu1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/smenu2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/smenu3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr.py.xdotool +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr1.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr2.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr3.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/tutorial/urwid_attr4.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/docs/urwid-logo.png +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/asyncio_socket_server.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/bigtext.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/browse.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/calc.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/dialog.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/fib.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/graph.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/input_test.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/lcd_cf635.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/palette_test.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/pop_up.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/subproc.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/subproc2.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/terminal.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/tour.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/treesample.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/twisted_serve_ssh.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/examples/twisted_serve_ssh.tac +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/pylint-requirements.txt +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/setup.cfg +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/test_requirements.txt +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/__init__.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_canvas.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_columns.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_container.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_doctests.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_escapes.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_filler.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_floatedit.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_font.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_frame.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_graphics.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_grid_flow.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_line_box.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_listbox.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_main_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_overlay.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_padding.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_pile.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_raw_display.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_signals.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_str_util.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_tree.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_util.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_vterm.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/test_widget.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tests/util.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/tox.ini +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/canvas.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/command_map.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/__init__.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_raw_display_base.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_web.css +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_web.js +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_win32.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/_win32_raw_display.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/curses.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/escape.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/html_fragment.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/lcd.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/raw.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/display/web.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/__init__.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/abstract_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/asyncio_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/glib_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/select_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/tornado_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/trio_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/twisted_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/event_loop/zmq_loop.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/graphics.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/numedit.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/split_repr.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/util.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/__init__.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/attr_map.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/bar_graph.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/big_text.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/box_adapter.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/constants.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/container.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/divider.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/edit.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/frame.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/grid_flow.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/line_box.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/overlay.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/padding.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/popup.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/progress_bar.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/scrollable.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/solid_fill.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/treetools.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/widget.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid/widget/widget_decoration.py +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/dependency_links.txt +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/not-zip-safe +0 -0
- {urwid-3.0.4 → urwid-4.0.0}/urwid.egg-info/top_level.txt +0 -0
|
@@ -172,13 +172,13 @@ jobs:
|
|
|
172
172
|
- name: Build dist
|
|
173
173
|
run: python -m build
|
|
174
174
|
|
|
175
|
-
- uses: actions/upload-artifact@
|
|
175
|
+
- uses: actions/upload-artifact@v7
|
|
176
176
|
with:
|
|
177
177
|
path: dist/*.tar.gz
|
|
178
178
|
name: built-sdist
|
|
179
179
|
retention-days: 3
|
|
180
180
|
|
|
181
|
-
- uses: actions/upload-artifact@
|
|
181
|
+
- uses: actions/upload-artifact@v7
|
|
182
182
|
with:
|
|
183
183
|
path: dist/*.whl
|
|
184
184
|
name: built-bdist
|
|
@@ -199,7 +199,7 @@ jobs:
|
|
|
199
199
|
run: |
|
|
200
200
|
python -m pip install --upgrade pip
|
|
201
201
|
pip install --upgrade twine
|
|
202
|
-
- uses: actions/download-artifact@
|
|
202
|
+
- uses: actions/download-artifact@v8
|
|
203
203
|
with:
|
|
204
204
|
# unpacks default artifact into dist/
|
|
205
205
|
# if `name: wheels` is omitted, the action will create extra parent dir
|
|
@@ -224,7 +224,7 @@ jobs:
|
|
|
224
224
|
permissions:
|
|
225
225
|
id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
|
|
226
226
|
steps:
|
|
227
|
-
- uses: actions/download-artifact@
|
|
227
|
+
- uses: actions/download-artifact@v8
|
|
228
228
|
with:
|
|
229
229
|
pattern: built-*
|
|
230
230
|
merge-multiple: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: urwid
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0
|
|
4
4
|
Summary: A full-featured console (xterm et al.) user interface library
|
|
5
5
|
Author-email: Ian Ward <ian@excess.org>
|
|
6
6
|
License-Expression: LGPL-2.1-only
|
|
@@ -33,7 +33,7 @@ Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
|
33
33
|
Requires-Python: >=3.9.0
|
|
34
34
|
Description-Content-Type: text/x-rst
|
|
35
35
|
License-File: COPYING
|
|
36
|
-
Requires-Dist: wcwidth
|
|
36
|
+
Requires-Dist: wcwidth>=0.4
|
|
37
37
|
Provides-Extra: curses
|
|
38
38
|
Requires-Dist: windows-curses; sys_platform == "win32" and extra == "curses"
|
|
39
39
|
Provides-Extra: glib
|
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
---------
|
|
4
4
|
|
|
5
|
+
Urwid 3.0.5
|
|
6
|
+
===========
|
|
7
|
+
|
|
8
|
+
2026-02-02
|
|
9
|
+
|
|
10
|
+
Deprecations ⚡
|
|
11
|
+
++++++++++++++
|
|
12
|
+
* Un-deprecate `user_arg` for signals by @penguinolog in https://github.com/urwid/urwid/pull/1092
|
|
13
|
+
Bug fixes 🕷
|
|
14
|
+
++++++++++++
|
|
15
|
+
* Fix terminal scroll TypeError by @penguinolog in https://github.com/urwid/urwid/pull/1098
|
|
16
|
+
* Bugfix some kinds of emojis with grapheme boundry support by @jquast in https://github.com/urwid/urwid/pull/1100
|
|
17
|
+
* Fix: Text.pack line splitting not match Text.render by @penguinolog in https://github.com/urwid/urwid/pull/1099
|
|
18
|
+
|
|
19
|
+
New Contributors
|
|
20
|
+
++++++++++++++++
|
|
21
|
+
* @jquast made their first contribution in https://github.com/urwid/urwid/pull/1100
|
|
22
|
+
|
|
23
|
+
**Full Changelog**: https://github.com/urwid/urwid/compare/3.0.4...3.0.5
|
|
24
|
+
|
|
5
25
|
Urwid 3.0.4
|
|
6
26
|
===========
|
|
7
27
|
|
|
@@ -14,7 +34,8 @@ Other Changes
|
|
|
14
34
|
+++++++++++++
|
|
15
35
|
* Refactoring: use official python way for lazy imports by @penguinolog in https://github.com/urwid/urwid/pull/1077
|
|
16
36
|
|
|
17
|
-
|
|
37
|
+
New Contributors
|
|
38
|
+
++++++++++++++++
|
|
18
39
|
* @sawdmn made their first contribution in https://github.com/urwid/urwid/pull/1079
|
|
19
40
|
|
|
20
41
|
**Full Changelog**: https://github.com/urwid/urwid/compare/3.0.3...3.0.4
|
|
Binary file
|
|
@@ -30,14 +30,9 @@ platforms=["unix-like"]
|
|
|
30
30
|
zip-safe = false
|
|
31
31
|
|
|
32
32
|
[tool.setuptools.packages.find]
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"examples",
|
|
37
|
-
"test",
|
|
38
|
-
"tests",
|
|
39
|
-
"bin",
|
|
40
|
-
".*"
|
|
33
|
+
include = [
|
|
34
|
+
"urwid",
|
|
35
|
+
"urwid.*",
|
|
41
36
|
]
|
|
42
37
|
namespaces = false
|
|
43
38
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
wcwidth>=0.4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruff==0.15.8
|
|
@@ -247,6 +247,7 @@ class AsyncioEventLoopTest(unittest.TestCase, EventLoopTestMixin):
|
|
|
247
247
|
self.assertRaises(ZeroDivisionError, evl.run)
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
@unittest.skip("Unstable test: race conditions happens")
|
|
250
251
|
@unittest.skipUnless(GLIB_AVAILABLE, "GLIB unavailable")
|
|
251
252
|
class GLibEventLoopTest(unittest.TestCase, EventLoopTestMixin):
|
|
252
253
|
def setUp(self):
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
from urwid import str_util
|
|
6
|
+
|
|
7
|
+
# 🇨🇦 regional indicator Canada flag
|
|
8
|
+
FLAG_CA = "\U0001F1E8\U0001F1E6"
|
|
9
|
+
# 👨👩👧 family (man + ZWJ + woman + ZWJ + girl)
|
|
10
|
+
FAMILY = "\U0001F468\u200D\U0001F469\u200D\U0001F467"
|
|
11
|
+
# 👩🏻💻 woman technologist with light skin tone
|
|
12
|
+
TECHNOLOGIST = "\U0001F469\U0001F3FB\u200D\U0001F4BB"
|
|
13
|
+
# 👋🏻 waving hand with light skin tone
|
|
14
|
+
WAVE_SKIN = "\U0001F44B\U0001F3FB"
|
|
15
|
+
# café with combining acute accent on e
|
|
16
|
+
CAFE = "cafe\u0301"
|
|
17
|
+
# ❤️ red heart with VS16 (emoji presentation)
|
|
18
|
+
HEART_VS16 = "\u2764\ufe0f"
|
|
19
|
+
# ❤ red heart (text presentation)
|
|
20
|
+
HEART = "\u2764"
|
|
21
|
+
# 中文 Chinese characters
|
|
22
|
+
CJK = "\u4e2d\u6587"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CalcWidthGraphemeTest(unittest.TestCase):
|
|
26
|
+
def wtest(self, text, expected):
|
|
27
|
+
result = str_util.calc_width(text, 0, len(text))
|
|
28
|
+
assert result == expected, f"got:{result!r} expected:{expected!r}"
|
|
29
|
+
|
|
30
|
+
def test_basic(self):
|
|
31
|
+
self.wtest("hello", 5)
|
|
32
|
+
self.wtest("", 0)
|
|
33
|
+
self.wtest(CJK, 4)
|
|
34
|
+
|
|
35
|
+
def test_flags(self):
|
|
36
|
+
self.wtest(FLAG_CA, 2)
|
|
37
|
+
self.wtest(FLAG_CA + FLAG_CA, 4)
|
|
38
|
+
|
|
39
|
+
def test_zwj_sequences(self):
|
|
40
|
+
self.wtest(FAMILY, 2)
|
|
41
|
+
self.wtest(TECHNOLOGIST, 2)
|
|
42
|
+
|
|
43
|
+
def test_skin_tones(self):
|
|
44
|
+
self.wtest(WAVE_SKIN, 2)
|
|
45
|
+
|
|
46
|
+
def test_combining(self):
|
|
47
|
+
self.wtest(CAFE, 4)
|
|
48
|
+
self.wtest("e\u0301", 1)
|
|
49
|
+
|
|
50
|
+
def test_variation_selectors(self):
|
|
51
|
+
self.wtest(HEART_VS16, 2)
|
|
52
|
+
self.wtest(HEART, 1)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CalcTextPosGraphemeTest(unittest.TestCase):
|
|
56
|
+
def ptest(self, text, pref_col, expected_pos):
|
|
57
|
+
pos, _ = str_util.calc_text_pos(text, 0, len(text), pref_col)
|
|
58
|
+
assert pos == expected_pos, f"got:{pos!r} expected:{expected_pos!r}"
|
|
59
|
+
|
|
60
|
+
def test_basic(self):
|
|
61
|
+
self.ptest("hello", 3, 3)
|
|
62
|
+
|
|
63
|
+
def test_flag_boundaries(self):
|
|
64
|
+
text = f"A{FLAG_CA}B"
|
|
65
|
+
self.ptest(text, 1, 1)
|
|
66
|
+
self.ptest(text, 2, 1)
|
|
67
|
+
self.ptest(text, 3, 3)
|
|
68
|
+
|
|
69
|
+
def test_zwj_boundaries(self):
|
|
70
|
+
text = f"A{FAMILY}B"
|
|
71
|
+
self.ptest(text, 2, 1)
|
|
72
|
+
self.ptest(text, 3, 6)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class MoveNextCharGraphemeTest(unittest.TestCase):
|
|
76
|
+
def test_move_through_family_emoji(self):
|
|
77
|
+
text = f"A{FAMILY}B"
|
|
78
|
+
pos = str_util.move_next_char(text, 1, len(text))
|
|
79
|
+
assert pos == 6
|
|
80
|
+
|
|
81
|
+
def test_move_through_flag(self):
|
|
82
|
+
text = f"A{FLAG_CA}B"
|
|
83
|
+
pos = str_util.move_next_char(text, 1, len(text))
|
|
84
|
+
assert pos == 3
|
|
85
|
+
|
|
86
|
+
def test_move_through_combining(self):
|
|
87
|
+
pos = str_util.move_next_char(CAFE, 3, len(CAFE))
|
|
88
|
+
assert pos == 5
|
|
89
|
+
|
|
90
|
+
def test_move_through_skin_tone(self):
|
|
91
|
+
text = f"A{WAVE_SKIN}B"
|
|
92
|
+
pos = str_util.move_next_char(text, 1, len(text))
|
|
93
|
+
assert pos == 3
|
|
94
|
+
|
|
95
|
+
def test_basic_ascii(self):
|
|
96
|
+
pos = str_util.move_next_char("hello", 0, 5)
|
|
97
|
+
assert pos == 1
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class MovePrevCharGraphemeTest(unittest.TestCase):
|
|
101
|
+
def test_move_back_through_family_emoji(self):
|
|
102
|
+
text = f"A{FAMILY}B"
|
|
103
|
+
pos = str_util.move_prev_char(text, 0, 6)
|
|
104
|
+
assert pos == 1
|
|
105
|
+
|
|
106
|
+
def test_move_back_through_flag(self):
|
|
107
|
+
text = f"A{FLAG_CA}B"
|
|
108
|
+
pos = str_util.move_prev_char(text, 0, 3)
|
|
109
|
+
assert pos == 1
|
|
110
|
+
|
|
111
|
+
def test_move_back_through_combining(self):
|
|
112
|
+
pos = str_util.move_prev_char(CAFE, 0, 5)
|
|
113
|
+
assert pos == 3
|
|
114
|
+
|
|
115
|
+
def test_move_back_through_skin_tone(self):
|
|
116
|
+
text = f"A{WAVE_SKIN}B"
|
|
117
|
+
pos = str_util.move_prev_char(text, 0, 3)
|
|
118
|
+
assert pos == 1
|
|
119
|
+
|
|
120
|
+
def test_basic_ascii(self):
|
|
121
|
+
pos = str_util.move_prev_char("hello", 0, 5)
|
|
122
|
+
assert pos == 4
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class IsWideCharGraphemeTest(unittest.TestCase):
|
|
126
|
+
def test_family_emoji_is_wide(self):
|
|
127
|
+
assert str_util.is_wide_char(FAMILY, 0)
|
|
128
|
+
|
|
129
|
+
def test_flag_is_wide(self):
|
|
130
|
+
assert str_util.is_wide_char(FLAG_CA, 0)
|
|
131
|
+
|
|
132
|
+
def test_cjk_is_wide(self):
|
|
133
|
+
assert str_util.is_wide_char(CJK, 0)
|
|
134
|
+
|
|
135
|
+
def test_ascii_not_wide(self):
|
|
136
|
+
assert not str_util.is_wide_char("hello", 0)
|
|
137
|
+
|
|
138
|
+
def test_skin_tone_is_wide(self):
|
|
139
|
+
assert str_util.is_wide_char(WAVE_SKIN, 0)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# UTF-8 bytes test constants
|
|
143
|
+
FLAG_CA_BYTES = FLAG_CA.encode("utf-8") # 8 bytes
|
|
144
|
+
FAMILY_BYTES = FAMILY.encode("utf-8") # 18 bytes
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class Utf8BytesGraphemeTest(unittest.TestCase):
|
|
148
|
+
"""Tests for grapheme clustering with UTF-8 bytes."""
|
|
149
|
+
|
|
150
|
+
def setUp(self):
|
|
151
|
+
self._orig_encoding = str_util.get_byte_encoding()
|
|
152
|
+
str_util.set_byte_encoding("utf8")
|
|
153
|
+
|
|
154
|
+
def tearDown(self):
|
|
155
|
+
str_util.set_byte_encoding(self._orig_encoding)
|
|
156
|
+
|
|
157
|
+
def test_calc_width_flag_bytes(self):
|
|
158
|
+
result = str_util.calc_width(FLAG_CA_BYTES, 0, len(FLAG_CA_BYTES))
|
|
159
|
+
assert result == 2
|
|
160
|
+
|
|
161
|
+
def test_calc_width_family_emoji_bytes(self):
|
|
162
|
+
result = str_util.calc_width(FAMILY_BYTES, 0, len(FAMILY_BYTES))
|
|
163
|
+
assert result == 2
|
|
164
|
+
|
|
165
|
+
def test_move_next_through_family_emoji_bytes(self):
|
|
166
|
+
# After 'A' (1 byte), should skip entire family emoji
|
|
167
|
+
text = f"A{FAMILY}B".encode("utf-8")
|
|
168
|
+
pos = str_util.move_next_char(text, 1, len(text))
|
|
169
|
+
assert pos == 1 + len(FAMILY_BYTES)
|
|
170
|
+
|
|
171
|
+
def test_move_next_through_flag_bytes(self):
|
|
172
|
+
# After 'A' (1 byte), should skip entire flag
|
|
173
|
+
text = f"A{FLAG_CA}B".encode("utf-8")
|
|
174
|
+
pos = str_util.move_next_char(text, 1, len(text))
|
|
175
|
+
assert pos == 1 + len(FLAG_CA_BYTES)
|
|
176
|
+
|
|
177
|
+
def test_move_prev_through_family_emoji_bytes(self):
|
|
178
|
+
# From position after family emoji, should go back to after 'A'
|
|
179
|
+
text = f"A{FAMILY}B".encode("utf-8")
|
|
180
|
+
end_pos = 1 + len(FAMILY_BYTES)
|
|
181
|
+
pos = str_util.move_prev_char(text, 0, end_pos)
|
|
182
|
+
assert pos == 1
|
|
183
|
+
|
|
184
|
+
def test_move_prev_through_flag_bytes(self):
|
|
185
|
+
# From position after flag, should go back to after 'A'
|
|
186
|
+
text = f"A{FLAG_CA}B".encode("utf-8")
|
|
187
|
+
end_pos = 1 + len(FLAG_CA_BYTES)
|
|
188
|
+
pos = str_util.move_prev_char(text, 0, end_pos)
|
|
189
|
+
assert pos == 1
|
|
190
|
+
|
|
191
|
+
def test_is_wide_family_emoji_bytes(self):
|
|
192
|
+
assert str_util.is_wide_char(FAMILY_BYTES, 0)
|
|
193
|
+
|
|
194
|
+
def test_is_wide_flag_bytes(self):
|
|
195
|
+
assert str_util.is_wide_char(FLAG_CA_BYTES, 0)
|
|
196
|
+
|
|
197
|
+
def test_calc_text_pos_flag_boundaries_bytes(self):
|
|
198
|
+
text = f"A{FLAG_CA}B".encode("utf-8")
|
|
199
|
+
# Column 1 should be at position 1 (after 'A')
|
|
200
|
+
pos, col = str_util.calc_text_pos(text, 0, len(text), 1)
|
|
201
|
+
assert pos == 1
|
|
202
|
+
assert col == 1
|
|
203
|
+
# Column 2 should still be at position 1 (inside flag grapheme)
|
|
204
|
+
pos, col = str_util.calc_text_pos(text, 0, len(text), 2)
|
|
205
|
+
assert pos == 1
|
|
206
|
+
assert col == 1
|
|
207
|
+
# Column 3 should be at position after flag
|
|
208
|
+
pos, col = str_util.calc_text_pos(text, 0, len(text), 3)
|
|
209
|
+
assert pos == 1 + len(FLAG_CA_BYTES)
|
|
210
|
+
assert col == 3
|
|
@@ -38,7 +38,8 @@ class TestScrollable(unittest.TestCase):
|
|
|
38
38
|
widget = urwid.Scrollable(long_content)
|
|
39
39
|
self.assertEqual(frozenset((urwid.BOX,)), widget.sizing())
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
long_content_canvas = long_content.render((reduced_size[0],))
|
|
42
|
+
cropped_content_canvas = urwid.CompositeCanvas(long_content_canvas)
|
|
42
43
|
cropped_content_canvas.trim_end(content_size[1] - reduced_size[1])
|
|
43
44
|
top_decoded = cropped_content_canvas.decoded_text
|
|
44
45
|
|
|
@@ -169,15 +169,27 @@ class CalcTranslateTest(unittest.TestCase):
|
|
|
169
169
|
def check(self, text, mode, width, result_left, result_center, result_right) -> None:
|
|
170
170
|
with self.subTest("left"):
|
|
171
171
|
result = urwid.default_layout.layout(text, width, "left", mode)
|
|
172
|
-
self.assertEqual(
|
|
172
|
+
self.assertEqual(
|
|
173
|
+
result_left,
|
|
174
|
+
result,
|
|
175
|
+
f"For {text=} {width=} {mode=} got {result!r} instead of {result_left!r}",
|
|
176
|
+
)
|
|
173
177
|
|
|
174
178
|
with self.subTest("center"):
|
|
175
179
|
result = urwid.default_layout.layout(text, width, "center", mode)
|
|
176
|
-
self.assertEqual(
|
|
180
|
+
self.assertEqual(
|
|
181
|
+
result_center,
|
|
182
|
+
result,
|
|
183
|
+
f"For {text=} {width=} {mode=} got {result!r} instead of {result_center!r}",
|
|
184
|
+
)
|
|
177
185
|
|
|
178
186
|
with self.subTest("right"):
|
|
179
187
|
result = urwid.default_layout.layout(text, width, "right", mode)
|
|
180
|
-
self.assertEqual(
|
|
188
|
+
self.assertEqual(
|
|
189
|
+
result_right,
|
|
190
|
+
result,
|
|
191
|
+
f"For {text=} {width=} {mode=} got {result!r} instead of {result_right!r}",
|
|
192
|
+
)
|
|
181
193
|
|
|
182
194
|
def test_calc_translate_char(self):
|
|
183
195
|
self.check(
|
|
@@ -255,15 +255,7 @@ VERSION = __version_tuple__
|
|
|
255
255
|
__locals: dict[str, typing.Any] = locals() # use mutable access for pure lazy loading
|
|
256
256
|
|
|
257
257
|
# Backward compatible lazy load with deprecation warnings
|
|
258
|
-
_moved_warn: dict[str, str] = {
|
|
259
|
-
"lcd_display": "urwid.display.lcd",
|
|
260
|
-
"html_fragment": "urwid.display.html_fragment",
|
|
261
|
-
"web_display": "urwid.display.web",
|
|
262
|
-
"monitored_list": "urwid.widget.monitored_list",
|
|
263
|
-
"listbox": "urwid.widget.listbox",
|
|
264
|
-
"treetools": "urwid.widget.treetools",
|
|
265
|
-
}
|
|
266
|
-
# Backward compatible lazy load without any warnings
|
|
258
|
+
_moved_warn: dict[str, str] = {}
|
|
267
259
|
# Before DeprecationWarning need to start PendingDeprecationWarning process.
|
|
268
260
|
|
|
269
261
|
|
|
@@ -315,7 +315,10 @@ class Screen(_raw_display_base.Screen):
|
|
|
315
315
|
selector.register(fd, selectors.EVENT_READ)
|
|
316
316
|
input_ready = selector.select(0)
|
|
317
317
|
while input_ready:
|
|
318
|
-
|
|
318
|
+
chunk = os.read(fd, 1024)
|
|
319
|
+
if len(chunk) == 0:
|
|
320
|
+
raise RuntimeError("stdin has been closed")
|
|
321
|
+
chars.extend(chunk)
|
|
319
322
|
input_ready = selector.select(0)
|
|
320
323
|
|
|
321
324
|
return chars
|
|
@@ -1018,7 +1018,8 @@ class BaseScreen(abc.ABC, metaclass=signals.MetaSignals):
|
|
|
1018
1018
|
Deprecated in favor of calling `start` as a context manager.
|
|
1019
1019
|
"""
|
|
1020
1020
|
warnings.warn(
|
|
1021
|
-
"run_wrapper is deprecated in favor of calling `start` as a context manager."
|
|
1021
|
+
"run_wrapper is deprecated in favor of calling `start` as a context manager."
|
|
1022
|
+
"API will be removed in version 5.0.",
|
|
1022
1023
|
DeprecationWarning,
|
|
1023
1024
|
stacklevel=3,
|
|
1024
1025
|
)
|
|
@@ -28,7 +28,6 @@ import os
|
|
|
28
28
|
import sys
|
|
29
29
|
import time
|
|
30
30
|
import typing
|
|
31
|
-
import warnings
|
|
32
31
|
from contextlib import suppress
|
|
33
32
|
|
|
34
33
|
from urwid import display, signals
|
|
@@ -169,16 +168,6 @@ class MainLoop:
|
|
|
169
168
|
else:
|
|
170
169
|
self._topmost_widget = self._widget
|
|
171
170
|
|
|
172
|
-
def _set_widget(self, widget: Widget) -> None:
|
|
173
|
-
warnings.warn(
|
|
174
|
-
f"method `{self.__class__.__name__}._set_widget` is deprecated, "
|
|
175
|
-
f"please use `{self.__class__.__name__}.widget` property."
|
|
176
|
-
"API will be removed in version 4.0.",
|
|
177
|
-
DeprecationWarning,
|
|
178
|
-
stacklevel=2,
|
|
179
|
-
)
|
|
180
|
-
self.widget = widget
|
|
181
|
-
|
|
182
171
|
@property
|
|
183
172
|
def pop_ups(self) -> bool:
|
|
184
173
|
return self._pop_ups
|
|
@@ -191,16 +180,6 @@ class MainLoop:
|
|
|
191
180
|
else:
|
|
192
181
|
self._topmost_widget = self._widget
|
|
193
182
|
|
|
194
|
-
def _set_pop_ups(self, pop_ups: bool) -> None:
|
|
195
|
-
warnings.warn(
|
|
196
|
-
f"method `{self.__class__.__name__}._set_pop_ups` is deprecated, "
|
|
197
|
-
f"please use `{self.__class__.__name__}.pop_ups` property."
|
|
198
|
-
"API will be removed in version 4.0.",
|
|
199
|
-
DeprecationWarning,
|
|
200
|
-
stacklevel=2,
|
|
201
|
-
)
|
|
202
|
-
self.pop_ups = pop_ups
|
|
203
|
-
|
|
204
183
|
def set_alarm_in(self, sec: float, callback: Callable[[Self, _T], typing.Any], user_data: _T = None):
|
|
205
184
|
"""
|
|
206
185
|
Schedule an alarm in *sec* seconds that will call *callback* from the
|
|
@@ -24,15 +24,15 @@ import typing
|
|
|
24
24
|
import warnings
|
|
25
25
|
from pprint import pformat
|
|
26
26
|
|
|
27
|
+
import wcwidth
|
|
28
|
+
|
|
27
29
|
from urwid.canvas import CanvasError, TextCanvas
|
|
28
30
|
from urwid.display.escape import SAFE_ASCII_DEC_SPECIAL_RE
|
|
29
|
-
from urwid.util import apply_target_encoding
|
|
31
|
+
from urwid.util import apply_target_encoding
|
|
30
32
|
|
|
31
33
|
if typing.TYPE_CHECKING:
|
|
32
34
|
from collections.abc import Iterator, Sequence
|
|
33
35
|
|
|
34
|
-
from typing_extensions import Literal
|
|
35
|
-
|
|
36
36
|
|
|
37
37
|
def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[str]]], bool]:
|
|
38
38
|
"""return (dictionary of glyphs, utf8 required)"""
|
|
@@ -60,7 +60,7 @@ def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[
|
|
|
60
60
|
character = key_line[key_index]
|
|
61
61
|
|
|
62
62
|
if key_index < len(key_line) and key_line[key_index] == character:
|
|
63
|
-
end_col +=
|
|
63
|
+
end_col += wcwidth.width(character, control_codes="ignore")
|
|
64
64
|
key_index += 1
|
|
65
65
|
continue
|
|
66
66
|
|
|
@@ -76,7 +76,7 @@ def separate_glyphs(gdata: str, height: int) -> tuple[dict[str, tuple[int, list[
|
|
|
76
76
|
if j >= len(line):
|
|
77
77
|
fill = end_col - start_col - y
|
|
78
78
|
break
|
|
79
|
-
y +=
|
|
79
|
+
y += wcwidth.width(line[j], control_codes="ignore")
|
|
80
80
|
j += 1
|
|
81
81
|
if y + fill != end_col - start_col:
|
|
82
82
|
raise ValueError(repr((y, fill, end_col)))
|
|
@@ -98,7 +98,8 @@ def add_font(name: str, cls: FontRegistry) -> None:
|
|
|
98
98
|
warnings.warn(
|
|
99
99
|
"`add_font` is deprecated, please set 'name' attribute to the font class,"
|
|
100
100
|
" use metaclass keyword argument 'font_name'"
|
|
101
|
-
" or use `Font.register(<name>)`"
|
|
101
|
+
" or use `Font.register(<name>)`"
|
|
102
|
+
"API will be removed in version 5.0.",
|
|
102
103
|
DeprecationWarning,
|
|
103
104
|
stacklevel=2,
|
|
104
105
|
)
|
|
@@ -219,22 +220,6 @@ class Font(metaclass=FontRegistry):
|
|
|
219
220
|
"""Font description."""
|
|
220
221
|
return f"{self.__class__.__name__}():\n {self.height!r}\n {pformat(self.data, indent=4)}"
|
|
221
222
|
|
|
222
|
-
@staticmethod
|
|
223
|
-
def _to_text(
|
|
224
|
-
obj: str,
|
|
225
|
-
encoding: str = "utf-8",
|
|
226
|
-
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
227
|
-
) -> str:
|
|
228
|
-
warnings.warn(
|
|
229
|
-
"_to_text is deprecated: only text fonts are supported. API will be removed in version 4.0.",
|
|
230
|
-
DeprecationWarning,
|
|
231
|
-
stacklevel=3,
|
|
232
|
-
)
|
|
233
|
-
if isinstance(obj, str):
|
|
234
|
-
return obj
|
|
235
|
-
|
|
236
|
-
raise TypeError(f"{obj!r} is not str")
|
|
237
|
-
|
|
238
223
|
def add_glyphs(self, gdata: str) -> None:
|
|
239
224
|
d, utf8_required = separate_glyphs(gdata, self.height)
|
|
240
225
|
self.char.update(d)
|
|
@@ -23,7 +23,6 @@ from __future__ import annotations
|
|
|
23
23
|
import abc
|
|
24
24
|
import itertools
|
|
25
25
|
import typing
|
|
26
|
-
import warnings
|
|
27
26
|
import weakref
|
|
28
27
|
|
|
29
28
|
if typing.TYPE_CHECKING:
|
|
@@ -99,9 +98,9 @@ class Signals:
|
|
|
99
98
|
:type name: signal name
|
|
100
99
|
:param callback: the function to call when that signal is sent
|
|
101
100
|
:type callback: function
|
|
102
|
-
:param user_arg:
|
|
103
|
-
after the arguments passed when the signal is
|
|
104
|
-
|
|
101
|
+
:param user_arg: additional argument to callback
|
|
102
|
+
(appended after the arguments passed when the signal is emitted).
|
|
103
|
+
If None no arguments will be added.
|
|
105
104
|
Don't use this argument, use user_args instead.
|
|
106
105
|
:param weak_args: additional arguments passed to the callback
|
|
107
106
|
(before any arguments passed when the signal
|
|
@@ -167,12 +166,6 @@ class Signals:
|
|
|
167
166
|
handler can also be disconnected by calling
|
|
168
167
|
urwid.disconnect_signal, which doesn't need this key.
|
|
169
168
|
"""
|
|
170
|
-
if user_arg is not None:
|
|
171
|
-
warnings.warn(
|
|
172
|
-
"Don't use user_arg argument, use user_args instead.",
|
|
173
|
-
DeprecationWarning,
|
|
174
|
-
stacklevel=2,
|
|
175
|
-
)
|
|
176
169
|
|
|
177
170
|
sig_cls = obj.__class__
|
|
178
171
|
if name not in self._supported.get(sig_cls, ()):
|
|
@@ -195,9 +188,8 @@ class Signals:
|
|
|
195
188
|
# their callbacks) from existing.
|
|
196
189
|
obj_weak = weakref.ref(obj)
|
|
197
190
|
|
|
198
|
-
def weakref_callback(weakref)
|
|
199
|
-
o
|
|
200
|
-
if o:
|
|
191
|
+
def weakref_callback(_ref: weakref.ReferenceType[typing.Any]) -> None:
|
|
192
|
+
if o := obj_weak():
|
|
201
193
|
self.disconnect_by_key(o, name, key)
|
|
202
194
|
|
|
203
195
|
user_args = self._prepare_user_args(weak_args, user_args, weakref_callback)
|
|
@@ -209,7 +201,7 @@ class Signals:
|
|
|
209
201
|
self,
|
|
210
202
|
weak_args: Iterable[typing.Any] = (),
|
|
211
203
|
user_args: Iterable[typing.Any] = (),
|
|
212
|
-
callback: Callable[
|
|
204
|
+
callback: Callable[[weakref.ReferenceType[typing.Any]], typing.Any] | None = None,
|
|
213
205
|
) -> tuple[Collection[weakref.ReferenceType], Collection[typing.Any]]:
|
|
214
206
|
# Turn weak_args into weakrefs and prepend them to user_args
|
|
215
207
|
w_args = tuple(weakref.ref(w_arg, callback) for w_arg in weak_args)
|
|
@@ -303,14 +295,13 @@ class Signals:
|
|
|
303
295
|
self,
|
|
304
296
|
callback,
|
|
305
297
|
user_arg: typing.Any,
|
|
306
|
-
weak_args:
|
|
307
|
-
user_args:
|
|
298
|
+
weak_args: Iterable[weakref.ReferenceType],
|
|
299
|
+
user_args: Iterable[typing.Any],
|
|
308
300
|
emit_args: Iterable[typing.Any],
|
|
309
301
|
) -> bool:
|
|
310
302
|
args_to_pass = []
|
|
311
303
|
for w_arg in weak_args:
|
|
312
|
-
real_arg
|
|
313
|
-
if real_arg is not None:
|
|
304
|
+
if (real_arg := w_arg()) is not None:
|
|
314
305
|
args_to_pass.append(real_arg)
|
|
315
306
|
else:
|
|
316
307
|
# de-referenced
|