Pytermgui Versions Save

Python TUI framework with mouse support, modular widget system, customizable and rapid terminal markup language and more!

v7.7.1

2 months ago

Really small update this time, mostly dealing with long-standing issues. Haven't had the time to work on any programming for a minute (a multiple-month spanning minute, at that), so apologies for it taking so long.

Additions

  • Add support for wide characters

Bugfixes

  • Fix inline widget referring to hover mouse mode

🚀

v7.7.0

8 months ago

Changelog

Additions

  • Add support for Button submit on carriage return (#127, 0cdcc91ca76b0745a4c73d080ee8e0b96de68e19)

Bugfixes

  • Fix sluggish and unreliable text input caused by getch_timeout (74f72df98ac04c0dd607dfb55636ea4bd5334a7d)

:rocket:

v7.6.0

9 months ago

Minor release to allow building our documentation.

Changelog

Bugfixes

  • (temporarily) Fix hyperlinks breaking SVG exports

Removals

  • Remove (undocumented) shortening behaviour from Button (#124)

    Normally this would be considered an API breaking change, but since it was never documented (and didn't even work properly in most scenarios) it's only a minor-level change.

v7.5.0

9 months ago

The standout feature from this update is finally getting windows mouse support working, courtesy of @Tired-Fox!

Changelog

Additions

  • Add support for Windows' mouse events (@Tired-Fox, #122)
  • Add support for feeding input into getch

Refactor

  • Allow immediately halting WindowManager programatically by not blocking on getch calls

Bugfixes

  • Fix mouse event handlers not getting cleaned up properly

v7.4.0

11 months ago

Lots and lots of things have happened since my previous time writing a release like this. It will all make sense soon (you can check my PyPi for a sneak peak!), but for the time being there's some changes to document.

Changelog

Additions

  • Add meta tokens for saving & restoring styles (bcf0a2ef091175407316204c2408e3c208cd0312)

    This adds the new meta tokens #stash, #pop and their clearing-variants #stash/ and #/pop, which allow you to save a snapshot of the currently applied styles and restore it at a later point within the same markup.

    It can be super useful for when you want to insert special styles for a single word.

    [bold 141]This is an example with a [#stash/ @black grey]code-snippet [#/pop] in the middle.

  • Add support for alt/ctrl+backspace to remove whole words (@i404788, #109)

  • Add support for word move actions (Ctrl/Alt+<L>/<R>) & Home/End move (@i404788, #109)

Bugfixes

  • Fix multiline Label widget to incorrectly display message with hyperlink (@leonard-IMBERT, #108)
  • Fix input errors when alert is open (@qoft, #115)

Miscellaneous

  • Add readme and changelog to sdist (@thewchan, #102)

v7.3.0

1 year ago

The primary focus for this release is the new documentation, but there is a lot of minor changes that slipped through in the meantime. The new documentation is great; it has much more in-depth content on most parts of the library, it looks amazing and it's a lot less of a hassle to maintain! It also has build-time screenshot generation using this very module, so all the SVGs will always remain up to date.

Changelog

Additions

  • Add support for SHIFT+ scroll events (74722db6787b733b75e3515dfc086cf0338235b8)
  • Add shade number indicators to Palette.print (06f2ec78d1b31d2a059035e72e8d1b014c929b81)
  • Add inline widget runner (ba19d36f51f5b16c9871070953fa348b6553504f)

Bugfixes

  • Fix incorrect macro caching behaviour (321f1f9549d03285d44904c344bd2551b795e0e6)
  • Fix various issues and misbehaviours with SVG exports
  • Fix incorrect placement of InputField cursor (7281c5716e5662d1cfa50d11c5da50d8f3e44d28)

Refactors

  • New MkDocs based documentation

    While the previous documentation was alright, it missed a lot of things. MkDocs makes it a bit easier to create step-by-step style documentation pages, which was exactly what we needed. We also made use of Termage to make it a lot more dynamic.

  • Change terminal.py -> term.py and serializer.py -> serialization.py to avoid naming conflicts (e38603ee390141dcdb7b0d090f8684f4e331b68f)

  • Improve pseudo token behaviour by parsing it as a new token type (f19b897ba99c8270c71e6f34c30e2a33ef4390f9)

  • Start generating semantic colors (success, warning, error) by blending with the primary (2daee90a3abe47aa632a7ca876151062946f0784)

Removals

  • Remove is_bindable widget attribute (866cd0951d3f3471ae0e62631ba59834912fd8e1)

    This was only used as an arbitrary limitation, one that was not needed in the library.

Showcase

Screenshot 2022-11-17 at 15 05 44 Screenshot 2022-11-17 at 15 05 53 Screenshot 2022-11-17 at 15 06 00 Screenshot 2022-11-17 at 15 06 07

v7.2.0

1 year ago

This version brings the long-awaited (at least by myself) color palette system!

Before that, the new #auto TIM pseudo-tag deserves a mention. It makes the parser look at the currently applied background color, and it is replaced with a color that properly contrasts it while parsing! This way, [alias #auto]Text, will always be legible, regardless of the meaning of alias. It is also automatically used by all widget styles (whenever there is no foreground specified), so you can just define a style as @surface+1, and PTG will make sure it looks nice and readable!

Anyways, the palette. Here is the gist of it:

  • The new module palettes provides a Palette class
  • This class can generate mashing colors from just one primary color input, with multiple possible strategies
  • The whole module now has a default palette that can be accessed using ptg.palette and in markup

TIM gains the following sets of new aliases:

  • primary
  • secondary
  • tertiary
  • accent
  • surface
  • surface2
  • surface3
  • surface4
  • success
  • warning
  • error

Each color has 7 shades, and each shade a foreground and background variant. The base color (i.e. with no shade modifications) use the name as written above, and every other alias is defined as {name}{+/-}{shade_amount}, like primary-3 for the darkest shade of primary, and surface+2for the second-brightest surface shade. Each alias comes complete with a background variant bound to@{alias}`, such as @secondary+2.

It's easier to show than tell, so here is the new default palette, as exported by ptg --palette --export-svg <filename>: palette

Changelog

Additions

  • Add various color manipulation utilities
  • Add #auto TIM pseudo-tag that always gives properly contrasted foreground text
  • Add palettes module for framework-wide color generation & configuration
  • Add Synchronized Output support
  • Add FancyReprWidget
  • Add ptg --palette flag

Bugfixes

  • Fix markup aliases getting literalized during parsing.eval_alias & MarkupLanguage.alias
  • Fix background colors creating vertical seams in SVG exports
  • Fix colors getting localized pre-maturely

Refactors

  • Make all the ptg program & all builting widgets use the global palette
  • Prefix all ANSI colors with ansi-

Showcase

Here is my home-grown project launcher, lens, using the new default color palette (and an upcoming new button widget, but that's for the next release notes):

Screenshot 2022-08-05 at 23 53 46

...and now, using it's custom-defined palette, using the primary color #58A46F:

Screenshot 2022-08-05 at 23 54 32

Here is ptg in the new default color palette:

Screenshot 2022-08-05 at 23 55 29

...and now with a bunch of randomly generated ones:

Screenshot 2022-08-05 at 23 55 35 Screenshot 2022-08-05 at 23 55 42 Screenshot 2022-08-05 at 23 55 46 Screenshot 2022-08-05 at 23 55 55

v7.1.0

1 year ago

This minor release mostly aims to fix up some issues caused in previous releases, as well as tighten up InputField cursor behaviour.

Note that the release size of the project became enormous since the move to pyproject.toml for building. This should be resolved by the next update.

Changelog

Additions

  • Re-introduce InputField prompt attribute (#90)

Bugfixes

  • Fix ptg --version (#89)
  • Start wrapping InputField cursor when it goes outside of the given width (634c1b5eb13e58ece182dee0c34555939fcb5e1e)
  • Fix incorrect KeyboardButton label generation (#88)

Showcase

Here is a sneak-peak of the upcoming update to the file-definition system, which will add things like selector-based targeting, native markup support and more!

Screenshot 2022-07-27 at 15 06 27

v7.0.0

1 year ago

This release brings completely re-written TIM implementation to the library. It is significantly faster (~2x for markup without macros, >10x for markup with macros) than the previous version, and it's a lot easier to maintain and improve upon.

The previous hyperlink syntax ([!link(https://example.org)]Example site[/!link]) is now deprecated in favour of a new, much simpler one:

[~https://example.org]Example site[/~]

There have also been a couple of changes to SVG exports to make them more accurate and aesthetically pleasing. We also support the inverse style for them as well!

Changelog

Changes marked in bold are API-breaking.

Additions

  • Support CSS color names in TIM code (77d2ca98150d48143e9a52eaa2e36578c369e4d6)
  • Support terminal resize events on Windows (@Tired-Fox, #80)
  • Add ignore_any parameter to Widget.execute_binding (68866e63fcba0dd36f5a1165966cb5b2377bc43b)

Bugfixes

  • Fix InputField handling clicks & drags started outside of it (#72, #75)
  • Fix CTRL_C not killing compositor thread by making it a daemon (#78)
  • Fix contents of widget.positioned_line_buffer being duplicated before and after vertical alignment (#70)

Refactors

  • Rewrite of the TIM engine (now at version 3!) (#84)
  • Move TIM highlighting from an instance method to a highlighter (635b6f60467aa4b854c72238f2bc9edc710576b8)
  • Move to pyproject.toml-based builds (#82)
  • Stop relying on visibility=hidden in SVG exports (d070724a591f6c0f62016944e2d49ed1382d9805)

Removals

  • Remove get_applied_sequences helper function (a7d41bd819abd1ea481dc536090199a98c9330e7)

API updates

Type Change Alternative Comment
Removal get_applied_sequences A custom tokenization based implementation This function was no longer used internally, and if someone needed it the new tokenizers are a much more performant and smart way to go about implementing it.
Removal MarkupLanguage.prettify_markup highlight_tim Using regex-based highlighting allows the output markup to be completely identical char-by-char to the source, whereas the previous token based implementation had a tendency to change things around. It's also much less LOC and faster.
Deprecation Markup hyperlink syntax !link(https://example.org) The new ~https://example.org syntax Previously hyperlinks were implemented as macros, but they were very messy under the hood. A first-class syntax for them is vastly superior.
Refactor The role of StyledText N/A Previously, StyledText was meant to be barely detectable when used. It was returned in various places, but wanted to not be a thing you thought about often. Now it's on an opt-in basis, and it no longer tries to pass as a str. It's immutable, and is only meant to be generated by MarkupLanguage.group_styles or StyledText.group_styles. You can also now create them from markup, not just ANSI-coded text.

Showcase

CSS colors Style modes Example parse Speed comparison Note that since the above image, tim-v3 went down to ~180-190 ns per parse

v6.4.0

1 year ago

This release brings the ability to use on_{mouse_event} functions to handle specific events, instead of having to sort manually within handle_mouse. This is done within Widget's handle_mouse method, so it will be implemented by all widgets that call super().handle_mouse before doing their own handling. It also improves general mouse handling behaviour, and makes it all a bit more predictable.

There is a minor change in return-value semantics as well. Previously, handle_mouse() == True stood for "I was able to handle this event, do not bubble it up". This wasn't very specific, and there were some special cases where undefined behaviour would occur. It has now been changed to "I want to handle more mouse events, even if they aren't directly targeting me". This essentially allows a widget to declare itself "sticky" for certain mouse events, and "unsticky" when it is no longer needed.

Changelog

Additions

  • Add hover highlighting for Button (dcf5b0d8f2981211a33aa30f12009ad0ddb371e3)

Bugfixes

  • Fix Splitters only sending mouse events on the first row of height (ad5cffc0327bb2a9eae504a7b4fc7d0c1f2ca12e)

Refactors

  • Improve mouse input cascade logic (ae971020fe6090eaa3c80cecd3b6d9d60f87ef64)

  • Implement semantic mouse handlers (f11f524a31375b4ccb19e7bbff96395bcf38e8f8)

    All widgets now have some "implicit" callbacks for each specific mouse event. They are only called when defined, so it is fully backwards compatible. They all follow the syntax of:

      on_{event}
    

    Where event can be any snakeified mouse action name. For example, LEFT_CLICK calls on_left_click, and SCROLL_UP calls on_scroll_up. For most events, there is a lesser layer of specificity allowed. For example, you usually want to handle both scroll directions with the same function. Because of this, we also allow defining on_scroll to deal with both events. Whenever multiple specificity level handlers are defined for the same action, the most specific will be used. For example, a widget with both on_drag and on_left_drag will invoke on_drag for RIGHT_DRAG, and on_left_drag for LEFT_DRAG.

Showcase

Here is a little mouse handling demo I created while writing this version. All mouse actions are defined with custom on_{event} handlers, where both drag events return True, and everything else False. This makes those events stick to the tile that started handling them.

https://user-images.githubusercontent.com/66030897/173597243-8bc30299-c342-40ca-8393-2203841b43fe.mp4