wintermute-cell / cowboy-gorl

0 stars 0 forks source link

Create GUI package #11

Closed wintermute-cell closed 1 year ago

wintermute-cell commented 1 year ago

Problem Description

Currently, the way GUI is rendered is inconcise and error prone. Since due to the pixelart style, text has to be rendered pixel-perfect, and it is currently too easy to wrongly place a text element, making it render blurry.

Solution Approach

Implement a GUI package, that provides an API for using a consistent, error resistant custom GUI, built on top of raylib/raygui. The package should allow for custom gui elements, and should ensure a consistent style.

wintermute-cell commented 1 year ago

I think for most GUI elements (text, button, slider, checkbox, ...) the code can be split into two distinct steps:

This clear separation allows a styling process like this: Handle interaction logic and determine element state -> select the used style based on state -> draw using selected style

wintermute-cell commented 1 year ago

Ideas on styling methods

The first idea that came to mind was a CSS/HTML like styling approach, and while that is useful for creating a variety of generic styles, it is hard to create something really unique that way.

Another approach would be using an entirely hand drawn approach, where GUI elements are composed of hand drawn sprites. It would be better for creating more complex pixelart GUI elements, although more time consuming.

Regarding what the gui package should implement: I think that the best way to implement styling in the gui package is to not implement it at all. Or at least not do a fixed implementation. Since the drawing process of a GUI element can be isolated into its own step, based on element state, one could allow the caller to provide their own drawing function, and use a fallback drawing implementation if none is provided by the caller.

Theoretical example:

func Button( text string, draw_button func(state_type) ) {
    state = ProcessButtonLogic()
    if draw_button == nil {
        draw_button_builtin(state)
    } else {
        draw_button(state)
    }
}

func draw_button_builtin(state_type) could then use a generic DrawRectangle() approach, maybe styling based on colors the user can customize.

This would allow for very deep customization and still provide quick and easy usage without a lot of customization.

wintermute-cell commented 1 year ago

Progress has been made and a specific approach has been chosen. The GUI is implemented as a retained mode recursive widget tree, with swappable rendering backend and a flexible communication spec between logical gui and rendering step. Read the following for more info:

https://github.com/wintermute-cell/cowboy-gorl/blob/gui/documentation/gui.md https://github.com/wintermute-cell/cowboy-gorl/blob/gui/documentation/gui-styledef.md

wintermute-cell commented 1 year ago

For the implementation, see: https://github.com/wintermute-cell/cowboy-gorl/tree/gui/pkg/gui

Usage example, from scene_gui_dev.go:

scn.g = gui.NewGui()

// gui elements
label := gui.NewLabel("retained mode widget", rl.NewVector2(16, 64), "font:alagard|font-scale:2.0")
btn_callback := func (s gui.ButtonState) {
    logging.Info("%v", s)
}
btn := gui.NewButton("retained button", rl.NewVector2(16, 96), rl.NewVector2(15*6, 16), btn_callback, "background-pressed:180,10,10,255")

scroll_panel := gui.NewScrollPanel(
    rl.NewRectangle(10, 48, 620, 400),
    rl.NewRectangle(10, 48, 620, 2000),
    "debug:false|background:200,200,200,255")

scn.g.AddWidget(scroll_panel)
scroll_panel.AddChild(label)
scroll_panel.AddChild(btn)
wintermute-cell commented 1 year ago

TODO Now