Gfret Gtk4 Port Part 1

Gfret was written originally as a CLI based program, later expanded with a gui which was originally in PyQT calling the Rust binary to do it's work, which I then changed to PyGtk due to some issues I was having with scaling the preview image in Qt. Eventually the scaling was solved and for a time, the program had both Gtk and Qt based gui's.

The Python gui, while it worked well enough, was only ever intended to be a prototype. Eventually I sat down and reimplemented what I had done in PyGtk using the awesome gtk-rs bindings, which were at the time stable only for Gtk3. Along the way the program gained some additional capabilities, including a template system allowing a user to reload a previously created fretboard.

Now that Gtk4 is available via gtk4-rs on crates.io I thought it might be a good time to revisit the program and add a few extras while porting the UI over to the new library.

What's going to be added:

  • The new Gtk4 interface (duh)
  • The ability to use imperial measurements (currently only metric is supported)
  • The ability to do both left and right handed necks
  • Splitting the business logic out from the program into a separate crate
    • Explore the feasability of a Qt gui and cross platform support

What's done now

The crate containing the business logic of the program has been split off in the best way I could manage. This was complicated somewhat by some decisions I made originally when writing Gfrfet, as I didn't manage quite as much separation of the program logic from the interface as I had intended. The main areas of overlap were in saving files and the configuration system. Until these parts are re-implemented more cleanly, the Cargo.toml will point at the git repo instead of crates.io. Indeed, I'm not publishing the crate until I fix a few more things and add some more tests.

The main window runs and the preview gets updated when any controls are changed. The preferences window now also runs, but none of the preferences are loaded or saved yet. The templates module has been added back in.

What's left to do

  • Saving files to be implemented in the backend crate
  • Adjust the CLI options to be more logical, as this is definitely going to be a major version release and I can break API.
  • Implement variants as an enum of monoscale, multiscale-right and multiscale-left
  • Implement conversion between Metric and Imperial
  • Add the previous two items as controls in the main window
  • Load and save templates (partially done)
  • Load and save config (partially done)

Pain points

In Gtk4 it is now significantly more difficult to get notified when the window is resized. Where previously I could simply connect to the "check_resize" signal, this no longer exists. Instead, it's going to be required to get a notification for a property change for GtkWindow:default-width and GtkWindow:default-height.

Glade is not being ported to Gtk4, and it's replacement isn't even planned yet beyond some talk that there will be a replacement. This means manually porting Glade xml to the gtk4 ui file format, which is similar but has enough differences to cause quite a number of visual changes without extensive manual intervention.

Menus have been simplified in Gtk4, greatly increasing the complexity of creating menus. Maybe it's actually easier, but a complete and functioning example in Rust has proven to be a bit of a unicorn. Note that I'm not trying to bring back the menu bar paradigm that Gnome loves to hate these days, I'm just trying to tuck a few things away in a hamburger menu to clean up the UI, which is not supposed to be a problem with Gnome's Design Nazis.

Not really painful

Don't get me wrong, Gtk4 is a vast improvement over previous versions of Gtk. The Rust crates are becoming quite mature and the consistency of behavior has been improved by leaps and bounds. In a lot of cases my compiler errors have come down to a function changing name by cutting off one redundant word, and I appreciate less verbosity so long as the meaning is conveyed.

I expect to have the interface fully working and back to feature parity in about a week's time or less, barring unforseen issues. Tackling the new features should be straightforward, but will require at least a little bit of time.

After buttoning up the 1.0 release a few montch ago, I began focusing on Zig and began writing Zterm and the zig-gtk bindings. I also took a good amount of time to spend with family as I had a montch long visit with my youngest daughter. I was afraid that after all of that I might have some difficulty getting back into Rust, but that has proven to not be the case at all. Apart from a couple times trying to put methods into a function declaration block instead of it's own impl block, the syntax came right back. Indeed, Rust and Zig share a lot of syntax as well as a lot of programming concepts. And I suppose that all of the time I spent in Zig was informed by my experience with Rust's compiler, which tends to enforce some good habits.

Ah, the compiler. Rustc has, simply put, the best error messages of any compiler that I have ever seen. It's unheard of anywhere else, but Rustc often has the fix right in the error message in a form which can be copy-pasted into your code. I would hug this compiler if I could.