Enhancing Rust GUI Development Techniques
The lack of enduring Graphics User Interface (GUI) solutions remains an ongoing challenge within the Rust ecosystem, despite the dedication of numerous talented individuals to the cause. While some existing options show promise, no framework has yet emerged as the definitive standard.
In my view, this situation arises from the attempt of each GUI framework to address two distinct issues simultaneously, which should naturally be categorized into two separate classes of problems:
- The layout calculations.
- The rendering and the user events handling.
Let’s examine the BioShock Infinite start menu GUI:
Clearly, the user-facing representation of the BioShock internal rendering framework is vastly different from what Egui is capable of displaying on the screen:
These two modeling examples share very little, if anything, in common:
- The rendering workflow model.
- The default sets of proposed widgets.
- Where and how these widgets should be displayed.
- The customization capabilities of the widget system.
BioShock’s framework likely has minimal to no customization capabilities (as it was specifically designed for the BioShock game engine), while even the most extensive customization features of Egui will not enable us to replicate the BioShock screenshot. Attempting to do so would be an inappropriate use of Egui.
However, what these two solutions have in common are the methods by which the alignment between “virtual” widgets are calculated, including their positions and sizes. This is what I refer to as the Layout Calculations. The widgets, as box frames, still maintain specific margins and paddings between each other that adhere to invisible guidelines or the invisible parent box frames.
Even if the arrangement of BioShock’s widgets was manually but precisely positioned by a human, this task could certainly be delegated to a purely math-based (Rust) library.
And even if we narrow down the scope of the GUI development problem to the rendering of standard widgets for regular window interfaces, end-users will still encounter the Four Incompatibility Problems mentioned above when integrating a specific solution into their application design.
We can never tell in advance which rendering/user interaction workflow design and other design specifics would be the best choice for the majority of Rust GUI applications. This is because Rust itself is a general-purpose language intended to solve an unbounded number of problems across various domains, with no single specific domain known in advance.
For this reason, I believe there could not be, and never will be, a single universal GUI solution that would satisfy the needs of the majority.
However, this does not imply that there should not be domain-specific solutions for certain categories of applications, where the use-case domains can be clearly defined.
I believe we could enhance the ecosystem by isolating the layout calculations sub-task within a distinct category of Rust libraries, forming the foundation for the development of new end GUI frameworks. This would prove beneficial for both GUI framework developers and the development of end applications, particularly for scenarios such as video games, where a customized, non-standard GUI rendering solution is required.
What Exactly is the Layout Calculations Library?
Naturally, it should be domain-agnostic and likely a dependency-free, pure geometry calculations solution.
We can draw an analogy to linear algebra libraries like Nalgebra or Cgmath. Such libraries conduct pure mathematical computations with mathematical objects like matrices, vectors, or quaternions. They make no assumptions about particular 3D object sets, their types, or the specifics of the rendering subsystem. Such inquiries lie beyond their scope. Nonetheless, they prove highly useful in the domain of 3D scene manipulations when applied to specific end applications or graphics API frameworks.
Following this analogy, our Layout Calculations Library should likely focus exclusively on abstract rectangular frames, their compositions, and their relative or absolute arrangements based on configured constraints such as margins, padding, or any numeric parameters required by a specific library layout model.
The purpose of this library is to serve as the single source of truth regarding the geometry of the system of rectangular frames. Its sole function is to be capable of answering questions such as:
- What are the relative or absolute coordinates of a particular frame, or of a set of frames?
- Which frame(s) does a particular point with absolute coordinates hit (for instance, in the case of cursor clicks)?
In essence, this problem relates to the resolution of the system of linear equations/inequalities, also known as Linear Programming. There exists a comprehensive and well-developed general-purpose theory on this subject, along with some Rust LP libraries such as Minilp.
However, our domain is more specific. We can restrict it to the nested system of rectangular areas aligned with the 2D coordinate axis, thereby simplifying all underlying mathematics accordingly. Following the same design principle as Rust linear algebra libraries, which typically confine their domains to 2/3-dimensional spaces based on practical use cases, we can reasonably assume that our Layout Calculations Library will be employed solely for rectangular geometry calculations.
Moreover, the design of such a library should likely prioritize the best practices from existing layout models and utilize terminology familiar to a wider audience.
I haven’t conducted exhaustive research on this topic (as it is not the purpose of my article), but to name a few:
Whether our Layout Calculations Library would implement one of these, propose a mixture, or suggest something entirely new is an open design question, and, I believe, an intriguing challenge for the library developer!
Also, we can raise a question about computational complexity. Recomputing the entire system of our abstract rectangular frames to answer specific questions about their geometric state could be inefficient in terms of performance. Furthermore, the input properties of the frames system could change over time, and this could happen frequently in certain situations (e.g., when the user resizes the window), further complicating the computations.
Perhaps, for large inputs, we should consider caching and incremental recomputations as well, which could elevate our solution beyond being just a pure math calculations library. Once again, whether such caching capabilities are built-in to our solution or assumed to be managed by third-party libraries is also an open design question.
Having a single “batteries-included” solution still fits, in my opinion, within the domain of libraries in this class, which leads us to the question of what should intentionally not be included in the Layout Calculations Library:
- Particular sets of widgets, especially those concerning user interaction and rendering specifics, should be avoided. Objects such as “Buttons” or “TextEdit” must not be included in this Library.
- Any assumptions about rendering workflows and user interactions must be disregarded.
- Any user-facing visual design features, such as “panel color” or “box shadows,” must be disregarded.
Essentially, it is not acceptable for the Library to provide any form of user interface renderer customization capabilities. Any rendering subsystem assumptions should be left to third-party Rendering GUI Frameworks, enabling the seamless reuse of your Layout Calculations Library in both the Windows GUI and the BioShock game engine start menu.
Conclusion
Essentially, what I have been discussing in this article is the application of the Separation of Concerns design principle to the development of Rust GUI frameworks.
I believe that dividing the GUI development domain into two independent yet seamlessly interconnected subtasks could enhance the current state of the Rust GUI ecosystem.
This idea has been with me for quite some time. While I do not presently have the opportunity to work on a specific solution, I hope that my insights will be beneficial for those of you currently approaching this challenge.
Ilya Lakhin.