Gfret Gtk4 Port Part 3

Current status

Earlier in the week, the preferences were brought back without too much trouble. I've taken some time to do a bit of extra work to the public API of the backend crate, fretboard_layout. Rather than storing colors as a string, we now save the individual RGBA channel values, and can do so using u8 or f64 storage types.

This morning I spent a little bit of time on making the file saving code a bit more idiomatic and bringing the templates features back.

#[derive(Clone, Deserialize, Debug, Serialize)]
pub struct HexColor {
    pub color: String,
    pub alpha: f64,
}

#[derive(Clone, Deserialize, Debug, Serialize)]
pub struct RGBA {
    pub red: f64,
    pub green: f64,
    pub blue: f64,
    pub alpha: f64,
}

#[derive(Clone, Deserialize, Debug, Serialize)]
pub struct ReducedRGBA {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
    pub alpha: u8,
}

#[derive(Clone, Deserialize, Debug, Serialize)]
#[serde(tag = "ColorType")]
pub enum Color {
    Hex(HexColor),
    Reduced(ReducedRGBA),
    Rgba(RGBA),
}

This is the storage types for color values. As can be seen, we're using an enum here, but it turns out that the serde and toml combination won't work here unless we specify a tag for our Color enum. I've been bouncing back and forth from Rust to Zig a lot lately, and this is interesting to me. Instead of having an enum type which accepts values, Zig has enum, union, and tagged union types. An enum is just that, an enumerated type, but without any storage. A union is roughly equivalent to the C counterpart, but where things get interesting (and indead very useful) is the tagged union type, which can be used in many of the same ways as a Rust enum with associated value. I've speculated before that under the hood the two types function fairly similarly. In the case of Zig, one can switch on the tag and extract the value as a payload. Anyway, interesting to me that in serde we're having to specify an external tag.

What's left

So there are three major things to reimplement before I'm calling feature parity with the gtk3 version. The preferences dialog needs to be able to get it's values from the saved configuration when it opens. Currently, the configuration is saved, but when the dialog is opened the widgets display default values.

Second, I need to reimplement loading of templates from the command line when the program first loads. This is hampered right now by the fact that in Gtk4, command line arguments are no longer passed into the GtkApplication struct. Instead, it is expected that every application will register with Dbus and be told through the system bus when it is time to open a file. Well, I'm not a fan, at least not if that's the only option. The solution I have in mind is to just check whether we have a valid template file if one is requested on the command line, and then copy it's contents into the statefile before loading the gui. Not great, but look Ma, no Dbus.

The last one is a little easier. I just need to hook the saved configuration back up to the CLI again.

There are a few minor differences like the menu items no longer having icons. The upstream devs are all very Gnome centric, and while I could still put them back in I'm not sure it's something that I care about.

Ah, one more major thing before feature parity actually. Keyboard shortcuts. But since I intend to make use of a gtk shortcut map and make them user configurable, this will actually be tied cosely to the mapped out upgrades planned for this cycle.

Upgrades

So let's talk upgrades then. The original Gfret only supported metric system measurements in spite of my being an "American". I'm going to go both the backend and gui and add support for Imperial measurements, because I'm sure that almost anyone besides myself in this country will want that. This is going to be a configuration option, not a runtime specification.

I'm also going to add the ability to do left handed designs. Logically speaking, there is no difference between a left and right handed fretboard assuming that we are looking at a monoscale design, so this gives us an enumeration of three basic neck variants. So instead of a boolean and an optional treble side scale, the storage type for this specification is going to change to an enum with scale values embedded into it. On the gui I'm planning to imlement this with a combo box, which will hide the treble scale widgets completely when set to monoscale. Currently, the scale and spinbox widgets are just set insensitive.