NebulousLabs/Sia — pull request #234 by lukechampine (2014-09-22 01:17:01+00:00)
Title: Termbox client

Whew. This is a big one. It's not finished yet either. I'm pull requesting it now in case I don't have a chance to work on it during my trip.

The client now has a much more well-defined structure. Similar to the previous implementation, each View is given an area in which it can draw. Views satisfy the View interface:
```go
type View interface {
SetDims(Rectangle) // Set the area in which the View can draw
Focus() // Executed when the View is given focus
Draw() // Called after every termbox event
HandleKey(termbox.Key) // Handle special keys (arrow keys, spacebar, etc.)
HandleRune(rune) // Handle unicode characters (a-z, 0-9, etc.)
}
```
Views can contain references to sub-Views, and are responsible for properly passing focus between themselves. "Focus" is loosely defined here; generally, it means which View is in charge of handling input, but this is not always the case. (For example, a View might watch for the 'q' key and handle it if pressed, but forward any other keys to the sub-view that has focus.)

An important aspect of this View scheme is the use of embedded types. Embedded types are anonymous struct fields whose methods and fields can be accessed as if they were part of the type they are embedded in. For example, most Views embed the "DefaultView" type:
```go
type Field struct {
DefaultView
color termbox.Attribute
text string
pos int
}
```
From and OO standpoint, a Field "is-a" DefaultView. Why is this useful? Because it saves us the trouble of writing tons of boilerplate code. DefaultView implements the View interface, so any type that embeds it is automatically a View as well. (In case you were wondering: if you redefine `Draw()` for the Field type, it will take precedence over the `Draw()` method of DefaultView. So you only have to redefine what's different.) DefaultView also contains a pointer to its parent, and a bool to indicate whether it has focus.

This isn't a perfect abstraction, but it saves a lot of redundant typing. For example, when I created the Field type, I didn't have to worry about satisfying the View interface. I just embedded the type and added what was missing: a color, a string to hold the Field contents, and an int to hold the cursor position.

My hope is that these abstractions make it easier to extend the client and create new Views. I'm trying to cut down on repeated code as much as possible.

In terms of functionality, this PR is still a bit lacking. The settings View is pretty much done, and the Participant names should update properly, but the Wallets screen doesn't display anything useful right now. It'd be helpful if you could specify what you'd like the Wallet screen to look like, and I can work from there.

My personal to-do list:
- A "Primary View" should be created as the top-most View. This would house status bars on the top/bottom, handle universal inputs such as ESC, and display error/warning/info pop-ups. The main menu would be a sub-View of this View.
- Fields should support utf-8, and should automatically scroll if the user enters too much input. See the [editbox.go](https://github.com/nsf/termbox-go/blob/master/_demos/editbox.go) demo.
- All API calls should be asynchronous. That is, if it takes a while to fetch the wallet list, the user should see a "Downloading wallets..." message instead of the entire interface freezing up.
- Lots of other small details that need attention: colors, input forwarding, consistent case in struct fields...

Overall I'm happy with how this is progressing. I'm a little bummed that I won't be able to work on it full time for a few days. Oh well.