254 lines
5.9 KiB
Go
254 lines
5.9 KiB
Go
package allg5ui
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
|
|
"opslag.de/schobers/geom"
|
|
|
|
"opslag.de/schobers/zntg/allg5"
|
|
"opslag.de/schobers/zntg/ui"
|
|
)
|
|
|
|
var _ ui.Renderer = &Renderer{}
|
|
|
|
func NewRenderer(w, h int, opts allg5.NewDisplayOptions) (*Renderer, error) {
|
|
var err = allg5.Init(allg5.InitAll)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
disp, err := allg5.NewDisplay(w, h, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
eq, err := allg5.NewEventQueue()
|
|
if err != nil {
|
|
disp.Destroy()
|
|
return nil, err
|
|
}
|
|
|
|
user := allg5.NewUserEventSource()
|
|
eq.RegisterKeyboard()
|
|
eq.RegisterMouse()
|
|
eq.RegisterDisplay(disp)
|
|
eq.RegisterUserEvents(user)
|
|
|
|
return &Renderer{disp, eq, map[string]*font{}, user, ui.MouseCursorDefault, ui.MouseCursorDefault}, nil
|
|
}
|
|
|
|
// Renderer implements ui.Renderer using Allegro 5.
|
|
type Renderer struct {
|
|
disp *allg5.Display
|
|
eq *allg5.EventQueue
|
|
ft map[string]*font
|
|
user *allg5.UserEventSource
|
|
cursor ui.MouseCursor
|
|
newCursor ui.MouseCursor
|
|
}
|
|
|
|
// Renderer implementation (events)
|
|
|
|
func (r *Renderer) PushEvents(t ui.EventTarget, wait bool) {
|
|
r.disp.Flip()
|
|
|
|
r.newCursor = ui.MouseCursorDefault
|
|
var ev = eventWait(r.eq, wait)
|
|
for nil != ev {
|
|
switch e := ev.(type) {
|
|
case *allg5.DisplayCloseEvent:
|
|
t.Handle(&ui.DisplayCloseEvent{EventBase: eventBase(e)})
|
|
case *allg5.DisplayResizeEvent:
|
|
t.Handle(&ui.DisplayResizeEvent{EventBase: eventBase(e), Bounds: geom.RectF32(float32(e.X), float32(e.Y), float32(e.X+e.Width), float32(e.Y+e.Height))})
|
|
case *allg5.MouseButtonDownEvent:
|
|
t.Handle(&ui.MouseButtonDownEvent{MouseEvent: mouseEvent(e.MouseEvent), Button: ui.MouseButton(e.Button)})
|
|
case *allg5.MouseButtonUpEvent:
|
|
t.Handle(&ui.MouseButtonUpEvent{MouseEvent: mouseEvent(e.MouseEvent), Button: ui.MouseButton(e.Button)})
|
|
case *allg5.MouseMoveEvent:
|
|
t.Handle(&ui.MouseMoveEvent{MouseEvent: mouseEvent(e.MouseEvent), MouseWheel: float32(e.DeltaZ)})
|
|
case *allg5.UserEvent:
|
|
// used to unblock the wait for events.
|
|
}
|
|
ev = r.eq.Get()
|
|
}
|
|
|
|
if r.newCursor != r.cursor {
|
|
r.cursor = r.newCursor
|
|
switch r.cursor {
|
|
case ui.MouseCursorNone:
|
|
r.disp.SetMouseCursor(allg5.MouseCursorNone)
|
|
case ui.MouseCursorDefault:
|
|
r.disp.SetMouseCursor(allg5.MouseCursorDefault)
|
|
case ui.MouseCursorNotAllowed:
|
|
r.disp.SetMouseCursor(allg5.MouseCursorUnavailable)
|
|
case ui.MouseCursorPointer:
|
|
r.disp.SetMouseCursor(allg5.MouseCursorLink)
|
|
case ui.MouseCursorText:
|
|
r.disp.SetMouseCursor(allg5.MouseCursorEdit)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *Renderer) Refresh() {
|
|
r.user.EmitEvent()
|
|
}
|
|
|
|
// Renderer implementation (lifetime)
|
|
|
|
func (r *Renderer) Destroy() error {
|
|
r.user.Destroy()
|
|
r.eq.Destroy()
|
|
for _, f := range r.ft {
|
|
f.Destroy()
|
|
}
|
|
r.ft = nil
|
|
r.disp.Destroy()
|
|
return nil
|
|
}
|
|
|
|
// Renderer implementation (drawing)
|
|
|
|
func (r *Renderer) Clear(c color.Color) {
|
|
allg5.ClearToColor(newColor(c))
|
|
}
|
|
|
|
func (r *Renderer) CreateImage(im image.Image) (ui.Image, error) {
|
|
bmp, err := allg5.NewBitmapFromImage(im, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &uiImage{bmp}, nil
|
|
}
|
|
|
|
func (r *Renderer) CreateImagePath(path string) (ui.Image, error) {
|
|
bmp, err := allg5.LoadBitmap(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &uiImage{bmp}, nil
|
|
}
|
|
|
|
func (r *Renderer) CreateImageSize(w, h float32) (ui.Image, error) {
|
|
bmp, err := allg5.NewVideoBitmap(int(w), int(h))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &uiImage{bmp}, nil
|
|
}
|
|
|
|
func (r *Renderer) DefaultTarget() ui.Image {
|
|
return &uiImage{r.disp.Target()}
|
|
}
|
|
|
|
func (r *Renderer) DrawImage(p geom.PointF32, im ui.Image) {
|
|
bmp := r.mustGetBitmap(im)
|
|
bmp.Draw(p.X, p.Y)
|
|
}
|
|
|
|
func (r *Renderer) FillRectangle(rect geom.RectangleF32, c color.Color) {
|
|
allg5.DrawFilledRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c))
|
|
}
|
|
|
|
func (r *Renderer) Font(name string) ui.Font {
|
|
return r.ft[name]
|
|
}
|
|
|
|
func (r *Renderer) mustGetBitmap(im ui.Image) *allg5.Bitmap {
|
|
m, ok := im.(*uiImage)
|
|
if !ok {
|
|
panic("image must be created on same renderer")
|
|
}
|
|
return m.bmp
|
|
}
|
|
|
|
func (r *Renderer) Rectangle(rect geom.RectangleF32, c color.Color, thickness float32) {
|
|
allg5.DrawRectangle(rect.Min.X, rect.Min.Y, rect.Max.X, rect.Max.Y, newColor(c), thickness)
|
|
}
|
|
|
|
func (r *Renderer) RegisterFont(path, name string, size int) error {
|
|
var f, err = allg5.LoadTTFFont(path, int(size))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var prev = r.ft[name]
|
|
if prev != nil {
|
|
prev.Destroy()
|
|
}
|
|
r.ft[name] = newFont(f)
|
|
return nil
|
|
}
|
|
|
|
func (r *Renderer) RegisterFonts(path string, fonts ...FontDefinition) error {
|
|
for _, f := range fonts {
|
|
err := r.RegisterFont(path, f.Name, f.Size)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Renderer) RenderTo(im ui.Image) {
|
|
bmp := r.mustGetBitmap(im)
|
|
bmp.SetAsTarget()
|
|
}
|
|
|
|
func (r *Renderer) RenderToDisplay() {
|
|
r.disp.SetAsTarget()
|
|
}
|
|
|
|
func (r *Renderer) Size() geom.PointF32 {
|
|
return geom.PtF32(float32(r.disp.Width()), float32(r.disp.Height()))
|
|
}
|
|
|
|
func (r *Renderer) SetIcon(im ui.Image) {
|
|
bmp := r.mustGetBitmap(im)
|
|
r.disp.SetIcon(bmp)
|
|
}
|
|
|
|
func (r *Renderer) SetMouseCursor(c ui.MouseCursor) {
|
|
r.newCursor = c
|
|
}
|
|
|
|
func (r *Renderer) SetWindowTitle(t string) {
|
|
r.disp.SetWindowTitle(t)
|
|
}
|
|
|
|
func (r *Renderer) Target() ui.Image {
|
|
return &uiImage{allg5.CurrentTarget()}
|
|
}
|
|
|
|
func (r *Renderer) Text(p geom.PointF32, font string, c color.Color, t string) {
|
|
var f = r.ft[font]
|
|
if f == nil {
|
|
return
|
|
}
|
|
f.f.Draw(p.X, p.Y, newColor(c), allg5.AlignLeft, t)
|
|
}
|
|
|
|
// Utility functions
|
|
|
|
func eventWait(eq *allg5.EventQueue, wait bool) allg5.Event {
|
|
if wait {
|
|
return eq.GetWait()
|
|
}
|
|
return eq.Get()
|
|
}
|
|
|
|
func eventBase(e allg5.Event) ui.EventBase {
|
|
return ui.EventBase{StampInSeconds: e.Stamp()}
|
|
}
|
|
|
|
func mouseEvent(e allg5.MouseEvent) ui.MouseEvent {
|
|
return ui.MouseEvent{EventBase: eventBase(e), X: float32(e.X), Y: float32(e.Y)}
|
|
}
|
|
|
|
func newColor(c color.Color) allg5.Color {
|
|
if c == nil {
|
|
return newColor(color.Black)
|
|
}
|
|
var r, g, b, a = c.RGBA()
|
|
var r8, g8, b8, a8 = byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)
|
|
return allg5.NewColorAlpha(r8, g8, b8, a8)
|
|
}
|