1// Copyright 2019 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Binary segmentdisplaydemo shows the functionality of a segment display.
16package main
17
18import (
19	"context"
20	"strings"
21	"time"
22
23	"github.com/mum4k/termdash"
24	"github.com/mum4k/termdash/cell"
25	"github.com/mum4k/termdash/container"
26	"github.com/mum4k/termdash/linestyle"
27	"github.com/mum4k/termdash/terminal/tcell"
28	"github.com/mum4k/termdash/terminal/terminalapi"
29	"github.com/mum4k/termdash/widgets/segmentdisplay"
30)
31
32// clock displays the current time on the segment display.
33// Exists when the context expires.
34func clock(ctx context.Context, sd *segmentdisplay.SegmentDisplay) {
35	ticker := time.NewTicker(1 * time.Second)
36	defer ticker.Stop()
37	for {
38		select {
39		case <-ticker.C:
40			now := time.Now()
41			nowStr := now.Format("15 04")
42			parts := strings.Split(nowStr, " ")
43
44			spacer := " "
45			if now.Second()%2 == 0 {
46				spacer = ":"
47			}
48			chunks := []*segmentdisplay.TextChunk{
49				segmentdisplay.NewChunk(parts[0], segmentdisplay.WriteCellOpts(cell.FgColor(cell.ColorNumber(33)))),
50				segmentdisplay.NewChunk(spacer),
51				segmentdisplay.NewChunk(parts[1], segmentdisplay.WriteCellOpts(cell.FgColor(cell.ColorRed))),
52			}
53			if err := sd.Write(chunks); err != nil {
54				panic(err)
55			}
56
57		case <-ctx.Done():
58			return
59		}
60	}
61}
62
63// rotate returns a new slice with inputs rotated by step.
64// I.e. for a step of one:
65//   inputs[0] -> inputs[len(inputs)-1]
66//   inputs[1] -> inputs[0]
67// And so on.
68func rotate(inputs []rune, step int) []rune {
69	return append(inputs[step:], inputs[:step]...)
70}
71
72// rollText rolls a text across the segment display.
73// Exists when the context expires.
74func rollText(ctx context.Context, sd *segmentdisplay.SegmentDisplay) {
75	const text = "Termdash"
76	colors := map[rune]cell.Color{
77		'T': cell.ColorNumber(33),
78		'e': cell.ColorRed,
79		'r': cell.ColorYellow,
80		'm': cell.ColorNumber(33),
81		'd': cell.ColorGreen,
82		'a': cell.ColorRed,
83		's': cell.ColorGreen,
84		'h': cell.ColorRed,
85	}
86
87	var state []rune
88	for i := 0; i < len(text); i++ {
89		state = append(state, ' ')
90	}
91	state = append(state, []rune(text)...)
92	ticker := time.NewTicker(1 * time.Second)
93	defer ticker.Stop()
94	for {
95		select {
96		case <-ticker.C:
97			var chunks []*segmentdisplay.TextChunk
98			for i := 0; i < len(text); i++ {
99				chunks = append(chunks, segmentdisplay.NewChunk(
100					string(state[i]),
101					segmentdisplay.WriteCellOpts(cell.FgColor(colors[state[i]])),
102				))
103			}
104			if err := sd.Write(chunks); err != nil {
105				panic(err)
106			}
107			state = rotate(state, 1)
108
109		case <-ctx.Done():
110			return
111		}
112	}
113}
114
115func main() {
116	t, err := tcell.New()
117	if err != nil {
118		panic(err)
119	}
120	defer t.Close()
121
122	ctx, cancel := context.WithCancel(context.Background())
123	clockSD, err := segmentdisplay.New()
124	if err != nil {
125		panic(err)
126	}
127	go clock(ctx, clockSD)
128
129	rollingSD, err := segmentdisplay.New()
130	if err != nil {
131		panic(err)
132	}
133	go rollText(ctx, rollingSD)
134
135	c, err := container.New(
136		t,
137		container.Border(linestyle.Light),
138		container.BorderTitle("PRESS Q TO QUIT"),
139		container.SplitHorizontal(
140			container.Top(
141				container.PlaceWidget(rollingSD),
142			),
143			container.Bottom(
144				container.PlaceWidget(clockSD),
145			),
146			container.SplitPercent(40),
147		),
148	)
149	if err != nil {
150		panic(err)
151	}
152
153	quitter := func(k *terminalapi.Keyboard) {
154		if k.Key == 'q' || k.Key == 'Q' {
155			cancel()
156		}
157	}
158
159	if err := termdash.Run(ctx, t, c, termdash.KeyboardSubscriber(quitter), termdash.RedrawInterval(1*time.Second)); err != nil {
160		panic(err)
161	}
162}
163