Lua backed gtk widgets for quickly throwing together UIs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Sebastian 9450937f67 Added a stack widget and tested it in the keyboard demo 8 months ago
css Improved notifications for the quick constrols example 9 months ago
lua Added a stack widget and tested it in the keyboard demo 8 months ago
src Added a stack widget and tested it in the keyboard demo 8 months ago
tools Project custom_kb created! 11 months ago
.gitignore Renamed the executable in the build and run script 11 months ago
LICENSE Add LICENSE 11 months ago MARKDOWN!! (Fixed) 10 months ago
build Renamed the executable in the build and run script 11 months ago Renamed to Lunar Widgets 11 months ago qq_test now no longer reimplements a ddb client (use ddb_connect) 9 months ago Project custom_kb created! 11 months ago
run Added support for custom css files and other minor improvements 11 months ago
update_src_build_files Project custom_kb created! 11 months ago

Lunar Widgets

Basically lua binding for some gtk widgets but without all the gobject introspection stuff. This started out as a simple program to show custom keyboards (the test.lua example is a custom keyboard for newsboat) that could be useful on a touchscreen but is now going in the general direction of Eww (This project is far away from what Eww can do).

Currently this is only a proof of concept with an undocumented and unstable and incomplete API.

How to build?


  • A working vala compiler
  • glib-2.0
  • meson
  • lua 5.3 (I think)
  • gtk 3

If you wannt to use all the scripts (not just build and run) you also need lua 5.x installed

A guaranteed up to date list of dependencies can be found in the src/ file.

If you added or removed source files use the update_src_build_files script, it will atomatically update the src/ file so you don't have to do anything.

Building and running

To build it, run the script, which will automatically setup the build folder, run ninja, and put the output in the projects root directory. The produced binary should be executable, now.

To make development easy, the run script calls the script and then runs whatever is at the output if the build succeeds.

Using it

The build script should produce a single binary called lunar-widgets running it with the --help flag will give you up to date usage information.

You can add a scriptfile using the -s path/to/script.lua option, adding multiple scriptfiles is supported by simply addding more -s options.

You can also load an external css file using the -c custom.css option.


# This example should wor directly after running the build script
# It first loads a widget framework from lunar.lua, then the actual ui script from test_lunar.lua and applys the stylesheet test.css
./lunar-widgets -s src/lunar.lua -s src/test_lunar.lua -c src/test.css


Lunar-Widgets core part is its lua API (That where thwe name comes from), the methods that are avalilable bedides the standard lua library are:


The main part is the widget API, see the section about widgets to see what the values mean.

	-- create a widget of the specified type and id (and optionally add it to another widget)
	make_widget(string:type, string:id, [string:parent_id], [string:position]) bool
	-- remove a widget from the widget hirarchy
	remove_widget(string:id) bool
	-- unparents the widget, it will be no longer visible until you assign it to another parent widget
	unparent_widget(string:id) bool
	-- adds a widget to a parent widget in a given position (append to the start/end of list)
	assign_widget(string:id, string:parent_id, [string:position]) bool
	-- push a command and an optional argument to a widget and get an optional reply
	widget_command(string:id, string:command, [string:data]) string

Global Signals

Global Signals are just functions in the global lua namespace (simply write a lua funtion without a "local" prefix)

	--receive a signal from a widget with a signal name and an optional argument
	on_widget_signal(string:id, string:signal, string?:data)


The only special IO thingy in Lunar-Widgets right now is a global signal that triggers whenever there is a new line of input on stdin without having to worry about blocking the application:



Some miscallenious functions that may come in handy:

	-- generates a uuid
	uuid() string
	-- quits the application by properly calling the mainloop quit
	-- calls the specified function name (like a global signal) after the set number of seconds
	timeout(string:global_function_name, number:seconds)


Widgets are mostly a 1 to 1 mapping of Gtk widgets into lua, exposing the most important properties and functions.

How the widget API works

The widget api under the hood is a has table of gtk widgets with a few funktions you can use to poke around in those widgets.

Adding Widgets to other Widgets

Widgets can be added to other widgets by specifying the widet, the parent and a position. The position in this case is just a label for a single spot, like the content of a widow or the image widget for a button or a collection of spots, like the start of a list where the widgets that are already there slide down. To find out wich one is wich you can look at the widget documetation or try to find out how the underlying gtk widet works.

If a widget provides those slos it will be notated as follows:

	-- single widget slot
	slot <name>
	-- a slot wich can accept and show multiple widgets at once
	slots <name>

In case you wonder why the most common name for spots is nil, that is becuase that's what you get when you leave the spot name blank in lua.

Widget command and properties

Widget commands are a function name and an optional string argument, that may or may not return a boolean or string or nil, for most functions the function name is the name of a property, when you give a non nil argument it will set the the property if a nil or no argument is given it will act like a getter.

Widget commands will be documented like this:

	-- a command that acts like a getter and setter
	-- a property of a given type directly translates to the lua type (tostring)
	-- currently used types are: integer, bool
	property <name> [<type>]
	-- any old command
	command <name> <arg> <result>

Signals from Widgets

When something happens on the ui like a click on a button the widget the event belong to fires a signal, wich results in the widget signal function being called, using a callback table you can split those signal up into different functions in lua (or you use the currently experimental widget library)

Signals will be documented like this:

	signal <name> <arg>

Interfaces implemented by multiple widgets

There are some commands that are shared by multiple widgets, like alsmost everyone else we call them interfaces here:

Generic Widget commands (base_widget)

These commands can be implemented by almost all widgets.

	-- disabled widgets and their decendants are not interactive
	-- equivalent to setting the sensitive property for gtk widgets
	command enable
	command disable
	-- pretty self explaining
	command show
	command hide
	-- you can add style classes
	command add_style_class <class>
	command remove_style_class <class>
	-- you can also set cutom styling properties
	-- (although you shouldn't, there is support for external css files)
	command margin <number>
	command add_css <css>

Orientation commands (orientable)

	-- set to horizontal, vertical or h, v
	-- will return horizontal or vertical
	property orientation


Window (window)

Windows map directly to Gtk Windows, they are currently the ony widgets, that do not require parent widget to show up.

Implements: base-widget

	property label
	property accept_focus bool
	property keep_above bool
	property keep_below bool
	-- sticky windows stay on screen when you switch workspaces
	command stick
	command unstick
	-- experimental, may be removed:
	-- mark window as a dock
	command make_dock
	--Windows can accept exactly one widget
	spot nil

Boxes (box)

Boxes are containers that can contain multiple widgets.

Implements: base_widget, orientable

	spots start/nil
	spots end
	spot center
	-- wheter every widget gets exactly the same width/height
	property homogeneous bool


Labels just display some text to drumroll label things badumts. Buttons have builtin labels.

Implements: base_widget

	property label

Images (image)

Images can show image files and icons.

Implements: base_widget

	-- path to the image file
	property file
	-- fredesktop icon name
	-- You can use programs like the gtk-icon-browser or the yad-icon-browser to
	-- find out wich icons are available on your system (don't assume everyone has exactly the same icons)
	property icon
	-- You can set the icon size using this property
	-- if you set custom icon sizes, plese make them easy to configure
	-- (If you know of a way to do this with css please leve me a message (Mail, Issue))
	property size

Buttons (button)

Those clicky things, you can add css classes to make them look inviting or scary (see the base_widget interface, for gtk the classes are suggested-action and destructive-action).

Implements: base_widget

	property label
	signal clicked
	slot image/nil

Togglebuttons (toggle)

Similar to buttons but they stay where you put them.

Implements: base_widget

	property label
	property active bool
	property inconsistent bool
	signal toggled <active:bool>
	slot image/nil


There is an experimental and very hacky popover implementation, but it will be replaced by a wrapper box that has the popover already attached soon™ (read: When someone who is able to implement it needs it).

The luner.lua framework

The lunar.lua framework wraps the low level widget poking api in a nicer class system that is more convenient to use.

Basic Usage

Widgets are constructed using constructors that are passed a table of attributes (lua has some syntax sugar for that), most of those atributes translate directly into widget commands and properties using the key of the table as the command name and the value as the argument.

Available constructors are:

  • Window
  • Box
  • Label
  • Image
  • Button
  • ToggleButton

There are some special attributes:

  • parent: set this one to the parent widget object (not the id)
  • position: the position in the parent widget
  • class: adds one css class (uses the base_widget interface)
  • classes: adds an entire table of css classes (uses the base_widget interface)

Attributes that begin with an underscore '_' can freely be set and used to store custom values. Other attributes are either part of the Widget objectts or patched trough like on construction (minus the special ones).

The values that are part of the widget are: - the widgets own id, a random uuid
	Widget.parent_id - stores the parent id
	Widget.position - stores the position withing the parent
	Widget.type - the widget_type that was used on construction
	Widget.widget - always "yes"
	Widget.children - a table used for keeping track of widget children
	-- methods
	Widget:remove() - deconstruct this widget
	Widget:unparent() - removes the widget from the visual hirarchy while keeping it useable
	Widget:call(name, arg) - call a widget command of that widget
	Widget:add_widget(widget, position) - adds a new widget to this at &lt;position&gt;

Widgets will automatically be remove()d when their objects get garbage collected.

Every widget is also found in the global widgets table wich maps widget ids to waek references of the widgets. This is primarily used to deliver signals.

Special custom values

There are some special custom values that can be set to functions on the widget objects that make life easier.


If present it will be called with the signals argument when a signal with the given name arrives (a bit like like javascript onSomething() handlers, but without all the webjunk)


If set it gets called whenever something sets a property with the given name, only works on propertys not starting with an underscore.

This is used to implement an icon property for Buttons and Togglebuttons

the function signature looks like this:

	custom_setter(this, key, value)


like the _set_{property_name}; function but for reading, the returned value will be the result of the reading operation.

the function signature looks like this:

	custom_getter(this, key) -> value