diff --git a/application.go b/application.go index 4929cc7..151fec0 100644 --- a/application.go +++ b/application.go @@ -33,42 +33,60 @@ func (application *Application) Run () ( return } +// OnQuit registers an event handler to be called just before the application +// quits. This can happen when the user closes the application, or the backend +// experiences an unrecoverable error. func (application *Application) OnQuit ( onQuit func (), ) { application.callbackManager.onQuit = onQuit } +// OnPress registers an event handler to be called when a key or mouse button +// is pressed. func (application *Application) OnPress ( onPress func (button Button), ) { application.callbackManager.onPress = onPress } +// OnPress registers an event handler to be called when a key or mouse button +// is released. func (application *Application) OnRelease ( onRelease func (button Button), ) { application.callbackManager.onRelease = onRelease } +// OnResize registers an event handler to be called when the application window +// is resized. After the event handler is called, any updates it makes will +// automatically be pushed to the screen. func (application *Application) OnResize ( onResize func (), ) { application.callbackManager.onResize = onResize } +// OnMouseMove registers an event handler to be called when mouse motion is +// detected. The coordinates of the cell that the mouse now hovers over are +// given as input. func (application *Application) OnMouseMove ( onMouseMove func (x, y int), ) { application.callbackManager.onMouseMove = onMouseMove } +// OnScroll registers an event handler to be called when the user uses the mouse +// scroll wheel. Horizontal and vertical amounts are given as input. func (application *Application) OnScroll ( onScroll func (x, y int), ) { application.callbackManager.onScroll = onScroll } +// OnStart registers an event handler to be called once when the application +// starts, right before the first time updates are pushed to the screen. +// Anything done in here will be the first thing to appear on screen. func (application *Application) OnStart ( onStart func (), ) { diff --git a/backend.go b/backend.go index d29e526..d009886 100644 --- a/backend.go +++ b/backend.go @@ -3,13 +3,51 @@ package stone import "image" import "errors" +// Backend represents a backend for stone. Backends can be registered for use +// with the RegisterBackend() function. All of the below methods MUST be thread +// safe! type Backend interface { - Run () + // Run is the backend's event loop. It must cleanly exit when the user + // closes the window, but not before calling the OnQuit event. Run + // must call event handlers within its own event loop in a + // non-concurrent fashion. + // + // The OnStart event handler must run after the backend has been fully + // initialized, and right before updates are first pushed to the screen. + // Whatever the application draws from within this event handler must be + // the first thing that appears on-screen. + // + // The OnResize evnt handler must run whenever the window is resized. + // The backend must push updates to the screen after OnResize has been + // run. + // + // The backend must not push updates to the screen in any other case, + // except when its Draw() method is specifically called. + // + // The OnPress, OnRelease, OnMouseMove, and OnMouseScroll events are to + // be called when such events happen. It is reccommended to compress + // resize, mouse move, and mouse scroll events whenever possible to + // reduce the likelihood of event buildup. + Run () + + // SetTitle sets the application title. This will most often be the + // window title. This method may not always produce an effect, depending + // on the backend. SetTitle (title string) (err error) - SetIcon (icons []image.Image) (err error) - Draw () + + // SetIcon takes in a set of images of different sizes and sets the + // window's icon to them. This method may not always produce an effect, + // depending on the backend. + SetIcon (icons []image.Image) (err error) + + // Draw pushes all updates made to the application's buffer to the + // screen. + Draw () } +// BackendFactory should completely initialize a backend, and return it. If +// anything goes wrong, it should stop, clean up any resources and return an +// error so another backend can be chosen. type BackendFactory func ( application *Application, callbackManager *CallbackManager, @@ -20,6 +58,7 @@ type BackendFactory func ( var factories []BackendFactory +// RegisterBackend registers a backend factory. func RegisterBackend (factory BackendFactory) { factories = append(factories, factory) } diff --git a/examples/color/main.go b/examples/color/main.go index fab929b..81e7d38 100644 --- a/examples/color/main.go +++ b/examples/color/main.go @@ -58,7 +58,7 @@ func redraw () { application.SetRune(0, height - 1, '+') for x := 0; x < width; x ++ { - application.SetColor(x, height / 2, stone.Color(x % 6 + 2)) + application.SetColor(x, height / 2, stone.Color(x % 5 + 3)) } for x := 1; x < width - 1; x ++ { diff --git a/examples/type/main.go b/examples/type/main.go index 1cda63e..a8285c7 100644 --- a/examples/type/main.go +++ b/examples/type/main.go @@ -1,17 +1,20 @@ package main import "os" +import "fmt" import "image" import _ "image/png" import "git.tebibyte.media/sashakoshka/stone" import _ "git.tebibyte.media/sashakoshka/stone/backends/x" var application = &stone.Application { } -var caret = 0 +var caretX = 0 +var caretY = 2 +var page = 1 func main () { application.SetTitle("hellorld") - application.SetSize(32, 16) + application.SetSize(32, 28) iconFile16, err := os.Open("assets/scaffold16.png") if err != nil { panic(err) } @@ -25,32 +28,57 @@ func main () { iconFile16.Close() application.SetIcon([]image.Image { icon16, icon32 }) + application.OnStart(redraw) + application.OnPress(onPress) + application.OnResize(redraw) - channel, err := application.Run() + err = application.Run() if err != nil { panic(err) } application.Draw() +} - for { - event := <- channel - switch event.(type) { - case stone.EventQuit: - os.Exit(0) +func redraw () { + application.Clear() + _, height := application.Size() + application.SetDot(0, 0) + fmt.Fprint(application, "type some text below:") + caretX = 0 + caretY = 2 + application.SetDot(0, height - 1) + fmt.Fprintf(application, "page %d", page) + drawCaret() +} - case stone.EventPress: - button := event.(stone.EventPress).Button - if button.Printable() { - application.SetRune(caret, 0, rune(button)) - caret ++ - width, _ := application.Size() - if caret >= width { - caret = 0 - } - application.Draw() - } +func drawCaret () { + application.SetRune(caretX, caretY, '+') + application.SetColor(caretX, caretY, stone.ColorDim) +} - case stone.EventResize: - application.Draw() +func onPress (button stone.Button) { + width, height := application.Size() + + if button == stone.KeyEnter { + application.SetRune(caretX, caretY, 0) + caretX = 0 + caretY ++ + + } else if button.Printable() { + application.SetRune(caretX, caretY, rune(button)) + application.SetColor(caretX, caretY, stone.ColorForeground) + caretX ++ + + if caretX >= width { + caretX = 0 + caretY ++ } } + + if caretY >= height - 2 { + page ++ + redraw() + } + + drawCaret() + application.Draw() }