Hello!
I just released Owlkettle 2.0.0. Owlkettle is a declarative GUI framework based on GTK 4. It brings the declarative GUI paradigm you know from many web frameworks to the Linux desktop.
Here is an example for a simple counter app. When you click on the "+" Button, the counter increments.
import owlkettle
viewable App:
counter: int
method view(app: AppState): Widget =
result = gui:
Window:
title = "Counter"
default_size = (200, 60)
Box(orient = OrientX, margin = 12, spacing = 6):
Label(text = $app.counter)
Button {.expand: false.}:
text = "+"
style = {ButtonSuggested}
proc clicked() =
app.counter += 1
brew(gui(App()))
This results in the following application:
Owlkettle 2.0.0 now also supports libadwaita apps. By importing owlkettle/adw and changing the last line of the example to adw.brew(gui(App())), the application uses the libadwaita theme.
Owlkettle requires GTK 4 to be installed on your system. You can install it by running dnf install gtk4-devel libadwaita-devel on Fedora or apt install libgtk-4-dev libadwaita-1-dev on Ubuntu.
$ nimble install owlkettle
Documentation for Owlkettle can be found on GitHub: https://github.com/can-lehmann/owlkettle#documentation
Also I really am enjoying the feel of using the = instead of a more YAML like : pattern I'm using for attributes in Fidgetty. It just feels slightly more "open". Maybe I'll switch to that DSL call style. :-)
Button {.expand: false.}:
text = "+"
style = {ButtonSuggested}
proc clicked() =
app.counter += 1
vs the more YAML-like DSL style:
Button(expand = false):
text: "+"
style: {ButtonSuggested}
onClick:
app.counter += 1
This is great! It runs nicely on MacOS too.
Thank you! It is also good to know that it (unintentionally) supports MacOS. Does GTK use the system style of just the default stylesheet?
vs the more YAML-like DSL style:
How do you differentiate between attributes and widgets currently? Both should result in very similar ASTs, shouldn't they?
One thing that seems to work, although I never needed it is:
Button[expand = false]:
sort of like generic arguments.
Button[expand = false]:
This certainly looks better than a pragma, it would however prevent me from supporting generic widgets in the future. Especially since the syntax also needs to support choosing an "adder" (a proc which adds the widget as a child to the parent), so HeaderBar[add_titlebar] would also have to be valid syntax.
Button(expand = false):
Looks better, but would break backwards compatibility, since widget constructors such as Label(text = ...) are currently equivalent to Label: text = "...". However expand is not an attribute of Button (and it should not be, since expand only makes sense when a button is added to a Box). One option might be to add a second argument list like Button()(expand = false): ..., but I do not think that this looks particularly great.
Here is an idea, which would support adders, but abuses Nim's syntax a bit.
Box:
Button as (expand = false):
icon = "..."
...
Window:
HeaderBar as add_titlebar:
...
Maybe the add_ could also be either (a) automatically inferred or (b) just dropped from the procedure names, so the second example becomes: HeaderBar as titlebar.
However expand is not an attribute of Button (and it should not be[...]
add properties to buttons etc.
button:
prop:
expand =
shrink =
attr:
text =
proc =
the second awnser in: https://stackoverflow.com/questions/258469/what-is-the-difference-between-attribute-and-property
Anyway you can document the dialogs and properties?
I didn't know that you can't add two boxes and if you want to (At least from what I see) is you have to use properties for instance the customer_dialog.nim file.
Love the framework so far , having lots of fun. Thanks again.
The Property widget in custom_dialog.nim is a simple viewable widget which is just an abstraction over the existing widgets. It is defined here and expands to a Box containing a Label.
You should be able to use a custom dialog without the Property widget like this:
Dialog:
Box:
orient = OrientY
Box:
orient = OrientX
Label(text = "Label 1")
Box:
orient = OrientX
Label(text = "Label 2")
Looks great!
I really like that you could write something like
Box:
Label(@expand = false):
text = "Hello"
without breaking backwards compatibility. I will look into implementing something like this in Owlkettle.
A new release was published! Check it out on GitHub for the full changelog. Here is a summary of the changes:
If you have not used owlkettle before, you may also be interested in this introduction video I produced some time ago.
The change to square brackets for the style field was necessary in order to support custom CSS classes. This change is currently only available in the head version of owlkettle (See this commit).
I currently recommend installing the head version of owlkettle (which will eventually become owlkettle 3.0), since it is up to date with the online documentation and examples:
nimble install owlkettle@#head
In case you install the head version, but still get a compilation error, nimble is probably installing the wrong version of owlkettle as @dlesnoff pointed out. See here for how to fix this issue.