Furex is a minimal GUI framework built on top of Ebitengine, a 2D game engine for Go. It provides support for the Flex Layout Algorithm, a powerful tool for laying out items of different sizes and dimensions. Furex is not a component library, but rather a framework for positioning and stacking virtual widgets, handling button and touch events for mobile, and more. How these widgets are rendered is up to the user.
Assets by Kenney. Fonts by 00ff.
Full source code of the example is here.
- Contents
- Motivation
- Features
- Getting Started
- Basic Usage
- Building UI with HTML
- Debugging
- Contributions
Flexbox is a popular layout mechanism in web development, used for creating responsive and flexible user interfaces. With Furex, we can bring this same concept to game development using Go and Ebitengine. This library aims to make it easier for developers with experience in web or React projects to create dynamic, user-friendly game UI.
If you are not familiar with Flexbox Layout, you can learn about it at this website.
Here are some of the key features of Furex:
-
Flexbox layout: The UI layout can be configured using the properties of View instances, which can be thought of as equivalent to
DIV
elements in HTML. These views can be stacked or nested to create complex layouts. -
Custom widgets:
View
instances can receive aHandler
which is responsible for drawing and updating the view. This allows users to create any type of UI component by implementing the appropriate handler interfaces, such as Drawer, Updater, and more. -
Button support: To create a button, users can implement the ButtonHandler interface. This supports both touch and mouse input for button actions. See the Example Button for more details.
-
Touch and mouse events: Furex provides support for handling touch events and positions using the TouchHandler interface, and mouse click events using the MouseLeftButtonHandler interface. It also offers support for detecting mouse position events using the MouseHandler interface, and mouse enter/leave events using the MouseEnterLeaveHandler interface.
-
Swipe gestures: Users can detect swipe gestures by implementing the SwipeHandler interface.
These are just a few examples of the capabilities of Furex. For more information, be sure to check out the GoDoc documentation.
To get started with Furex, install Furex using the following command:
go get github.com/yohamta/furex/v2
Here's a simple example of how to use Furex to create an UI in your game:
Full source code of the example
type Game struct {
initOnce sync.Once
screen screen
gameUI *furex.View
}
func (g *Game) Update() error {
g.initOnce.Do(func() {
g.setupUI()
})
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
g.gameUI.Draw(screen)
}
func (g *Game) setupUI() {
screen.Fill(color.RGBA{0x3d, 0x55, 0x0c, 0xff})
colors := []color.Color{
color.RGBA{0x3d, 0x55, 0x0c, 0xff},
color.RGBA{0x81, 0xb6, 0x22, 0xff},
color.RGBA{0xec, 0xf8, 0x7f, 0xff},
}
g.gameUI = &furex.View{
Width: g.screen.Width,
Height: g.screen.Height,
Direction: furex.Row,
Justify: furex.JustifyCenter,
AlignItems: furex.AlignItemCenter,
AlignContent: furex.AlignContentCenter,
Wrap: furex.Wrap,
}
for i := 0; i < 20; i++ {
g.gameUI.AddChild(&furex.View{
Width: 100,
Height: 100,
Handler: &Box{
Color: colors[i%len(colors)],
},
})
}
}
type Box struct {
Color color.Color
}
var _ furex.Drawer = (*Box)(nil)
func (b *Box) Draw(screen *ebiten.Image, frame image.Rectangle, view *furex.View) {
graphic.FillRect(screen, &graphic.FillRectOpts{
Rect: frame, Color: b.Color,
})
}
Sometimes making a complex UI tree in Go can be cumbersome. You can use HTML to construct the UI tree more easily.
Here's how to create a view tree from HTML:
-
ui.html
<html> <head> <style> container { align-items: center; justify-content: center; } .sprite { width: 64px; height: 64px; } </style> </head> <body> <container> <character class="sprite"></character> </container> </body> </html>
-
ui.go
//go:embed assets/html/ui.html var html string view := furex.Parse(html, &furex.ParseOptions{ Width: 480, Height: 600, Components: map[string]furex.Component{ "character": &widgets.Sprite{SpriteID: "mario.png"}, }, })
Note: The
<body>
tag itself won't be converted as aView
, but its children will be converted toView
instances.
This example is equivalent to the following Go code. By using HTML for styling the UI, the code becomes more maintainable.
view := (&furex.View{
Width: 480,
Height: 600,
AlignItems: furex.AlignItemsCenter,
Justify: furex.JustifyCenter,
}).AddChild(
&furex.View{
Width: 64,
Height: 64,
Handler: &widgets.Sprite{SpriteID: "mario.png"},
},
)
For a more extensive example, check out the example here and the embedded HTML file.
The following table lists the available CSS properties:
CSS Property | Type | Available Values |
---|---|---|
left |
int | Any integer value |
right |
int | Any integer value |
top |
int | Any integer value |
bottom |
int | Any integer value |
width |
int | Any integer value or percentage |
height |
int | Any integer value or percentage |
margin-left |
int | Any integer value |
margin-top |
int | Any integer value |
margin-right |
int | Any integer value |
margin-bottom |
int | Any integer value |
position |
Position | static , absolute |
flex-direction |
Direction | row , column |
flex-wrap |
FlexWrap | no-wrap , wrap , wrap-reverse |
justify-content |
Justify | flex-start , flex-end , center , space-between , space-around |
align-items |
AlignItem | stretch , flex-start , flex-end , center |
align-content |
AlignContent | flex-start , flex-end , center , space-between , space-around , stretch |
flex-grow |
float64 | Any float64 value |
flex-shrink |
float64 | Any float64 value |
display |
Display | flex , none |
The following table lists the available HTML attributes:
HTML Attribute | Type | Available Values |
---|---|---|
id |
string | Any string value |
hidden |
bool | true , false |
There are three types of components you can create in Furex:
- Handler Instance: A
furex.Handler
instance, such asDrawer
orUpdater
. - Factory Function: A function that returns a
furex.Handler
instance. This is useful when you want to create separate handler instances for each HTML tag. - Function Component: A function that returns a
*furex.View
instance. This is an alternative way to create components that encapsulate their own behavior and styles.
To register a custom component globally, use the furex.RegisterComponents function. For example:
func init() {
furex.RegisterComponents(furex.ComponentsMap{
"button": &widgets.Button{},
"sprite": &widgets.Sprite{},
})
}
You can enable Debug Mode by setting the variable below.
furex.Debug = true
Contributions are welcome! If you find a bug or have an idea for a new feature, feel free to open an issue or submit a pull request.