Skip to content

Releases: bczsalba/pytermgui

v7.1.0: Better InputField cursor!

27 Jul 13:10
Compare
Choose a tag to compare

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 (634c1b5)
  • 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: TIM-v3!

24 Jul 10:10
Compare
Choose a tag to compare

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 (77d2ca9)
  • Support terminal resize events on Windows (@Tired-Fox, #80)
  • Add ignore_any parameter to Widget.execute_binding (68866e6)

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 (635b6f6)
  • Move to pyproject.toml-based builds (#82)
  • Stop relying on visibility=hidden in SVG exports (d070724)

Removals

  • Remove get_applied_sequences helper function (a7d41bd)

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: Semantic mouse handling!

14 Jun 14:07
Compare
Choose a tag to compare

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 (dcf5b0d)

Bugfixes

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

Refactors

  • Improve mouse input cascade logic (ae97102)

  • Implement semantic mouse handlers (f11f524)

    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.

LbPGMBCr5xPDU8oW.mp4

v6.3.0: A better input field

05 Jun 12:17
Compare
Choose a tag to compare

The previous InputField implementation was highly restrained by the lacking (and slow) break_line implementation present at the time. Since that was recently improved a lot, the manually placed restrictions didn't make much sense anymore. There is now support for multiline text editing, syntax highlighters and more!

Changelog

Refactors

  • Refactor InputField (6cd72d6)

  • Start caching Token.sequence to improve performance (9638e95)

  • Start lazy-evaluating terminal resolution (4a7c25b)

    Some terminals don't support this feature, so we used to have a very short (0.01) second timeout for an emulator to respond. This was way too short in some situations, leaving the response to be printed to STDOUT instead of being captured. The bigger problem was that there is basically no code in the wild that actually uses this feature: it was implemented for the still-upcoming image support, but no other systems really use it. Because of this, it was pretty useless to query it on every startup.

Bugfixes

  • Use SVG export prefix as the class of the generated text elements (7c977f1, #67)

    Previously, the text style selector selected every SVG in the document. This was problematic, as it messed up non-PTG SVGs displayed alongside our own.

  • Fix Inspector not resizing to custom global terminals (0bb0717)

Additions

  • Add break_line fill argument (1426edc)

    This is currently unused by the library at large, but it allows padding out every yielded line to the target width using the given character. Can be pretty useful.

  • Add chrome SVG argument (1822585)

Showcase

Here is a simple file I wrote using sandbox/input_field.py, a file created from examples/boilerplate.py in barely any code. Try it yourself!

Screenshot 2022-06-05 at 14 09 13

v6.2.2: SVG fixes

26 May 11:14
Compare
Choose a tag to compare

Web compatibility strikes again. While Chrome supports alignment-baseline for SVG text tags, Firefox only supports dominant-baseline. Then, Safari comes in as a unicorn proclaiming (very quietly) that it supports neither.

All hail dy, I guess!

Changelog

Bugfixes

  • Fix text being misaligned in both Firefox and Safari

Refactors

  • Reduce redundancy in SVG export dimensions

Headless SVGs!

25 May 21:13
Compare
Choose a tag to compare

This release mainly targets my new mkdocs plugin, termage, which allows you to insert SVGs of Python program output, generated at build time. Its documentation website is still waiting for DNS propagation, but it uses the plugin as well. You can expect a revamp of the documentation using mkdocs and termage very soon!

For that system to work, WindowManager needed to gain the capability of running in headless mode, so that it can render an output and quit without interaction.

Changelog

Bugfixes

  • Fix \n being escaped when highlighting python (48a365a)

Refactors

  • Add WindowManager.autorun class attribute (6ea6044)

Showcase

The aforementioned documentation website:
127 0 0 1_8000_ (1)

v6.2.0: Native SVGs!

24 May 13:56
Compare
Choose a tag to compare

Changelog

Bugfixes

  • Fix StandardColor HEX and RGB being indexed from the wrong pool (66384ac)
  • Fix overly greedy yeeting optimization of ttype=POSITION tokens (2f7a561)
  • Fix ttype=POSITION tokens acting unpredictably when multiple were present in a string (c2c227f)

Additions

  • Add WindowManager.autorun attribute (ab0fd94)

Refactors

  • Implement usage of SVG tags when exporting (589e650)

    Previously, we were using foreignObject SVG tags to render inner content. This worked, but only in browsers, and looked ever so slightly different between each of them. We now use the proper SVG tags, and thus our exports are now real images not linked to browsers!

    You can also now export screenshots from a WindowManager application, and they look the same as they did in your terminal. This is allowed by the 2 position token fixes above. As with the previous version, this uses your terminal's background & foreground colors, as well as its size when generating. Makes it actually look like it's a screenshot of your terminal!

export

Showcase

All of the images here are straight-up SVGs dragged into the release note, no conversions or anything necessary.

screenshot
export

Thank you to everyone for the 1000 stars the project recently reached. It's an incredible achievement, and I am beyond grateful for the support.


🚀

v6.1.0: Fancy repr protocol!

17 May 15:13
Compare
Choose a tag to compare

This version was mostly for bugfixing, but it introduces the new __fancy_repl__ protocol into the library.

The purpose of this protocol is for objects to gain control over how they are displayed in a pretty REPL environment. See the docs for more info.

There is also now support for string format specs for Color objects. You can now do things like:

markup = f"[{color:tim}]Text"

and I think that's pretty neat.

Changelog

Additions

  • Introduce __fancy_repr__ protocol (09bcb0e)

    This protocol is somewhat inspired by rich's __rich__, but tries to be agnostic to the underlying ANSI interface used. It can be used to customize highlighting an object's repr output.

    It is already implemented by all the Color classes, and RegexHighlighter. More (probably) to come.

  • Add Color format specifiers (c529c2c)

  • Add Animation.pause & Animation.unpause (bf7ddbd)

Bugfixes

  • Fix scrolled widgets not getting positioned correctly (29a2aeb)
  • Fix an issue with 2-bit colors being interpreted as 8-bit ones (632ac1b, #56)

Refactors

Showcase

Screenshot 2022-05-17 at 17 09 46

v6.0.0: Layouts, inspection & highlighting!

12 May 21:53
Compare
Choose a tag to compare

This version was originally meant to focus on the new layout system, but things got out of hand quickly. In the end, we have the aforementioned feature, alongside a completely reimagined & much more useful CLI, a regex-based syntax highlighting base, major performance improvements and some general QOL things. There unfortunately are some (relatively minor) API changes, which is why this version is 6.0.0 instead of 5.1.0.

Changelog

  • Remove Window.allow_fullscreen, Window.toggle_fullscreen (114da3a)

    These methods are no longer really relevant in the new layout-based system. They were also handled in a really odd way from the library's side, so it should be one less bug-prone area to figure out. You can now set windows fullscreen using the layout system:

     manager.layout.add_slot("Body")
     manager.add(window, assign="body")

    More on the layout API later.

Refactors

  • Add new, customizable window blur styles (114da3a)

    The old style was visually impressive, however also prone to causing bugs that could not be resolved with either stripping functionality or changing the library source. The new version follows traditional and modern TUIs more closely by only changing the color of borders, and is actually customizable. However, since there are new styles for both blurred and focused states for both the corner and border set of characters, a small helper was also added, Window.set_focus_styles.

  • Rename all builtin aliases to follow the domain.item naming scheme (3a416f5)

  • Improve animation stability (c806b97, 05b6126)

  • Start caching real_length, strip_markup & strip_ansi results (a07ce9f, 532e3cc)

    This was altogether a sub-10 line change, but it actually has huge performance implications. It will also be useful for the future, more advanced compositing running at an acceptable speed.

  • Refactor StyledText to only tokenize on-demand (b15eba8)

  • Rewrite the pattern used to match markup to improve how escapes are handled (d656b75, 5a37470)

  • Introduce new, layout based CLI with an RGB colorpicker & inspector (3143336)

    This started off as a way to showcase the new layouts, and wasn't even supposed to be a full rewrite. However, I eventually realized how ridiculously over-engineered the previous CLI was, so I decided to come up with a new system. There is a new app, Inspector, which basically runs the inspect function interactively, and can be called from the command line with no interaction. There is also now an RGB color picker, and some bindings to the new highlight_python function (more on that later).

    For some reason my favorite part of this new module is the output of -v:

    Screenshot 2022-05-12 at 23 37 24

Additions

  • Add Collapsible widget (114da3a)

  • Add new highlighters module with RegexHighlighter class (7e6cd02, 6bd3d4a)

    This class can be used to statically highlight any type of text, combining regex with TIM. Since it's fully based around regex it will never be a full highlighter, a'la Pygments, but I feel like that is outside of the scope of the library. See an example of this in the showcase section.

  • Add is_scroll, is_primary & is_secondary helpers to MouseEvent (522e4dd)

  • Add Terminal.no_record context (b7d4976)

  • Add WindowManager.alert and WindowManager.float methods (53e9b84)

    The alert function is pretty much the exact same in terms of functionality to the one that came before it, though it's been refactored a bit. The toast method however is completely new to the library, but it's goal and general look & feel come from the Android feature of the same name. I personally think it looks dope, and you catch a glance at it on ptg startup!

  • Add layout assign parameter to WindowManager.add (6c95ac0)

  • Add window manager Layout class (8250d6d)

    This really is the feature of this update. Essentially, layouts let you define sizes & positions for your windows, which is then continually applied whenever the terminal resizes or windows are removed/added. It also tries to be minimal but expressive in its syntax, and do as many dynamic things as possible.

    For example, take the following code:

     manager.layout = ptg.Layout()
     manager.layout.add_slot("Header", height=5)
     manager.layout.add_break()
     manager.layout.add_slot("Sidebar", width=0.2)
     manager.layout.add_slot("Body")
     manager.layout.add_break()
     manager.layout.add_slot("Footer", height=3)

    It creates a 3-row layout. The first row just contains a header with a height of 5, and spanning the entire terminal in its width. The second row contains a sidebar that is 20% of the terminal's width, and a body that fills whatever remaining space it has. Finally, we have a footer, that is 3 characters height and once again spans the entire terminal.

    You may have noticed that the second row had no height definitions whatsoever. This is because the layout can calculate the remaining height for the row based on the slots with static dimensions. In effect, this means that you only need to specify the 'defining dimension' for each slot, and the rest will be calculated by the library. This Dimension system may in the future be extended into the realm of widgets, and I have plans of first adding the ability to nest layouts, and maybe eventually adding a layout-based container widget.

    Screenshot 2022-05-12 at 23 49 27

Bugfixes

  • Fix overly greedy optimization in MarkupLanguage.parse getting rid of important unsetters (32e30b5)
  • Fix / not being handled properly in various places (2b15e51, e3e9e87)

Showcase

Screenshot 2022-05-12 at 23 02 27
Screenshot 2022-05-12 at 23 02 55
Screenshot 2022-05-12 at 23 04 02
Screenshot 2022-05-12 at 23 05 36

Closing thoughts

That's all for now. Next up should be either a full ScrollView widget that wraps around an inner Container, a more efficient compositing method, a universal & real-life user tested color palette or a combination of all. Since I'm currently quite busy with university I'm not sure when I'll get around to creating a new release, but it shouldn't be too far off.

As a final note, this release might be the one to push the library over 1000 stars. Thank you to everyone who has ever used PTG, raised an issue or even just checked it out or left a critical HackerNews comment for it. I never imagined my work to become this popular, and I can't wait to see where it ends up.

Ok, bye

🚀

v5.0.0: Compositing a better future!

19 Apr 17:00
Compare
Choose a tag to compare

Pretentious title, I know.

This version is essentially a rewrite of the window_manager module. There is now better task distribution, things are cleaner, and there is one less 1000+ line module in the library.

The biggest change was moving all printing logic into a new module compositor. At the moment this does basically the same thing as WindowManager used to, albeit a slight bit more optimized, but in the future implementing a difference-only drawer should be relatively simple.

The animation system also got completely rewritten. I'm still not fully satisfied with some aspects, but this should be the last API-breaking set of changes done to it for the foreseeable future. The TL;DR of this would be something like:

  • A single Animation base class
  • Looping animations
  • An exposed FloatAnimation that simply transitions its state from 0.0 to 1.0 over the given duration
  • Better logic, usage of elapsed time instead of independent measurements & framerate calculations

Overall, this release provides much better internal structure and stability, plus some extra features and additions. The upcoming couple of releases will focus more on the feature side of things, but there is at least one similar refactor planned (for the ansi_interface module), though likely involving less API changes.


Items marked in bold are API breaking changes.

Refactors

  • Refactor window_manager.py into 3 files under window_manager submodule
  • Refactor the entire animation system (71cf261)
  • Move scrolling behaviour into new ScrollableWidget class (37f8ffa)
  • Improve Terminal API (d0209fb)
  • Rename widgets/layoutswidgets/containers (e06608d)

Additions

  • Add StyleManager.__call__ method that sets the given **kwargs keys and values (2e69cbf)
  • Add (currently unused) Widget.get_change helper (48d3e5c)

Showcase

As this is an internal refactor based update, there isn't much to showcase. Toggling fullscreen on a Window is now animated, and some other animations are improved.

Here is a random SVG screenshot.

screenshot

Unrelated note on browser rendering oddities HTML engines seem to render `foreignObject` SVG-s just slightly differently to eachother. Here is what the above looks like on Chrome for my machine:

_Users_lapis_Code_Projects_pytermgui_screenshot svg

In comparison, my phone's GitHub app tends to render text off the "terminal" panel for some reason, and even the browser tends to render the colored unicode blocks incorrectly, often adding a border around them. Odd!