bWidgets
Core widget toolkit designed for Blender
|
Which problems does the current Blender widget code have and how does bWidgets try to solve them?
With bWidgets we try to solve a number of issues the current Blender UI code has. The following sections describe some of these issues and how bWidgets tries to address them.
Note
At this point bWidgets is limited to drawing and some very basic handling. Hence, much of the issues mentioned here are not addressed yet, but bWidgets is designed to do so from the ground up.
Aside from a general MVC approach (which is broken in many places), the Blender interface code as a whole doesn't really follow any design ideas. "Just make it work" is the route it takes. Granted, this may not necessarily be bad, and after all this code worked for many years; however, it became a big mass of spaghetti code that is hard to maintain, extend and understand.
bWidgets does have a far more defined design. It sticks to using MVC and it is much more strict in applying it. Further, it incorporates useful abstractions to clearly separate different parts of the code. That alone adds another level of flexibility.
For more information, check the [design overview] (Design Overview).
The central widget related data-structures (e.g. uiBlock
, uiBut
, uiHandleButtonData
) try to be one-size-fits-all designs. All widgets use exactly these structures, even though they need to store completely different data. Hacks are used to get variables to work for multiple widget-types, even if the needed data-type differs. Examples are the uiBut.a1
and uiBut.a2
fields, or uiBut.poin
. Additionally, the data-structures contain many members they only need in certain situations (e.g. for a certain button type).
These points make the structures pretty confusing to work with and ask for trouble (and add unnecessary bytes of memory).
In the bWidgets design, every widget-type has its own data-structure, specifically crafted for its own needs. Data-duplication across multiple widget-types can be avoided using inheritance. The controller (speaking in MVC terminology) can implement interfaces which store custom data as needed, like RNA or operator data. Although not implemented yet, imagine it to work like this: the controller registers a handler object for widgets (e.g. uiRNAStringHandler
or uiOperatorHandler
), that stores its own data and gets notified on state changes.
The Blender UI code is built really tightly around the Blender data systems, much closer than healthy. There are calls to DNA and RNA everywhere. Different modules are also mixed, the UI code does many Blender-kernel or window-manager calls. Such tight coupling of data and modules provokes problems:
Making the code more modular and adding reasonable abstractions should help here.
bWidgets would not know anything about Blender's internal data systems. In its design, widgets are more like blackboxes, manufactured to represent more complex data with primitive tools. They only know about strings, numeric-values, color values, etc. The controller (in MVC terms) manages the application specific data manipulations (e.g. updating DNA through RNA).
It is general consensus that widget/GUI toolkits benefit from object-oriented designs. There are many different kinds of GUI elements and there is lots of overlap between them. Such overlap can be managed nicely using object-oriented-programming (OOP) inheritance in code. Encapsulation and abstraction are useful to ensure valid states (often through manager-objects and the like), maintainability and great flexibility. Blender doesn't follow any of these conventions, even though they seem to be very reasonable.
For new programmers, this adds another hurdle, since they are used to entirely different designs.
Note that this point is not about programming languages: GTK+ (a widely used widget toolkit) is written in C, but tries to emulate OOP features as much as possible. However an OOP-based language should still be better suited.
The design of bWidgets is based on these conventions and should be able to gain from their benefits. It also uses C++ which has OOP features builtin.
No part of the Blender UI code is unit-tested as of now. Because of the tight coupling of modules and data, it's just not easy to do. A unit-test can't simply create a widget to test some of its functionality, a big part of the widget pipeline would have to be involved. Or at least, only very few units can be tested in a useful manner.
Neither is there a way to render the GUI or parts of it (individual widgets) into an image. Such images would be useful for regression-tests, for example in the layout- or theme-system.
bWidgets addresses this as follows:
Widgets are grouped together into blocks (uiBlock
) in Blender. This system is not very flexible in that there is a lack of further grouping capabilities. There's no nesting of blocks.
A hierarchical structure could bring some benefits:
uiBlock
with case specific data-structures.On the downside, hierarchical or tree structures are harder to work with than linear lists, thus it's easier to make mistakes. We can unit-test hierarchies nicely though.
In bWidgets we could do what Qt does: Widgets can have child widgets, whereby each parent widget is responsible for managing the layout of its children. In most cases, the parent widget would ask the layout system to do needed calculations.
interface_handlers.c
Most GUI interaction code is placed in interface_handerls.c
, a 10,000+ lines beast. It's really hard to navigate in such a file, though the file size by far isn't the only problem (that alone would be easy to solve):
ui_but_menu()
).Popup handling is part of interface_handlers.c
and suffers from the same issues. It still deserves special mention though:
If you want to get a taste of it, have a look at ui_handle_menus_recursive()
, or the gorgeous ui_handle_menu_event()
.
For bWidgets we plan to have something like a popup-manager to take over high-level popup handling. Using the hierarchical widget storage described earlier, proper relations can be established that make the execution easier to follow. We may also add some internal UI-context to bWidgets for managing states. All these changes should split the popup handling into smaller, more manageable parts.
The current theme-system only allows limited tweaks to the appearance of GUI elements. Why shouldn't it have full control over how the GUI looks? What if it had control over header sizes, widget sizes, layout margins, text alignments, radii of widget corners, the used icon set, icon sizes, widget drop-shadows, ... With the current approach to theming in Blender that's not possible, because it mainly concerns colors and every option is exposed in the UI. We should not expose any more theme options.
Let's rethink theming completely.
bWidgets has Styles builtin, which should eventually give full control over how widgets look. These styles can be CSS based (WIP), meaning users can edit them even if not exposed in the UI. And, they should be able to share styles online. The way they are designed, styles (and thus CSS) can affect properties outside of bWidgets too, for example header sizes.
Imagine a theming-system, which lets you choose from a flat, depth-based design (like Google Material Design), a more classic design, or even an OS-native design in Blender. And that fully customizable and with support for sharing.
GTK+ is a great example of such flexibility
Eventually, styles should be able to completely override the drawing (and handling) of widgets. For example a theme/style could make Blender's unconventional number-widgets use a conventional design for those who want it.
There is another big benefit to take from such a flexible styling system: UIs for usage in virtual reality worlds could be just another style. A style that makes buttons bigger, easier to read and easier to operate via handheld controllers.
Depending on where widgets are drawn, they may need to use different theme colors. Blender's push-buttons (to execute an operator) are drawn completely different when placed in a menu - and different again when placed in a pie-menu. Similarly, text colors may need adjustments when used in front of special backgrounds (see T48241). Currently, Blender solves this with hardcoded checks for these special cases. These become hard to keep track of and maintain. Fixing problems with the hard-coded checks is no rocket-science, but we can actually get rid of them entirely.
A widget theming system that allows overrides based on where widgets are placed in a hierarchy would be preferable. CSS has that builtin: The bwMenu > bwPushButton
CSS selector would apply its style properties only to push-buttons that are placed directly in a menu. bWidgets could support that.
Blender already supports storing and loading themes using XML. However it is a markup language, not a style-sheet language. In theory we could add all the features we need to it by adding more custom tags and properties. But all that is just emulating something that CSS is built to do. Just look at one of the theme files, and it's obvious how it tries to mimic CSS. Designers should also be familiar with CSS, unlike some Blender specific XML usage.
The CSS standards define many features we'd need for an advanced theme-system:
shadetop
and shadedown
for widget themes)So we would be able to support many of the CSS standard properties, and the selectors suit our needs as well. There are CSS parsing libraries that we can use. Although we may have to write our own code to interpret the parsing result according to the standards. We can start with a small supported subset of the standards and add more features as needed.
Work on CSS support is ongoing in the css_styles branch.