Layout is one of the hardest problems in UI toolkit design because there are so many different ways to design a layout.
Creating a system that supports them all is practically impossible. Even creating a system that merely makes them
all possible to implement (even if difficult) is a challenge. And making the layout fast is even harder. However,
given that we are targeting embedded systems, rather than general purpose interfaces (ex: no resizable windows), we
should be able to narrow down the scope to something usable. Let’s dig in.
As part of my ongoing research with embedded Rust on ESP32 devices, (starting with the T-Deck but eventually branching out to other devices), I started creating a simple reusable UI toolkit to automate the boring code that I end up writing over and over for every example. The code is on github but I haven't made a crate for it yet.
I’m really enjoying using no_std Rust for embedded programming on
the Lilygo T-deck and
I want to share what I’ve learned so far. There are many excellent tutorials and docs on Embedded Rust in general and the ESP32 in particular, so I’m going to cover things that are specific to the T-deck or that I’ve found to be under documented. Today let’s start by looking at the T-deck’s signature feature, the keyboard.
I've been having a lot of fun with this little device called a LilyGo T-Deck. It's built on an ESP32-S3, has a 320x240 touch screen, a funky little track ball, built in wi-fi, and a seriously nostalgia-inducing physical keyboard. I bought the T-Deck about a year ago but because I was sick (with undiagnosed Type 1 Diabetes) and haven't done much with it until the past few weeks.
How can you draw a filled rectangle fast? By making it not slow! Great if you are using a GPU to accelerate rectangle drawing for you, but what if you are doing it oldskool with an in memory frame-buffer? You'd probably write some code like this:
After a few weeks of work I’ve been able to get Tetris to boot and play. I can also run Dr Mario to enter the play screen but all of the pieces are hidden for some reason.
A week or so ago I ran across a video called The Ultimate Gameboy Talk, and indeed it was. Inspired by the simplicity and elegance of the original Gameboy, I decided to try my hand at building an emulator. A week later I have this:
At this point I’ve been working on the browser for over a month and I’ve learned a ton about Rust. Sadly, it’s time for me to stop. I’ve taken it about as far as I can.
Rendering. The big payoff. This is where we actually get to see something drawn to the screen. This is where the mini-browser starts to feel real. It’s also where the code is straight forward and the hard part is picking the library.
Now we get to the big dramatic part of building a browser. Layout. Until now we’ve just had a tree of data. This is the part where we position actual rectangles and colors and text blocks. The part where we do line wrapping and worry about font sizes. This is the real deal! Let’s dive in.
Styling in a browser is conceptually very simple. We’ve parsed the DOM into a tree structure of elements. We’ve parsed the CSS into a tree structure of rules.
I have greatly enjoyed the reliability of Rust so far, but a few things really annoy / mystify me. One is the type annotations. I understand that type annotations lets you say what type another type is defined in terms of. The common case is a vector of points, with something like:
I had hoped to be talking more about how to build a browser, but reality has intervened. It’s taken about a week, but the family is starting to calm down now and get used to the new normal of staying home. I’ve stocked up on supplies and prepped for exclusively working from home. Jesse is recovered from pink-eye and a cold, and we’ve scrubbed the house clean. Now all we can do is wait and try to help others as best we can.
I have done something very foolish. I've started building a new web browser. From scratch. Not a new wrapper around Chromium or WebKit or Gecko. No, an actual new browser. Why have I done such a thing?!