diff --git a/exhibit/block.go b/exhibit/block.go index 0d83e68..ec39167 100644 --- a/exhibit/block.go +++ b/exhibit/block.go @@ -16,6 +16,10 @@ func NewBlock(originx, originy, sizex, sizey int) Block { return b } +func (b Block) Size() image.Point { + return b.Rect.Size() +} + func (b *Block) SetSize(p image.Point) { dx := b.Rect.Min.X dy := b.Rect.Min.Y @@ -23,6 +27,10 @@ func (b *Block) SetSize(p image.Point) { b.Rect.Max.Y = p.Y + dy } +func (b Block) Origin() image.Point { + return b.Rect.Min +} + func (b *Block) SetOrigin(p image.Point) { d := p.Sub(b.Rect.Min) b.Rect = b.Rect.Add(d) diff --git a/exhibit/border.go b/exhibit/border.go index ee1be5e..e30a422 100644 --- a/exhibit/border.go +++ b/exhibit/border.go @@ -1,5 +1,9 @@ package exhibit +import ( + "image" +) + const ( Thick = Style(iota) Thin @@ -22,6 +26,13 @@ const ( Intersect ) +type Border struct { + Style + Box + Attributes + Visible bool +} + type Style int type Box int @@ -51,3 +62,34 @@ func BorderRune(c Box, s Style) rune { return ' ' } } + +func (b Border) Cell(p image.Point, r image.Rectangle) (Cell, bool) { + c := Cell{} + + if !b.Visible { + return c, false + } + + c.Attrs = b.Attributes + + if p.X != r.Min.X && p.X != r.Max.X-1 && + p.Y != r.Min.Y && p.Y != r.Max.Y-1 { + return c, false + } + + if p.X == r.Min.X && p.Y == r.Min.Y { + c.Value = BorderRune(TopLeft, b.Style) + } else if p.X == r.Max.X-1 && p.Y == r.Min.Y { + c.Value = BorderRune(TopRight, b.Style) + } else if p.X == r.Min.X && p.Y == r.Max.Y-1 { + c.Value = BorderRune(BottomLeft, b.Style) + } else if p.X == r.Max.X-1 && p.Y == r.Max.Y-1 { + c.Value = BorderRune(BottomRight, b.Style) + } else if p.X == r.Min.X || p.X == r.Max.X-1 { + c.Value = BorderRune(Vertical, b.Style) + } else if p.Y == r.Min.Y || p.Y == r.Max.Y-1 { + c.Value = BorderRune(Horizontal, b.Style) + } + + return c, true +} diff --git a/exhibit/list_widget.go b/exhibit/list_widget.go index 0ddf8a9..d6543ed 100644 --- a/exhibit/list_widget.go +++ b/exhibit/list_widget.go @@ -11,16 +11,17 @@ type ListEntry interface { } type ListWidget struct { - Style Style - blockLock sync.Mutex block Block attributesLock sync.Mutex attributes Attributes - rightAlign bool - border bool + rightAlignLock sync.Mutex + rightAlign bool + + borderLock sync.Mutex + border Border listLock sync.Mutex list [][]Cell @@ -115,22 +116,39 @@ func (l *ListWidget) Commit() { l.recalculateCells() } -func (l *ListWidget) SetBorder(b bool) { +func (l *ListWidget) Border() Border { + l.borderLock.Lock() + defer l.borderLock.Unlock() + + return l.border +} +func (l *ListWidget) SetBorder(b Border) { + l.borderLock.Lock() if l.border == b { return } l.border = b + l.borderLock.Unlock() l.recalculateCells() } +func (l *ListWidget) RightAlign() bool { + l.rightAlignLock.Lock() + defer l.rightAlignLock.Unlock() + + return l.rightAlign +} + func (l *ListWidget) SetRightAlign(b bool) { + l.rightAlignLock.Lock() if l.rightAlign == b { return } l.rightAlign = b + l.rightAlignLock.Unlock() l.recalculateCells() } @@ -140,14 +158,20 @@ func (l *ListWidget) recalculateCells() { defer l.listLock.Unlock() l.blockLock.Lock() - origin := l.block.Rect.Min - size := l.block.Rect.Size() + rect := l.block.Rect l.blockLock.Unlock() + origin := rect.Min + size := rect.Size() + + rightAlign := l.RightAlign() + + border := l.Border() + cells := make(map[image.Point]Cell) var i, bx, by int - if l.border { + if border.Visible { size = size.Add(image.Point{2, 2}) bx = 1 by = 1 @@ -158,18 +182,16 @@ func (l *ListWidget) recalculateCells() { c := Cell{Value: ' '} point := image.Pt(x, y).Add(origin) - if l.border { - cell, ok := l.borderCell(image.Pt(x, y), size) - if ok { - cells[point] = cell - continue - } + cell, ok := border.Cell(point, rect) + if ok { + cells[point] = cell + continue } if y < len(l.list)+by { length := len(l.list[y-by]) - if l.rightAlign { + if rightAlign { i = (size.X - x - length - bx) * -1 } else { i = x - bx @@ -188,28 +210,3 @@ func (l *ListWidget) recalculateCells() { l.block.Cells = cells l.blockLock.Unlock() } - -func (l *ListWidget) borderCell(p image.Point, size image.Point) (Cell, bool) { - c := Cell{} - c.Attrs = l.Attributes() - - if p.X != 0 && p.X != size.X-1 && p.Y != 0 && p.Y != size.Y-1 { - return c, false - } - - if p.X == 0 && p.Y == 0 { - c.Value = BorderRune(TopLeft, l.Style) - } else if p.X == size.X-1 && p.Y == 0 { - c.Value = BorderRune(TopRight, l.Style) - } else if p.X == 0 && p.Y == size.Y-1 { - c.Value = BorderRune(BottomLeft, l.Style) - } else if p.X == size.X-1 && p.Y == size.Y-1 { - c.Value = BorderRune(BottomRight, l.Style) - } else if p.X == 0 || p.X == size.X-1 { - c.Value = BorderRune(Vertical, l.Style) - } else if p.Y == 0 || p.Y == size.Y-1 { - c.Value = BorderRune(Horizontal, l.Style) - } - - return c, true -} diff --git a/exhibit/scene.go b/exhibit/scene.go index c557db5..d8a0ef4 100644 --- a/exhibit/scene.go +++ b/exhibit/scene.go @@ -11,6 +11,7 @@ type Scene struct { func (s *Scene) Render() { s.Window.SetSize(s.Terminal.Size()) + s.Window.SetOrigin(image.Point{}) c := make([]Cell, 0) diff --git a/exhibit/widget.go b/exhibit/widget.go index ee0b405..3613f1e 100644 --- a/exhibit/widget.go +++ b/exhibit/widget.go @@ -4,17 +4,12 @@ import ( "image" ) -type Border struct { - Top bool - Bottom bool - Left bool - Right bool -} - type Widget interface { Render(image.Point) Block Size() image.Point SetSize(image.Point) + Origin() image.Point + SetOrigin(image.Point) Attributes() Attributes SetAttributes(Attributes) } diff --git a/exhibit/window_widget.go b/exhibit/window_widget.go index add6103..12ef642 100644 --- a/exhibit/window_widget.go +++ b/exhibit/window_widget.go @@ -2,16 +2,28 @@ package exhibit import ( "image" + "sync" ) type WindowWidget struct { - block Block - attributes Attributes + Style Style + blockLock sync.Mutex + block Block + + attributesLock sync.Mutex + attributes Attributes + + borderLock sync.Mutex border Border + + widgetLock sync.Mutex widgets []Widget } func (w *WindowWidget) AddWidget(widget Widget) { + w.widgetLock.Lock() + defer w.widgetLock.Unlock() + if w.widgets == nil { w.widgets = make([]Widget, 0) } @@ -20,10 +32,16 @@ func (w *WindowWidget) AddWidget(widget Widget) { } func (w WindowWidget) Attributes() Attributes { + w.attributesLock.Lock() + defer w.attributesLock.Unlock() + return w.attributes } func (w *WindowWidget) SetAttributes(a Attributes) { + w.attributesLock.Lock() + defer w.attributesLock.Unlock() + w.attributes = a } @@ -35,46 +53,78 @@ func (w *WindowWidget) SetSize(p image.Point) { w.block.SetSize(p) } -func (w *WindowWidget) Render() Block { +func (w *WindowWidget) Origin() image.Point { + w.blockLock.Lock() + defer w.blockLock.Unlock() + + return w.block.Rect.Min +} + +func (w *WindowWidget) SetOrigin(p image.Point) { + w.blockLock.Lock() + defer w.blockLock.Unlock() + + w.block.SetOrigin(p) +} + +func (w *WindowWidget) Border() Border { + w.borderLock.Lock() + defer w.borderLock.Unlock() + + return w.border +} + +func (w *WindowWidget) SetBorder(b Border) { + w.borderLock.Lock() + defer w.borderLock.Unlock() + + w.border = b +} + +func (w *WindowWidget) Render(origin image.Point) Block { if w.block.Rect.Size().X == 0 || w.block.Rect.Size().Y == 0 { - return w.renderContent() - } else { - return w.renderSize() + return NewBlock(0, 0, 0, 0) } -} - -func (w *WindowWidget) renderContent() Block { - // c := make([][]Cell, 0) - - // var y int - - // for _, w := range w.widgets { - // for _, row := range w.Render() { - - // t := make([]Cell, len(row)) - // c = append(c, t) - - // for j, col := range row { - // col.Pos.Y = y - // c[y][j] = col - // } - - // y++ - // } - // } - - return Block{} -} - -func (w *WindowWidget) renderSize() Block { - // c := make([][]Cell, w.size.Y) - - // for y := 0; y < w.size.Y; y++ { - // for x := 0; x < w.size.X; x++ { - // c[y][x] = Cell{Pos: Position{X: x, Y: y}} - // } - // } - - // return c - return Block{} + + w.widgetLock.Lock() + defer w.widgetLock.Unlock() + + w.blockLock.Lock() + defer w.blockLock.Unlock() + + var borderAdj image.Point + border := w.Border() + if border.Visible { + borderAdj = image.Pt(1, 1) + } + + block := NewBlock(0, 0, 0, 0) + block.SetSize(w.block.Rect.Size()) + block.SetOrigin(origin.Add(w.block.Origin())) + + for _, widget := range w.widgets { + cells := widget.Render(block.Origin().Add(borderAdj)).Cells + + for k, v := range cells { + if !k.In(block.Rect) { + continue + } + + block.Cells[k] = v + } + } + + if border.Visible { + for x := 0; x < block.Size().X; x++ { + for y := 0; y < block.Size().Y; y++ { + point := image.Pt(x, y).Add(block.Origin()) + c, ok := border.Cell(point, block.Rect) + if ok { + block.Cells[point] = c + } + } + } + } + + return block } diff --git a/spectator.go b/spectator.go index ed387d1..a67c7b4 100644 --- a/spectator.go +++ b/spectator.go @@ -77,27 +77,25 @@ func main() { defer terminal.Shutdown() terminal.HideCursor() - // window = &exhibit.WindowWidget{} + window = &exhibit.WindowWidget{} + window.SetBorder(exhibit.Border{Visible: true}) topAsks = &exhibit.ListWidget{} // topAsks.SetSize(image.Point{100, 100}) - topAsks.SetBorder(true) topAsks.SetRightAlign(true) topAsks.SetAttributes(exhibit.Attributes{ForegroundColor: exhibit.FGCyan}) topBids = &exhibit.ListWidget{} - // topBids.SetBorder(true) // topBids.SetRightAlign(true) // topBids.SetAttributes(exhibit.Attributes{ForegroundColor: exhibit.FGGreen}) midPrice = &exhibit.ListWidget{} // midPrice.SetRightAlign(true) - // window.AddWidget(topAsks) + window.AddWidget(topAsks) // window.AddWidget(midPrice) // window.AddWidget(topBids) - // scene := exhibit.Scene{terminal, window} - scene := exhibit.Scene{terminal, topAsks} + scene := exhibit.Scene{terminal, window} conn, _, err := websocket.DefaultDialer.Dial("wss://ws-feed.gdax.com", nil) if err != nil {