Worked on refining the prototype. Had to write my own tab container to handle drag & drop consistently the way I wanted it to work.
Also added proper styling ๐
Worked on refining the prototype. Had to write my own tab container to handle drag & drop consistently the way I wanted it to work.
Also added proper styling ๐
Today I worked on an in-app property inspector. It uses an object derived from Resource as the source of the inspection.
It currently supports string, folder and file selection.
Property changes are being synced between the UI and object.
Todo: add export-categories as sections, int, float, vectors, arrays and sub-resources
The SpinBox-Control that you can use for integer and float value editing has a function that lets you set the step like "0.1". When you use the mouse-wheel it increases or decreases by the step.
So you would expect 1.0 -> 1.1, 1.2 etc.
But the real values are something like 1.0 -> 1.100002322, 1.2000005432 etc.
Custom rounding function to the rescue:
func round_to_decimal(num:float, digit:int) -> float:
return round(num * pow(10.0, digit)) / pow(10.0, digit)
Added array and dictionary support for the in-app inspector.
Todo 1: add,remove of elements in an array or dictionary. Build an add dialog for the element type selection.
Todo 2: Add support for nested Resource objects for inspection.
NodePath is very inconsistent in Godot. Accessing and setting a dictionary through get_indexed() or set_indexed() behaves differently based on the type of the key of the dict.
If the key is a String "PathToDictionary:StringKey" works. If the key is an int for example it does not work. It then behaves like an Array which you also cannot address like "PathToArray:Index"
[Update]: Opened an issue [1] to see if this is intended behavior

Tested versions Godot 4.7 dev1 System information Godot v4.7.dev1 - Linux Mint 22.3 (Zena) on X11 - X11 display driver, Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4...
Added "add" and "remove" element for arrays and dictionaries. Had to write my own get_indexed and set_indexed functions as the vanilla ones behave inconsistently on array and dicts.
I also added a scroll container. And I had to use a tween (with 0 seconds tween time) to set the vertical_scroll position. Setting it directly or via set_deferred did not work consistently or sometimes at all.
Collections are now foldable container that remember their state.
Building this tool was the right decision. I learn so much about the UI implemention in Godot. The good and the bad.
For example the SpinBox (for ints with the arrow up and down) is missing the font_size theme property. Meaning you cannot set the font-size with an override.
It consists of the SpinBox container and a LineEdit child. The child doesn't inherit the Theme_Style_Variant but uses the normal LineEdit Theme.
after new() the child also doesn't exist yet.
Found the solution.
The SpinBox has a get_line_edit() function that returns the line edit control.
There you can apply the add_theme_font_size_override("font_size", font_size) override.
Why is the LineEdit not a child of the SpinBox? I will probably never know. Or perhaps someone from the fediverse knows?
Put all the learnings together and added mouse wheel zoom and export_categories.
Next: remove for arrays (dicts already work). File and Dir dialog for dictionaries (the hint_string format is a bit different than for arrays).
backlog: Add vector and color edit.
After that I will work on the next feature of the tool.
The inspector is fun and I got a bit sidetracked ๐
Today I extended the inspector to handle vectors and range type hints.
Added a global after_scene_tree_ready handler that fires after the GUI is ready.
Useful for things like loading the last open project.
Added a global timer that ticks every second and processes a list of callbacks. Useful for things like save the settings x seconds after the last change. Or autosave the project every 5 minutes.
Enabled the "Low Processor Mode" for the godot project.
Just one more export type ... added export_flags and export_enum and made the icons for files and folders smaller.
The app now recognizes the preferred user theme (if the system is capable of providing that info) and you can select the behavior in the app settings.
And I added a light theme (wip).
Earlier Today I thought objects were not freed properly when closing tabs as I could see the object count and mem usage rising steadily.
But then I remembered that I am using a RichTextLabel for debug console output. This creates a lot of objects to show fancy colorful output.
Ran a lilttle automated test of creating an inspector, adding it, freeing it and clearing the rich text label.
Everything looks good. No leak!
The memory/object test is kind of relaxing to watch ๐
Added the first iteration of the library explorer. It uses the library paths defined in the project settings.
Building the file and folder cache for roughly 38.000 folders and 500.000 files takes around 2 seconds.
After that everything runs against the cache.
The file manager and cache manager are threaded. I like how clean the threading implementation is in gdscript.
Added a file viewer component that reacts to the item selected in the explorer component.
next: analyse the file and see if it is an atlas texture. Then guess rows and columns to animate the file in the viewer.
The tool can now parse different metadata sources to determine the frame-information for a given image file.
Currently implemented are #aseprite json, list as txt and cols & rows encoded in the filename. Adding more when I come across them in my #pixelart library.
The tool uses the extracted frames to animate the file when you select it in the explorer panel.
next: display the animations in a grid if the tool detects multiple layers and/or rows.
Added a new Atlas Info inspector to the right panel. Showing the extracted meta information like columns, rows, layers and frames.
My custom tab container can now also be collapsed and expanded.
next: make a condensed tree view where I show single file folders one layer up to reduce the amount of clicking.
backlog: Then add a gallery to show animated sprites in a grid.
If there are no meta information available I am going to guess the frames by extracting polygons.
Using these bitmap functions you can convert an image alpha to a bitmap alpha mask. The bitmap can create polygons based on the transparent boundaries.
Bitmap.create_from_image_alpha
Bitmap.opaque_to_polygons
Frame guessing for sprite sheets without meta information is working!
And the debug visualization looks awesome! ๐
(When there are no json or txt files that provid meta information I try to guess it using the bitmap class through alpha mask and polygon extraction.)