freya is more impressive than I thought.

93 points avinthakur080 31 comments

I recently started building a hobby calculator app in egui, given its popularity. The app mimics a Graphing calculator that was abandoned several years ago.

I started with egui, given its popularity, but soon started facing hurdles because it challenges my web dev (aka retained mode) brain. I'm curious if these points are common among other devs also or how they handle it.

First, I started with egui ( only dependencies: eframe + peg )

  • Being immediate mode, the components often don't know about the layout requirements of their next siblings or often their children/parent. This creates some challenges, like a flexibly growing widget either needs to be created last or manual size calculations need to be done to accommodate the next elements.
  • We are free to create state anyway, like mutable members in a component struct or get_temp, get_persisted, etc. methods, which hand over the state management to egui. This threw me into a puzzle of finding what is best for my case. I ended up using the mutable members in a component struct.
  • The event handling is also something that gets weird at times. While the trivial cases of handling clicks are as simple as `if btn_resp.clicked() {...}`, I searched unnecessarily long for other cases, like how to detect Enter presses on an Input element. Apparently, it is `input_resp.lost_focus() && ui.input(|s| s.key_pressed(Key::Enter))`, but it felt unconvincing that this is the way.
  • Command-Action-Response (Message passing, command execution, etc) architecture is also something I spent significantly more time on, and even though I can make it work now, I don't think it is convincing. By Command-Action-Response, I mean a deeply nested component(keyboard button) triggering a command that should do calculations in a separate thread and get rendered in some other component. It requires passing channels deep down to your components, which works, but it will always be something that will trip me up into thinking of a better approach.
  • Lastly, I was learning and implementing by going through their examples or other public code, and often it was taking too much time for simple things. I found a few resources also, but after seeing that many are using App::update (now removed) instead of App::logic + App::ui, it again felt unconvincing to go with them, and I took the route of reading examples & public code to learn.

Then I tried rewriting it in Freya

While there were also new things to learn, it felt like a breeze till the end and I achieved the same state as the egui variant in probably 80% less time.

  • The documentation is really good. It gives an overview of not only modules, but also common patterns like for state management. Even beyond that, every other function would be documented with examples that show the usage patterns around it.
  • Being in retained mode, the layout management was really easy. It did take time to know things like a growing widget requires the parent to also be `.content(Flex)`, but it was still very intuitive, particularly coming from web dev / retained mode.
  • State management was even better. After just writing egui implementation, I tried creating similar state management types but eventually found that the use_state/use_memo hooks remove a lot of boilerplate. The state is reactive out of the box.
  • I expected a retained-mode app to take more RAM, but it was actually less. Egui app, in release mode, took 320+ MB, while Freya takes <270MB. The release binary is slightly large: 29MB egui vs 36MB Freya, but the RAM & CPU usage for freya was lesser.
  • Lastly, the freya app looked more polished and took way less time to reach a convincing state than egui.

Why am I writing this here?

See the above text as me dumping my naive observations after spending a day writing the egui+freya app. I am more interested in knowing from other developers who wrote egui/GUI apps on

  • whether they also encountered these speed bumps
  • if they found resources or developed patterns to ease out the things

P.S.: I don't think there is anything interesting or relevant in the implementation itself, that's why not putting the effort of creating a repo to share, but if someone is curious, I can push it.

Also, open to feedback - on the subject or the post, whatever.

https://preview.redd.it/onvvzgkbv1xh1.png?width=1377&format=png&auto=webp&s=1886642c6a2e48184c838471cc56e5f7eff6517f

EDIT: Changed image to have light mode (original) and dark mode variant of Freya