tinygl

package module
v0.0.0-...-4289e0a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2024 License: BSD-3-Clause Imports: 5 Imported by: 9

README

Tiny graphics library

Note: work in progress. This module isn't ready for use yet.

Create fast graphics on slow SPI-connected displays.

This library is inspired by LVGL and Fyne and provides a way to create fast user interfaces on slow SPI connected displays.

Supported displays

Most dislays from the TinyGo drivers repository can be supported. The main requirement is that they have a way to send raw pixel data to the display (DrawRGBBitmap8).

Contributing

You are free to contribute, but note that the design isn't complete yet so more features may need to be delayed a bit until I think the design is right.

Code style

To make things fast and to reduce memory consumption, there are a few code style considerations apart from the usual Go conventions:

  • All internal colors are stored as the target pixel data. This is usually RGB565 big-endian.
  • Coordinates are of the int type in the public API and when doing integer math, but are stored as int16.
    Rationale: int16 is usually big enough for all coordinates. However, the main compilation target is 32-bit microcontrollers which are most efficient when working with 32-bit integers. Casting on load/store is usually free: ARM has a sign-extending 16-bit load instruction and stores are simply a truncating 16-bit store. Using int in the public API is also more convenient for users of the API.
  • No memory allocations should be needed while writing data to the display.
  • The main package (tinygl) does not care about any particular UI style like Material. Such styles should be implemented separately.
Release criteria

This library isn't complete. There are some things I'd like to improve before calling this stable:

  • The Displayer interface isn't great: DrawRGBBitmap8 always takes a byte slice, which is unfortunate. It would be better if it could take a slice of the underlying pixel data instead.
  • Black-and-white screens aren't well supported: they have pixel data smaller than a single byte. These screens aren't natively supported in LVGL either, but it would be nice if we were able to.
  • Displays with in-memory buffers could be better supported, by writing to the buffers directly. An example is the hub75 display driver.
  • Theming needs to be improved. Ideally, the theme and the layout code are entirely separate and the theme just sets the sizes/colors to be used for standard widgets.
  • DPI scaling isn't implemented yet. It should ideally be able to do all important calculations at compile time.
  • Lots of features are missing, like:
    • show/hide animations that look smooth (by only redrawing parts of the screen that changed)
    • using hardware scrolling present in most SPI displays
    • all the missing widgets and container types

License

BSD 2-clause license, see LICENSE.txt for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PaintSolidColor

func PaintSolidColor[T pixel.Color](s *Screen[T], color T, x, y, width, height int)

Internal function. Do not use directly except in custom widgets.

It paints the given area on screen with the given color.

Types

type Displayer

type Displayer[T pixel.Color] interface {
	Size() (int16, int16)
	DrawBitmap(x, y int16, bitmap pixel.Image[T]) error
	Display() error
	Rotation() drivers.Rotation
}

The Displayer that is drawn to.

type Event

type Event uint8
const (
	NoEvent Event = iota
	TouchStart
	TouchMove
	TouchEnd
	TouchTap
)

type EventBox

type EventBox[T pixel.Color] struct {
	Object[T]
	// contains filtered or unexported fields
}

EventBox wraps an object and handles events for it.

func NewEventBox

func NewEventBox[T pixel.Color](child Object[T]) *EventBox[T]

Create a new wrapper container that handles events.

func (*EventBox[T]) HandleEvent

func (b *EventBox[T]) HandleEvent(event Event, x, y int)

func (*EventBox[T]) SetEventHandler

func (b *EventBox[T]) SetEventHandler(handler func(event Event, x, y int))

SetEventHandler sets the event callback for this object.

type Image

type Image[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

func NewImage

func NewImage[T pixel.Color](img image.Image[T]) *Image[T]

NewImage creates a new image object that will display the given image. By default, the image is centered in the available space, with a black background.

func (*Image[T]) Layout

func (obj *Image[T]) Layout(width, height int)

Layout implements tinygl.Object.

func (*Image[T]) MinSize

func (obj *Image[T]) MinSize() (width, height int)

MinSize returns the minimal size of the image.

func (*Image[T]) SetBackground

func (obj *Image[T]) SetBackground(color T)

SetBackground sets the background color for the image.

func (*Image[T]) Update

func (obj *Image[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

Update implements tinygl.Object.

type ListBox

type ListBox[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

A scrollable list of strings, of which one is currently selected.

func NewListBox

func NewListBox[T pixel.Color](font font.Font, foreground, background, tint T, elements []string) *ListBox[T]

Create a new listbox with the given elements. The elements (and number of elements) cannot be changed after creation.

func (*ListBox[T]) HandleEvent

func (box *ListBox[T]) HandleEvent(event Event, x, y int)

HandleEvent handles events such as touch events and calls the event handler with the given child as a parameter.

func (*ListBox[T]) Layout

func (box *ListBox[T]) Layout(width, height int)

Layout implements tinygl.Object.

func (*ListBox[T]) Len

func (box *ListBox[T]) Len() int

Len returns the number of elements in the listbox.

func (*ListBox[T]) MarkUpdated

func (box *ListBox[T]) MarkUpdated()

func (*ListBox[T]) MinSize

func (box *ListBox[T]) MinSize() (width, height int)

MinSize returns the height of all the list items combined. The minimal width is always zero (it is expected to set to expand).

func (*ListBox[T]) RequestUpdate

func (box *ListBox[T]) RequestUpdate()

RequestUpdate will request an update for this object and all of its children.

func (*ListBox[T]) Select

func (box *ListBox[T]) Select(index int)

Select selects the given index. The index -1 means "no child selected".

func (*ListBox[T]) Selected

func (box *ListBox[T]) Selected() int

Selected returns the index of the currently selected element, or -1 if no element is selected.

func (*ListBox[T]) SetAlign

func (box *ListBox[T]) SetAlign(align TextAlign)

SetAlign sets the alignment of all text children.

func (*ListBox[T]) SetColumns

func (box *ListBox[T]) SetColumns(columns int)

SetColumns splits the view into multiple columns, so that elements can also display horizontally.

func (*ListBox[T]) SetEventHandler

func (box *ListBox[T]) SetEventHandler(eventHandler func(event Event, index int))

SetEventHandler sets the callback when one of the elements in the list gets selected.

func (*ListBox[T]) SetPadding

func (box *ListBox[T]) SetPadding(horizontal, vertical int)

Set padding for each text child.

func (*ListBox[T]) Update

func (box *ListBox[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

Update implements tinygl.Object.

type Object

type Object[T pixel.Color] interface {
	// Request an update to this object (that doesn't change the layout).
	RequestUpdate()

	// Request a re-layout of this object.
	RequestLayout()

	// Layout the object in the provided area. The object will take up all the
	// given area (if needed, by filling in the rest with its background color).
	Layout(width, height int)

	// Update the screen if needed. It recurses into children, if the object has
	// any. It does not change any state: this is done separately in
	// MarkUpdated.
	// The displayX, displayY, displayWidth, and displayHeight parameters are
	// the area of the display that needs to be updated. The width and height
	// must never be zero.
	// The x and y parameter are the offset from the start of the object, which
	// can be zero or positive. They can be positive when the parent is a scroll
	// container, for example.
	Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

	// MarkUpdated clears any 'needs update' flags that may be present on this
	// object, including its children if any.
	MarkUpdated()

	// Handle some event (usually, touch events).
	HandleEvent(event Event, x, y int)

	// Called when adding a child to a parent. Should only ever be called during
	// the construction of a container object.
	SetParent(object Object[T])

	// Minimal width and height of an object.
	MinSize() (width, height int)

	// SetGrowable sets the grow factor in the horizontal and vertical
	// direction.
	SetGrowable(horizontal, vertical int)

	// If possible, scroll the parent (or parent-of-parent, recursively) scroll
	// container so that the given area moves into view. The top line is the
	// topmost pixel that should become visible, while the bottom offset is one
	// pixel past the area that should become visible (so that bottom-top is the
	// height of the area to make visible).
	ScrollIntoViewVertical(top, bottom int, object Object[T])
	// contains filtered or unexported methods
}

type Rect

type Rect[T pixel.Color] struct {
	// contains filtered or unexported fields
}

func MakeRect

func MakeRect[T pixel.Color](background T) Rect[T]

MakeRect returns a new initialized Rect object. This is mostly useful to initialize an embedded Rect struct in a custom object.

func (*Rect[T]) Background

func (r *Rect[T]) Background() T

Background returns the current background for this object.

func (*Rect[T]) HandleEvent

func (r *Rect[T]) HandleEvent(event Event, x, y int)

HandleEvent handles input events like touch taps.

func (*Rect[T]) MarkUpdated

func (r *Rect[T]) MarkUpdated()

MarkUpdated clears the 'update' flags for this object.

func (*Rect[T]) NeedsLayout

func (r *Rect[T]) NeedsLayout() (needsLayout bool)

NeedsLayout returns whether this object needs to be re-layout, and clears the layout flag at the same time.

func (*Rect[T]) NeedsUpdate

func (r *Rect[T]) NeedsUpdate() (this, child bool)

NeedsUpdate returns whether this object and/or a child object needs an update, and clears the update flag at the same time.

func (*Rect[T]) Parent

func (r *Rect[T]) Parent() Object[T]

Parent returns the current parent element, if available.

func (*Rect[T]) RequestLayout

func (r *Rect[T]) RequestLayout()

func (*Rect[T]) RequestUpdate

func (r *Rect[T]) RequestUpdate()

func (*Rect[T]) ScrollIntoViewVertical

func (r *Rect[T]) ScrollIntoViewVertical(top, bottom int, child Object[T])

func (*Rect[T]) SetGrowable

func (r *Rect[T]) SetGrowable(horizontal, vertical int)

SetGrowable sets the grow factor in the horizontal and vertical direction. This means, for example, if one object has a factor of 1 and another of 2, the first object will get ⅓ and the second object will get ⅔ of the remaining space in the container after the minimal space has been given to all children.

func (*Rect[T]) SetParent

func (r *Rect[T]) SetParent(parent Object[T])

type Screen

type Screen[T pixel.Color] struct {
	// contains filtered or unexported fields
}

func NewScreen

func NewScreen[T pixel.Color](display Displayer[T], buffer pixel.Image[T], ppi int) *Screen[T]

NewScreen creates a new screen to fill the whole display. The buffer needs to be big enough to fill at least horizontal row of pixels, but should preferably be bigger (10% of the screen for example). The ppi parameter is the number of pixels per inch, which is important for touch events.

func (*Screen[T]) Buffer

func (s *Screen[T]) Buffer() pixel.Image[T]

Buffer returns the pixel buffer used for sending data to the screen. It can be used inside an Update call.

func (*Screen[T]) Layout

func (s *Screen[T]) Layout()

Layout determines the size for all objects in the screen. This is called from Update() so it normally doesn't need to be called manually, but it can sometimes be helpful to know the size of objects before doing further initialization, for example when drawing on a canvas.

func (*Screen[T]) Send

func (s *Screen[T]) Send(x, y int, buffer pixel.Image[T])

Send an image buffer to the given coordinates.

This function is used internally, and should only be used to implement custom widgets.

func (*Screen[T]) SetChild

func (s *Screen[T]) SetChild(child Object[T])

SetChild sets the root child. This will typically be a container of some sort.

func (*Screen[T]) SetTouchState

func (s *Screen[T]) SetTouchState(x, y int16)

Set the position of the current touch point, or (-1, -1) if nothing currently touching the screen.

TODO: handle multitouch. This will require an API change. In other words, this API is going to change at some point in the future.

func (*Screen[T]) Size

func (s *Screen[T]) Size() (width, height int)

Size returns the size of the screen in pixels.

func (*Screen[T]) Update

func (s *Screen[T]) Update() error

Update sends all changes in the screen to the (hardware) display.

type ScrollBox

type ScrollBox[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

ScrollBox is a scrollable wrapper of an object. It is growable and reports a minimal size of (0, 0) to the parent, so that it will take up the remaining space in the parent box.

func NewScrollBox

func NewScrollBox[T pixel.Color](child Object[T]) *ScrollBox[T]

NewScrollBox returns an initialized scroll box with the given child. If you want to scroll multiple children (vertically, for example), you can use a VBox instead.

func (*ScrollBox[T]) HandleEvent

func (b *ScrollBox[T]) HandleEvent(event Event, x, y int)

func (*ScrollBox[T]) Layout

func (b *ScrollBox[T]) Layout(width, height int)

func (*ScrollBox[T]) MarkUpdated

func (b *ScrollBox[T]) MarkUpdated()

func (*ScrollBox[T]) MinSize

func (b *ScrollBox[T]) MinSize() (width, height int)

func (*ScrollBox[T]) RequestUpdate

func (b *ScrollBox[T]) RequestUpdate()

func (*ScrollBox[T]) ScrollIntoViewVertical

func (b *ScrollBox[T]) ScrollIntoViewVertical(top, bottom int, child Object[T])

func (*ScrollBox[T]) SetGrowable

func (b *ScrollBox[T]) SetGrowable(horizontal, vertical int)

func (*ScrollBox[T]) Update

func (b *ScrollBox[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

type ScrollableDisplay

type ScrollableDisplay[T pixel.Color] interface {
	Displayer[T]
	SetScrollArea(topFixedArea, bottomFixedArea int16)
	SetScroll(line int16)
	StopScroll()
}

ScrollableDisplay is a display that supports hardware scrolling. A display doesn't have to support it, but if it does, scrolling can become a lot more smooth.

type Text

type Text[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

func MakeText

func MakeText[T pixel.Color](font font.Font, foreground, background T, text string) Text[T]

MakeText returns a new initialized Rect object. This is mostly useful to initialize an embedded Text struct in a custom object. If you want a standalone text object, use NewText.

func NewText

func NewText[T pixel.Color](font font.Font, foreground, background T, text string) *Text[T]

func (*Text[T]) Layout

func (t *Text[T]) Layout(width, height int)

func (*Text[T]) MinSize

func (t *Text[T]) MinSize() (width, height int)

MinSize returns the minimal size of the text label.

func (*Text[T]) SetAlign

func (t *Text[T]) SetAlign(align TextAlign)

func (*Text[T]) SetBackground

func (t *Text[T]) SetBackground(background T)

SetBackground changes the background color of the text.

func (*Text[T]) SetColor

func (t *Text[T]) SetColor(color T)

SetColor sets the text color.

func (*Text[T]) SetPadding

func (t *Text[T]) SetPadding(horizontal, vertical int)

Set horizontal and vertical padding in screen pixels. The padding must be a positive integer that is less than 128.

func (*Text[T]) SetText

func (t *Text[T]) SetText(text string)

SetText changes the text for this text label.

func (*Text[T]) Update

func (t *Text[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

type TextAlign

type TextAlign uint8
const (
	AlignCenter TextAlign = iota
	AlignLeft
)

type VBox

type VBox[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

func NewVBox

func NewVBox[T pixel.Color](background T, children ...Object[T]) *VBox[T]

func (*VBox[T]) HandleEvent

func (b *VBox[T]) HandleEvent(event Event, x, y int)

HandleEvent propagates an event to its children.

func (*VBox[T]) Layout

func (b *VBox[T]) Layout(width, height int)

func (*VBox[T]) MarkUpdated

func (b *VBox[T]) MarkUpdated()

func (*VBox[T]) MinSize

func (b *VBox[T]) MinSize() (width, height int)

MinSize returns the minimal size to fit around all the elements in this container.

func (*VBox[T]) RequestUpdate

func (b *VBox[T]) RequestUpdate()

RequestUpdate will request an update for this object and all of its children.

func (*VBox[T]) Update

func (b *VBox[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

type VerticalScrollBox

type VerticalScrollBox[T pixel.Color] struct {
	Rect[T]
	// contains filtered or unexported fields
}

VerticalScrollBox is a scrollable wrapper of an object that will use hardware acceleration when available. It is growable and reports a minimal size of (0, 0) to the parent, so that it will take up the remaining space in the parent box.

func NewVerticalScrollBox

func NewVerticalScrollBox[T pixel.Color](top, child, bottom Object[T]) *VerticalScrollBox[T]

NewVerticalScrollBox returns an initialized scroll box with the given child. If you want to scroll multiple children (vertically, for example), you can use a VBox to wrap these children.

func (*VerticalScrollBox[T]) HandleEvent

func (b *VerticalScrollBox[T]) HandleEvent(event Event, x, y int)

func (*VerticalScrollBox[T]) Layout

func (b *VerticalScrollBox[T]) Layout(width, height int)

func (*VerticalScrollBox[T]) MarkUpdated

func (b *VerticalScrollBox[T]) MarkUpdated()

func (*VerticalScrollBox[T]) MinSize

func (b *VerticalScrollBox[T]) MinSize() (width, height int)

func (*VerticalScrollBox[T]) RequestUpdate

func (b *VerticalScrollBox[T]) RequestUpdate()

func (*VerticalScrollBox[T]) ScrollIntoViewVertical

func (b *VerticalScrollBox[T]) ScrollIntoViewVertical(top, bottom int, child Object[T])

func (*VerticalScrollBox[T]) SetGrowable

func (b *VerticalScrollBox[T]) SetGrowable(horizontal, vertical int)

func (*VerticalScrollBox[T]) Update

func (b *VerticalScrollBox[T]) Update(screen *Screen[T], displayX, displayY, displayWidth, displayHeight, x, y int)

Directories

Path Synopsis
Package font provides simple fonts for use on embedded devices.
Package font provides simple fonts for use on embedded devices.
Package image wraps read-only image buffers and allows drawing them on pixel buffers.
Package image wraps read-only image buffers and allows drawing them on pixel buffers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL