Over the past decade and a bit, on almost every project I've worked on, I've come to realise that there is often quite a massive gulf between what the Standard Library + UI Toolkits typically offer, and what it is that you really typically require when building anything of consequence for the real world. The problem is such that really, you end up needing to spend quite a bit of time re-inventing the following sub-systems for each project OR end up paying the tax of not having these for each and every feature you add (by effectively reimplementing them *per feature* instead, but without the benefits that having the standardised system brings).
While the initial seeds of what I'm about to discuss date back to around 2010, things really started to take shape around 2018/2019, when I first seriously started mooting the idea of maybe creating my own Programming Language / Environment someday (i.e. "Kea"), a batteries-included environment for doing everything "my way"...
One of the key aspects of that language would be that the following functionality / capabilities would end up being "baked" into the language as first-class citizens. Before fully embarking on that journey again, I thought it would be good to first prototype these systems in other environments where they may help advance the overall state of the software industry. Hence this blog post.
The Short List - Core Functionality:
* 1) Property Metadata System (i.e. something like Blender's "RNA" system)
* 2a) Bidirectional Property Binding (Data Objects <-> UI Widgets)
* 2b) Widget / Factory that creates standard auto-bound widgets, given only the Property ID + host object reference (i.e. something like Blender's UI widgets)
* 3) Automatic hierarchical property serialisation system (i.e. something like Skyline-X's Preference Sets)
* 4) Version patching (i.e. something like what is used for Blender's SDNA system)
* Includes utilities related to version number management and/or Git branch/revision info
* 5) System for Physically-Based Unit Handling and/or Unit Conversions
The Short List - Extended Functionality:
* 6) Static/Dynamic Expressions / Drivers, Expression-Evaluation, and/or String Template Substitutions
* 7) A "Datablocks" System + CRUD API's for managing those
* 8) "User Preferences" system
* 9) "Operator"-like system (for standardised logging / error handling, undo/redo, and background-exec of expensive long-running-processes) - inspired by what Blender uses since 2.5
* 10) Basic Extendable Templates/Base-Implementations for Handling the Following Functionality (Optional)
* Standard logic for New / Load Project/ Save Project stuff
* Standard logic for handling cache files / support data files
* Built-in screenshot per-viewport/view-angle screenshot functionality (with repeatable / savable parametric configuration)
* Grease Pencil / Built-in Freehand Annotation Tools
* Node-editor
* 11) UI Toolkit Extensions + Features
* Collapsible Panel implementation (Note: This is surprisingly absent from most UI toolkits in practice)
* Control-Gain Ladders / Input Convenience mechanisms
* Popup panels templates
* "Overlay toolbar / interactive viewport tools" system
* Popup info panels / extended menus for Unit Conversions + String Templating functionality
Some of you may be reading that thinking that much of that reads like a stripped down version of Blender's internals (minus all the 3D-specific stuff).
a) Yes, you're right. Much of this is inspired by stuff that I found to have been really awesome infrastructure innovations from Blender that the rest of the world should be using too (i.e. it took nearly 3 decades for the industry at large to see the light and adopt what they refer to as "Reactive" patterns for handling UI coding / updating)
b) I also think it's important to try porting these concepts to other implementations / environments, versus forking the Blender code then forever trying to keep it in sync with upstream.
Why the Core vs Extended Split?
Well, the last time I implemented something like this, I managed to get the main benefits (resulting in a useful basic app) by implementing the "core" parts first (i.e. the property + auto-widget components), allowing for greater development velocity after investing the ~1 month of work required to set those parts up.
However, the extended functionality then took the greater part of a year or two to fill in, gradually + over time as required for various functionality. Some of these are also non-trivial developments in and of themselves. Therefore, they are more "nice to have" things that help fill out the toolkit (vs being the essential parts which set this approach apart from what existing toolkits offer)
Core Concepts Explained
Let's now look into what each of these entail, and why their significance.
1) Property Definitions System + UI Auto-Widgets + Auto-Serialisation
The most important aspect of this whole system relates to the following tenets:
* i) The definitions for how a property behaves when interacted with should be defined in one place (i.e. where the property is defined, or maybe in an attached / related file). Crucially, that location should be in the application / business logic code (i.e. model / controller layers) and NOT in the UI layer
* ii) The widget-definition code should by and large *NOT* be where all the behavioural aspects for editing that property get defined. Doing so makes it a pain trying to expose that setting in multiple places / for different reasons, while maintaining the same behaviours. It also simplifies the UI layout code (which in turn, means that you're not trying to resort to horrid "Form Designer" drag and drop tools to try and lay these things out)
* iii) You should not be explicitly instantiating widgets from the UI toolkit yourself, and hooking them up to the relevant backing properties.
In almost every toolkit I've ever used, you'll inevitably find that certain widgets have certain default behaviour quirks / default-settings that are either really annoying OR glitchy OR incompatible with your designs, and you'll need to want to override those quirks. For every. single. instance. EVERYWHERE. Oh, and you'll always be wanting to style your widgets in particular ways / etc, so there is a consistent LnF across the application.
Hence, it's actually easier to just have all of these widgets auto-generated to confirm to the house-style for handling that type of property in your application, making maintenance fixes easier.
* iv) As a software developer, you should NOT have to waste your time creating repetitious plumbing boilerplate to flush values back and forth between various objects to keep them in sync (IIRC, was 16-20 lines of boilerplate for property definitions with PyQt for app-side definitions). This is boring monkey-work that is easy for humans to stuff up, and is an example of something where the machine should "just do it" without requiring manual human work.
* v) To make the auto-serialisation work, you need to not only know what the properties are called and their types, but also additional info such as whether they should get skipped for serialisation (i.e. internal-only settings that also got exposed as properties like the others for easier checking from the UI / etc.)
No comments:
Post a Comment