Week 28: Simpler framework configuration, and better in-editor rendering

A screenshot of the Godot editor with the Momma Duck project open, showing the about-screen scene open in the scene editor with styles rendering that were defined in the Scaffolder AutoLoad.
I fixed Scaffolder to run global-singleton scripts in-editor,
which is important for rendering accurate styling in the scene editor tool!

tl;dr: I simplified how my frameworks are configured, I fixed a big problem with in-editor rendering, and I made more styling improvements.

What happened last week?


  • I simplified how my frameworks are configured and bootstrapped.
    • I had received some feedback that there were a few too many moving parts to worry about!
    • The client now only needs to:
      1. Include the code in `addons/`
      2. Define an AutoLoad singleton
      3. Define framework parameters in an `app_manifest` dictionary
      4. Call `Sc.run(app_manifest)`
    • I also updated some names and error messages to be less confusing when getting started.
    • I also simplified the READMEs and getting-set-up docs.
  • I updated my AutoLoad global singleton scripts to run in-editor.
    • This gives a much better development experience!
    • The problem was that the scene editor tool had become less-and-less useful and reflected reality less-and-less as I moved more-and-more configuration into my global singletons.
    • There were some interesting quirks of the Godot editor to learn about and work around, but things are working pretty well!
  • I made some more small improvements to styling.

Laundry list

  • Framework configuration simplification

    • Refactor bootstrap/app-config system to require only a single invocation for the client.

    • Replace bootstrap class-extension pattern with composition, and use signals instead of overridden lifecycle methods.

    • Remove reference to main node from bootstrap.

    • Make app-config classes extend a common interface.

    • Remove the first-line-crash-reporter-notification log message.

    • Rename the Scaffolder, Surfacer, and MommaDuck AutoLoads, and rename the config filenames to match their corresponding AutoLoad names.

      • `scaffolder_config.gd` / `Gs` => `sc.gd` / `Sc`

      • `surafcer_config.gd` / `Surfacer` => `su.gd` / `Su`

      • `momma_duck_config.gd` / `MommaDuck` => `app.gd` / `App`

      • This has the downside of being less descriptive.

      • But, the consistency between filename and AutoLoad name is a benefit.

      • And having the AutoLoads use a very short/abbreviation name is good, since they are used all over the place.

    • Update READMEs.

    • Update screen accept/exit input handling to use registered actions.

    • Move some metadata methods out of the top-level Scaffolder config.

    • Add support for automatically defining various Project Settings fields and Input Map actions, so the client doesn't need to define these manually.

  • AutoLoad global-singleton in-editor support

    • Make top-level app-config AutoLoad singletons be `tool`s, so they can run in the editor, and remove in-editor guards from other `tool`s that were previously meant to avoid accessing AutoLoads.

  • Declare many more scripts as `tool`s.

  • Fix lots of console errors that were due to running scripts in the editor (now that they’re `tool`s).

  • Fix usages of _enter_tree and _exit_tree, so that they never rely on only being called once.

    • When running things in the game environment, I can reliably control a Node’s lifecycle and when it attaches and detaches from the scene tree.

    • However, when running things as `tool`s in the editor environment, I cannot predict or control when the editor will attach/detach nodes!

    • This was causing a really terrible experience where the editor would crash as soon as it opened itself and then tried to re-open the previous scenes, which would then crash because the editor detaches and reattaches them, which leads to invalid state, because I sometimes was freeing-up state on-detach.

    • So I use a custom `_destroy` method for any nodes that I need to clean-up state when they are done.

      • The downside of this approach is that I have to be very careful to remember to manually call `_destroy` on the right things at the right times.

  • Use Gs.device.get_viewport_size() instead of get_viewport().size directly.

    • The benefit of this, is that it will work even if the call-site context isn’t currently attached to the scene tree.

  • Declare most remaining scenes as `tool`s.

  • Styling improvements

    • Support StyleBoxTexture for overlay panels.

      • This gives me more control over how panel borders are rendered, by using nine-patch textures.

      • This also lets me render things with a more pixelated style.

      • The previous style I was using created flat colors, with rounded corners, and shadows.

      • Usages:

        • Dropdown/OptionButton panels

        • Welcome panel

        • Inspector panel

        • HUD key-value boxes

        • Etc.

  • Create ScaffolderHSeparator and ScaffolderVSeparator.

  • Use ScaffolderHSeparator in WelcomePanel and InspectorPanel.

  • Update StyleBoxTextureScalable to re-use a globally-cached Image, rather than re-creating the same re-scaled image many times.

  • Fix initial rotation of accordion carets.

  • Fix focus styling.

    • Godot inconsistently uses the keys “focused” and “focus”.

    • Also, Godot doesn’t report when you try to reference an invalid style key.

    • So I wasted a fair amount of time trying to figure out what was going wrong here!

  • Remove bg_color option for color_pulse, and just use modulate_color.

  • Create a placeholder for a new accordion-header class.

  • Miscellaneous

    • Move `display_resized` to DeviceUtils.

    • Rename `app_metadata` to `metadata`.

    • Add a work-around for a Godot bug incorrectly reporting a missing InputMap action.

What's next?

  • Do a tiny bit more styling and pixelation work in Scaffolder.
  • Resume work on a list of Surfacer improvements.

🎉 Cheers!

This is a simple icon representing my sabbatical.