I've been working on a new experimental GUI. It's not ready for release yet; however, this last weekend I made good progress on events and wanted to share the progress! It's been coming together in a pleasing way so far.
You can see an example video here: https://www.youtube.com/watch?v=NsZyzbzHVMc
It builds on the event system in Fidget and Fidgetty, but with some important changes:
These pieces should make it much easier to work with events than in Fidgetty. In particular, combining events using signals is pretty nice. It's starting to approach the usability of a traditional GUI while retaining the "declarative" feel of Fidget or SwiftUI for drawing. I'm hoping it can keep coming together like that.
I started my long weekend by trying to write a simple "clickable" demo. Little did I suspect how much work it'd turn out to be! It was mostly fun and satisfying, but frustrating at points as well.
Trying to create that basic "clickable" demo turned up a number of issues both from the Fidgetty/Fidget days but some new ones unique to Figuro. For example, I had to re-factor current and parent which were globals previously so they could work properly with callbacks and partial re-draws. Phew, that was a pain to get right.
This big refactor removes a lot of blockers from what I want Figuro to be able to do. That means it's getting closer to being ready for a beta release!
This is really useful as signals and slots are key to how Figuro does user events and interactions between widgets. It's also very simple to get the type of any signal/slots by invoking Widget.mySignal().typeof(). You can get the RPC proc using AgentProc.mySignal(Widget).
Overall, this piece has been hard to get implemented right, but I'm fairly happy with it now. Generics will need more work, but they work for simple cases.
Mouse events were largely inherited from Fidgetty and Fidget. I had previously upgraded them to only give a single event per action, but they still had issues.
Now they've been largely re-written to better handle events that involve multiple nodes like "click-out" and "hover" and to not get captured by child nodes. The rewrite also resolves some race conditions since the original design wasn't multi-thread aware.
Importantly, mouse events can now be used from either immediate mode style API's like onClick or by using signals and slots. Also, clicks now default to activating on button-release, but support on button-pressed as well. Thanks to @enthus1ast (spelling?) for pointing this out.
Events still need a bit of work before a beta release. I've really only plumbed in onHover and onClick. Keyboard events will be similar to mouse events, but aren't wired in yet.
This was the first bit I ended up re-writing. For various reasons I went back to using a closure for widget callbacks. The callbacks are the code blocks inside a widget "template" which allow the programmer to modify a widget's look or behavior, like this:
for idx in 0..5:
button void, "btn", idx: ## capture `idx`
# this section is transformed into a callback
box 10 + idx * 120, 10, 100, 100
let count = Button[int](current.parent).state ## access widget state; this needs simplified
setText(font, $(count))
Previously, trying to use idx would have resulted in a compiler error. Folks who tried to use Fidgetty found this difficult, and it's tricky to explain.
Variables can now be "captured" (basically copied) into the closure scope. This makes it possible to properly do partial drawing updates as well!
There's still a fair bit to do, but it's getting smaller: theming, layers, keyboard inputs, create basic widgets. Layers are important to be able to make good menus and popups. I have an idea for theming too.
There's some race conditions with using Pixie across multiple threads - easish to fix but it crashes every so often for now. Also, things are still likely to break or change but you could do simple demos already.
How does the hello world-code look like?
Are there allready controls like a combobox?
The first widget that needs your attention is a text editor widget that can do syntax highlighting. ;-)
Feel free to copy as much code as you think is good from my abandoned NimEdit project.
Are there allready controls like a combobox?
Not yet, I'm still working on the core. It'll be fairly easy to make them once the core is ready.
How does the hello world-code look like?
It's very similar to Fidget/Fidgetty for the basics:
https://github.com/elcritch/figuro/blob/main/tests/twidget.nim
The first widget that needs your attention is a text editor widget that can do syntax highlighting. ;-)
haha, that's like the hardest widget to do. Though I think @ElegantBeef made one for Nico? :)
Feel free to copy as much code as you think is good from my abandoned NimEdit project.
If I get to that point, sure.
> Feel free to copy as much code as you think is good from my abandoned NimEdit project.
If I get to that point, sure.
I second, third, and fourth @Araq 's point 👍
Finally got layering in place!
It's one of the last bits before I started trying to make dropdowns, menus, and other widgets. Here's a video description: https://www.youtube.com/watch?v=-Hhea3MDjuk
Any updates with Figuro that can be shared?
Yep there's been some steady progress! Though not much that makes good screenshots. ;)
Things are clearly into the 20% of the 80/20 rule. It's still in alpha stage as some of the core api's are being tweaked, but it's largely bug squashing or making things more robust. I've been able to resolve a number of limitations that Fidgetty faced due to the new architecture.
On another note, @elegantbeef made a simple slider widget that should support any type like enums, arrays, ranges, etc. It helped surface some missing APIs. Others are welcome to work on widgets too, but note that at this stage there isn't documentation or even APIs needed to do many things. You'll likely need to read Figuro source or bug me on Discord in the appdev channel.
Here's a list of some things since my last update:
Wow, that list was longer than I thought it'd be. It has been a couple of weeks.
Here's an example of a bind style API I was toying with yesterday:
type
Main* = ref object of Figuro
value: int
counter = Property[int]()
proc draw*(self: Main) {.slot.} =
withDraw(self):
self.name.setLen(0)
self.name.add "main"
fill "#9F2B00"
size 100'pp, 100'pp
rectangle "count":
cornerRadius 10.0
box 40'ux, 30'ux, 80'ux, 40'ux
fill "#8F2B8B"
node nkText, "btnText":
box 40'pp, 10'ux, 80'pp, 80'pp
fill blackColor
bindProp(self.counter) # will redraw this text whenever counter changes
setText({font: $self.counter.value})
button "btnAdd":
box 160'ux, 30'ux, 80'ux, 40'ux
fill "#9F2B00"
node nkText, "btnText":
box 40'pp, 10'ux, 80'pp, 80'pp
fill blackColor
setText({largeFont: "+"})
## something like this:
onEvent(doClick, self.counter) do(counter: Property[int]):
counter.update(counter.value+1)
button "btnSub":
box 240'ux, 30'ux, 80'ux, 40'ux
fill "#9F2B00"
node nkText, "btnText":
box 40'pp, 10'ux, 80'pp, 80'pp
fill blackColor
setText({largeFont: "–"})
onEvent(doClick, self.counter) do(counter: Property[int]):
counter.update(counter.value-1)
I just merged another fairly big update for Figuro and CSSGrid which fixes a number of important pieces: https://github.com/elcritch/figuro/pull/41
TLDR: previously the layout system defaults were difficult to use and it was broken for auto-flow layouts like vertical-layouts. The signal / slot connect was tricky to use for actual widgets due to some missing helpers and typing issues.