commit 7fae7f7

Hubert Hirtz  ·  2020-10-21 14:22:28 +0000 UTC
parent 75d5148
Vertical channel list
7 files changed,  +231, -213
M app.go
M app.go
+34, -25
  1@@ -3,7 +3,6 @@ package senpai
  2 import (
  3 	"crypto/tls"
  4 	"fmt"
  5-	"hash/fnv"
  6 	"os/exec"
  7 	"strings"
  8 	"time"
  9@@ -45,6 +44,7 @@ func NewApp(cfg Config) (app *App, err error) {
 10 	if err != nil {
 11 		return
 12 	}
 13+	app.win.SetPrompt(">")
 14 
 15 	app.initWindow()
 16 
 17@@ -261,19 +261,31 @@ func (app *App) handleUIEvent(ev tcell.Event) {
 18 		case tcell.KeyRight:
 19 			if ev.Modifiers() == tcell.ModAlt {
 20 				app.win.NextBuffer()
 21+				app.updatePrompt()
 22 			} else {
 23 				app.win.InputRight()
 24 			}
 25 		case tcell.KeyLeft:
 26 			if ev.Modifiers() == tcell.ModAlt {
 27 				app.win.PreviousBuffer()
 28+				app.updatePrompt()
 29 			} else {
 30 				app.win.InputLeft()
 31 			}
 32 		case tcell.KeyUp:
 33-			app.win.InputUp()
 34+			if ev.Modifiers() == tcell.ModAlt {
 35+				app.win.PreviousBuffer()
 36+			} else {
 37+				app.win.InputUp()
 38+			}
 39+			app.updatePrompt()
 40 		case tcell.KeyDown:
 41-			app.win.InputDown()
 42+			if ev.Modifiers() == tcell.ModAlt {
 43+				app.win.NextBuffer()
 44+			} else {
 45+				app.win.InputDown()
 46+			}
 47+			app.updatePrompt()
 48 		case tcell.KeyHome:
 49 			app.win.InputHome()
 50 		case tcell.KeyEnd:
 51@@ -282,11 +294,13 @@ func (app *App) handleUIEvent(ev tcell.Event) {
 52 			ok := app.win.InputBackspace()
 53 			if ok {
 54 				app.typing()
 55+				app.updatePrompt()
 56 			}
 57 		case tcell.KeyDelete:
 58 			ok := app.win.InputDelete()
 59 			if ok {
 60 				app.typing()
 61+				app.updatePrompt()
 62 			}
 63 		case tcell.KeyTab:
 64 			ok := app.win.InputAutoComplete()
 65@@ -305,9 +319,11 @@ func (app *App) handleUIEvent(ev tcell.Event) {
 66 					Body:      fmt.Sprintf("%q: %s", input, err),
 67 				})
 68 			}
 69+			app.updatePrompt()
 70 		case tcell.KeyRune:
 71 			app.win.InputRune(ev.Rune())
 72 			app.typing()
 73+			app.updatePrompt()
 74 		default:
 75 			return
 76 		}
 77@@ -442,22 +458,22 @@ func (app *App) formatMessage(ev irc.MessageEvent) (buffer string, line ui.Line,
 78 	headColor := ui.ColorWhite
 79 	if isFromSelf && isQuery {
 80 		head = "\u2192 " + ev.Target
 81-		headColor = app.identColor(ev.Target)
 82+		headColor = ui.IdentColor(ev.Target)
 83 	} else if isAction || isNotice {
 84 		head = "*"
 85 	} else {
 86-		headColor = app.identColor(head)
 87+		headColor = ui.IdentColor(head)
 88 	}
 89 
 90 	body := strings.TrimSuffix(ev.Content, "\x01")
 91 	if isNotice && isAction {
 92-		c := ircColorSequence(app.identColor(ev.User.Name))
 93+		c := ircColorSequence(ui.IdentColor(ev.User.Name))
 94 		body = fmt.Sprintf("(%s%s\x0F:%s)", c, ev.User.Name, body[7:])
 95 	} else if isAction {
 96-		c := ircColorSequence(app.identColor(ev.User.Name))
 97+		c := ircColorSequence(ui.IdentColor(ev.User.Name))
 98 		body = fmt.Sprintf("%s%s\x0F%s", c, ev.User.Name, body[7:])
 99 	} else if isNotice {
100-		c := ircColorSequence(app.identColor(ev.User.Name))
101+		c := ircColorSequence(ui.IdentColor(ev.User.Name))
102 		body = fmt.Sprintf("(%s%s\x0F: %s)", c, ev.User.Name, body)
103 	}
104 
105@@ -471,6 +487,16 @@ func (app *App) formatMessage(ev irc.MessageEvent) (buffer string, line ui.Line,
106 	return
107 }
108 
109+func (app *App) updatePrompt() {
110+	buffer := app.win.CurrentBuffer()
111+	command := app.win.InputIsCommand()
112+	if buffer == Home || command {
113+		app.win.SetPrompt(">")
114+	} else {
115+		app.win.SetPrompt(app.s.Nick())
116+	}
117+}
118+
119 func ircColorSequence(code int) string {
120 	var c [3]rune
121 	c[0] = 0x03
122@@ -479,23 +505,6 @@ func ircColorSequence(code int) string {
123 	return string(c[:])
124 }
125 
126-// see <https://modern.ircdocs.horse/formatting.html>
127-var identColorBlacklist = []int{1, 8, 16, 27, 28, 88, 89, 90, 91}
128-
129-func (app *App) identColor(s string) (code int) {
130-	h := fnv.New32()
131-	_, _ = h.Write([]byte(s))
132-
133-	code = int(h.Sum32()) % (99 - len(identColorBlacklist))
134-	for _, c := range identColorBlacklist {
135-		if c <= code {
136-			code++
137-		}
138-	}
139-
140-	return
141-}
142-
143 func cleanMessage(s string) string {
144 	var res strings.Builder
145 	var sb ui.StyleBuffer
+2, -0
1@@ -872,6 +872,8 @@ func (s *Session) handle(msg Message) (err error) {
2 			delete(s.users, nickCf)
3 			s.users[newNickCf] = formerUser
4 			u = formerUser.Name.Copy()
5+		} else {
6+			break
7 		}
8 
9 		if nickCf == s.nickCf {
+89, -175
  1@@ -1,8 +1,6 @@
  2 package ui
  3 
  4 import (
  5-	"fmt"
  6-	"math"
  7 	"strings"
  8 	"time"
  9 
 10@@ -169,101 +167,31 @@ type buffer struct {
 11 	isAtTop   bool
 12 }
 13 
 14-func (b *buffer) DrawLines(screen tcell.Screen, x0, width, height, nickColWidth int) {
 15-	st := tcell.StyleDefault
 16-	for x := 0; x < width; x++ {
 17-		for y := 0; y < height; y++ {
 18-			screen.SetContent(x, y, ' ', nil, st)
 19-		}
 20-	}
 21-
 22-	y0 := b.scrollAmt + height
 23-	for i := len(b.lines) - 1; 0 <= i; i-- {
 24-		if y0 < 0 {
 25-			break
 26-		}
 27-
 28-		x1 := x0 + 9 + nickColWidth
 29-
 30-		line := &b.lines[i]
 31-		nls := line.NewLines(width - x1)
 32-		y0 -= len(nls) + 1
 33-		if height <= y0 {
 34-			continue
 35-		}
 36-
 37-		if i == 0 || b.lines[i-1].At.Truncate(time.Minute) != line.At.Truncate(time.Minute) {
 38-			printTime(screen, x0, y0, st.Bold(true), line.At.Local())
 39-		}
 40-
 41-		head := truncate(line.Head, nickColWidth, "\u2026")
 42-		x := x0 + 7 + nickColWidth - StringWidth(head)
 43-		st = st.Foreground(colorFromCode(line.HeadColor))
 44-		if line.Highlight {
 45-			st = st.Reverse(true)
 46-		}
 47-		screen.SetContent(x-1, y0, ' ', nil, st)
 48-		screen.SetContent(x0+7+nickColWidth, y0, ' ', nil, st)
 49-		printString(screen, &x, y0, st, head)
 50-		st = st.Reverse(false).Foreground(tcell.ColorDefault)
 51-
 52-		x = x1
 53-		y := y0
 54-
 55-		var sb StyleBuffer
 56-		sb.Reset()
 57-		for i, r := range line.Body {
 58-			if 0 < len(nls) && i == nls[0] {
 59-				x = x1
 60-				y++
 61-				nls = nls[1:]
 62-				if height < y {
 63-					break
 64-				}
 65-			}
 66-
 67-			if y != y0 && x == x1 && IsSplitRune(r) {
 68-				continue
 69-			}
 70-
 71-			if st, ok := sb.WriteRune(r); ok != 0 {
 72-				if 1 < ok {
 73-					screen.SetContent(x, y, ',', nil, st)
 74-					x++
 75-				}
 76-				screen.SetContent(x, y, r, nil, st)
 77-				x += runeWidth(r)
 78-			}
 79-		}
 80-
 81-		sb.Reset()
 82-	}
 83-
 84-	b.isAtTop = 0 <= y0
 85-}
 86-
 87 type BufferList struct {
 88 	list    []buffer
 89 	current int
 90-	status  string
 91 
 92-	width        int
 93-	height       int
 94+	tlWidth      int
 95+	tlHeight     int
 96 	nickColWidth int
 97 }
 98 
 99-func NewBufferList(width, height, nickColWidth int) BufferList {
100+func NewBufferList(tlWidth, tlHeight, nickColWidth int) BufferList {
101 	return BufferList{
102 		list:         []buffer{},
103-		width:        width,
104-		height:       height,
105+		tlWidth:      tlWidth,
106+		tlHeight:     tlHeight,
107 		nickColWidth: nickColWidth,
108 	}
109 }
110 
111-func (bs *BufferList) Resize(width, height int) {
112-	bs.width = width
113-	bs.height = height
114+func (bs *BufferList) ResizeTimeline(tlWidth, tlHeight, nickColWidth int) {
115+	bs.tlWidth = tlWidth
116+	bs.tlHeight = tlHeight
117+}
118+
119+func (bs *BufferList) tlInnerWidth() int {
120+	return bs.tlWidth - bs.nickColWidth - 9
121 }
122 
123 func (bs *BufferList) Next() {
124@@ -326,7 +254,7 @@ func (bs *BufferList) AddLine(title string, highlight bool, line Line) {
125 		line.computeSplitPoints()
126 		b.lines = append(b.lines, line)
127 		if idx == bs.current && 0 < b.scrollAmt {
128-			b.scrollAmt += len(line.NewLines(bs.width-9-bs.nickColWidth)) + 1
129+			b.scrollAmt += len(line.NewLines(bs.tlInnerWidth())) + 1
130 		}
131 	}
132 
133@@ -363,10 +291,6 @@ func (bs *BufferList) AddLines(title string, lines []Line) {
134 	b.lines = append(lines[:limit], b.lines...)
135 }
136 
137-func (bs *BufferList) SetStatus(status string) {
138-	bs.status = status
139-}
140-
141 func (bs *BufferList) Current() (title string) {
142 	return bs.list[bs.current].title
143 }
144@@ -384,12 +308,12 @@ func (bs *BufferList) ScrollUp() {
145 	if b.isAtTop {
146 		return
147 	}
148-	b.scrollAmt += bs.height / 2
149+	b.scrollAmt += bs.tlHeight / 2
150 }
151 
152 func (bs *BufferList) ScrollDown() {
153 	b := &bs.list[bs.current]
154-	b.scrollAmt -= bs.height / 2
155+	b.scrollAmt -= bs.tlHeight / 2
156 
157 	if b.scrollAmt < 0 {
158 		b.scrollAmt = 0
159@@ -415,79 +339,28 @@ func (bs *BufferList) idx(title string) int {
160 	return -1
161 }
162 
163-func (bs *BufferList) Draw(screen tcell.Screen) {
164-	bs.list[bs.current].DrawLines(screen, 0, bs.width, bs.height-2, bs.nickColWidth)
165-	bs.drawStatusBar(screen, bs.height-2)
166-	bs.drawTitleList(screen, bs.height-1)
167-}
168-
169-func (bs *BufferList) drawStatusBar(screen tcell.Screen, y int) {
170-	st := tcell.StyleDefault.Dim(true)
171-
172-	for x := 0; x < bs.width; x++ {
173-		screen.SetContent(x, y, 0x2500, nil, st)
174-	}
175-
176-	if bs.status == "" {
177-		return
178-	}
179-
180-	x := 2
181-	screen.SetContent(1, y, 0x2524, nil, st)
182-	printString(screen, &x, y, st, bs.status)
183-	screen.SetContent(x, y, 0x251c, nil, st)
184-}
185-
186-func (bs *BufferList) drawTitleList(screen tcell.Screen, y int) {
187-	var widths []int
188-	for _, b := range bs.list {
189-		width := StringWidth(b.title)
190-		if 0 < b.highlights {
191-			width += int(math.Log10(float64(b.highlights))) + 3
192-		}
193-		widths = append(widths, width)
194-	}
195-
196+func (bs *BufferList) DrawVerticalBufferList(screen tcell.Screen, x0, y0, width, height int) {
197+	width--
198 	st := tcell.StyleDefault
199 
200-	for x := 0; x < bs.width; x++ {
201-		screen.SetContent(x, y, ' ', nil, st)
202-	}
203-
204-	x := (bs.width - widths[bs.current]) / 2
205-	printString(screen, &x, y, st.Underline(true), bs.list[bs.current].title)
206-	x += 2
207-
208-	i := (bs.current + 1) % len(bs.list)
209-	for x < bs.width && i != bs.current {
210-		b := &bs.list[i]
211-		st = tcell.StyleDefault
212-		if b.unread {
213-			st = st.Bold(true)
214-		}
215-		printString(screen, &x, y, st, b.title)
216-		if 0 < b.highlights {
217-			st = st.Foreground(tcell.ColorRed).Reverse(true)
218+	for y := y0; y < y0+height; y++ {
219+		for x := x0; x < x0+width; x++ {
220 			screen.SetContent(x, y, ' ', nil, st)
221-			x++
222-			printNumber(screen, &x, y, st, b.highlights)
223-			screen.SetContent(x, y, ' ', nil, st)
224-			x++
225 		}
226-		x += 2
227-		i = (i + 1) % len(bs.list)
228+		screen.SetContent(x0+width, y, 0x2502, nil, st.Dim(true))
229 	}
230 
231-	i = (bs.current - 1 + len(bs.list)) % len(bs.list)
232-	x = (bs.width - widths[bs.current]) / 2
233-	for 0 < x && i != bs.current {
234-		x -= widths[i] + 2
235-		b := &bs.list[i]
236+	for i, b := range bs.list {
237 		st = tcell.StyleDefault
238+		x := x0
239+		y := y0 + i
240 		if b.unread {
241 			st = st.Bold(true)
242+		} else if y == bs.current {
243+			st = st.Underline(true)
244 		}
245-		printString(screen, &x, y, st, b.title)
246+		title := truncate(b.title, width, "\u2026")
247+		printString(screen, &x, y, st, title)
248 		if 0 < b.highlights {
249 			st = st.Foreground(tcell.ColorRed).Reverse(true)
250 			screen.SetContent(x, y, ' ', nil, st)
251@@ -496,33 +369,74 @@ func (bs *BufferList) drawTitleList(screen tcell.Screen, y int) {
252 			screen.SetContent(x, y, ' ', nil, st)
253 			x++
254 		}
255-		x -= widths[i]
256-		i = (i - 1 + len(bs.list)) % len(bs.list)
257+		y++
258 	}
259 }
260 
261-func printString(screen tcell.Screen, x *int, y int, st tcell.Style, s string) {
262-	for _, r := range s {
263-		screen.SetContent(*x, y, r, nil, st)
264-		*x += runeWidth(r)
265+func (bs *BufferList) DrawTimeline(screen tcell.Screen, x0, y0, nickColWidth int) {
266+	st := tcell.StyleDefault
267+	for x := x0; x < x0+bs.tlWidth; x++ {
268+		for y := y0; y < y0+bs.tlHeight; y++ {
269+			screen.SetContent(x, y, ' ', nil, st)
270+		}
271 	}
272-}
273 
274-func printNumber(screen tcell.Screen, x *int, y int, st tcell.Style, n int) {
275-	s := fmt.Sprintf("%d", n)
276-	printString(screen, x, y, st, s)
277-}
278+	b := &bs.list[bs.current]
279+	yi := b.scrollAmt + y0 + bs.tlHeight
280+	for i := len(b.lines) - 1; 0 <= i; i-- {
281+		if yi < 0 {
282+			break
283+		}
284+
285+		x1 := x0 + 9 + nickColWidth
286+
287+		line := &b.lines[i]
288+		nls := line.NewLines(bs.tlInnerWidth())
289+		yi -= len(nls) + 1
290+		if y0+bs.tlHeight <= yi {
291+			continue
292+		}
293+
294+		if i == 0 || b.lines[i-1].At.Truncate(time.Minute) != line.At.Truncate(time.Minute) {
295+			printTime(screen, x0, yi, st.Bold(true), line.At.Local())
296+		}
297+
298+		identSt := st.Foreground(colorFromCode(line.HeadColor)).Reverse(line.Highlight)
299+		printIdent(screen, x0+7, yi, nickColWidth, identSt, line.Head)
300+
301+		x := x1
302+		y := yi
303+
304+		var sb StyleBuffer
305+		sb.Reset()
306+		for i, r := range line.Body {
307+			if 0 < len(nls) && i == nls[0] {
308+				x = x1
309+				y++
310+				nls = nls[1:]
311+				if bs.tlHeight < y {
312+					break
313+				}
314+			}
315+
316+			if y != yi && x == x1 && IsSplitRune(r) {
317+				continue
318+			}
319+
320+			if st, ok := sb.WriteRune(r); ok != 0 {
321+				if 1 < ok {
322+					screen.SetContent(x, y, ',', nil, st)
323+					x++
324+				}
325+				screen.SetContent(x, y, r, nil, st)
326+				x += runeWidth(r)
327+			}
328+		}
329+
330+		sb.Reset()
331+	}
332 
333-func printTime(screen tcell.Screen, x int, y int, st tcell.Style, t time.Time) {
334-	hr0 := rune(t.Hour()/10) + '0'
335-	hr1 := rune(t.Hour()%10) + '0'
336-	mn0 := rune(t.Minute()/10) + '0'
337-	mn1 := rune(t.Minute()%10) + '0'
338-	screen.SetContent(x+0, y, hr0, nil, st)
339-	screen.SetContent(x+1, y, hr1, nil, st)
340-	screen.SetContent(x+2, y, ':', nil, st)
341-	screen.SetContent(x+3, y, mn0, nil, st)
342-	screen.SetContent(x+4, y, mn1, nil, st)
343+	b.isAtTop = y0 <= yi
344 }
345 
346 func IrcColorCode(code int) string {
+40, -0
 1@@ -0,0 +1,40 @@
 2+package ui
 3+
 4+import (
 5+	"fmt"
 6+	"time"
 7+
 8+	"github.com/gdamore/tcell/v2"
 9+)
10+
11+func printIdent(screen tcell.Screen, x, y, width int, st tcell.Style, s string) {
12+	s = truncate(s, width, "\u2026")
13+	x += width - StringWidth(s)
14+	screen.SetContent(x-1, y, ' ', nil, st)
15+	printString(screen, &x, y, st, s)
16+	screen.SetContent(x, y, ' ', nil, st)
17+}
18+
19+func printString(screen tcell.Screen, x *int, y int, st tcell.Style, s string) {
20+	for _, r := range s {
21+		screen.SetContent(*x, y, r, nil, st)
22+		*x += runeWidth(r)
23+	}
24+}
25+
26+func printNumber(screen tcell.Screen, x *int, y int, st tcell.Style, n int) {
27+	s := fmt.Sprintf("%d", n)
28+	printString(screen, x, y, st, s)
29+}
30+
31+func printTime(screen tcell.Screen, x int, y int, st tcell.Style, t time.Time) {
32+	hr0 := rune(t.Hour()/10) + '0'
33+	hr1 := rune(t.Hour()%10) + '0'
34+	mn0 := rune(t.Minute()/10) + '0'
35+	mn1 := rune(t.Minute()%10) + '0'
36+	screen.SetContent(x+0, y, hr0, nil, st)
37+	screen.SetContent(x+1, y, hr1, nil, st)
38+	screen.SetContent(x+2, y, ':', nil, st)
39+	screen.SetContent(x+3, y, mn0, nil, st)
40+	screen.SetContent(x+4, y, mn1, nil, st)
41+}
+5, -5
 1@@ -242,20 +242,20 @@ func (e *Editor) computeTextWidth() {
 2 	}
 3 }
 4 
 5-func (e *Editor) Draw(screen tcell.Screen, y int) {
 6+func (e *Editor) Draw(screen tcell.Screen, x0, y int) {
 7 	st := tcell.StyleDefault
 8 
 9-	x := 0
10+	x := x0
11 	i := e.offsetIdx
12 
13-	for i < len(e.text[e.lineIdx]) && x < e.width {
14+	for i < len(e.text[e.lineIdx]) && x < x0+e.width {
15 		r := e.text[e.lineIdx][i]
16 		screen.SetContent(x, y, r, nil, st)
17 		x += runeWidth(r)
18 		i++
19 	}
20 
21-	for x < e.width {
22+	for x < x0+e.width {
23 		screen.SetContent(x, y, ' ', nil, st)
24 		x++
25 	}
26@@ -265,7 +265,7 @@ func (e *Editor) Draw(screen tcell.Screen, y int) {
27 	if e.cursorIdx+1 < len(e.textWidth) {
28 		curEnd = e.textWidth[e.cursorIdx+1] - e.textWidth[e.offsetIdx]
29 	}
30-	for x := curStart; x < curEnd; x++ {
31+	for x := x0 + curStart; x < x0+curEnd; x++ {
32 		screen.ShowCursor(x, y)
33 	}
34 }
+19, -0
 1@@ -1,6 +1,8 @@
 2 package ui
 3 
 4 import (
 5+	"hash/fnv"
 6+
 7 	"github.com/gdamore/tcell/v2"
 8 	"github.com/mattn/go-runewidth"
 9 )
10@@ -223,3 +225,20 @@ func colorFromCode(code int) (color tcell.Color) {
11 	}
12 	return
13 }
14+
15+// see <https://modern.ircdocs.horse/formatting.html>
16+var identColorBlacklist = []int{1, 8, 16, 27, 28, 88, 89, 90, 91}
17+
18+func IdentColor(s string) (code int) {
19+	h := fnv.New32()
20+	_, _ = h.Write([]byte(s))
21+
22+	code = int(h.Sum32()) % (99 - len(identColorBlacklist))
23+	for _, c := range identColorBlacklist {
24+		if c <= code {
25+			code++
26+		}
27+	}
28+
29+	return
30+}
+42, -8
 1@@ -18,8 +18,10 @@ type UI struct {
 2 	exit   atomic.Value // bool
 3 	config Config
 4 
 5-	bs BufferList
 6-	e  Editor
 7+	bs     BufferList
 8+	e      Editor
 9+	prompt string
10+	status string
11 }
12 
13 func New(config Config) (ui *UI, err error) {
14@@ -115,7 +117,11 @@ func (ui *UI) AddLines(buffer string, lines []Line) {
15 }
16 
17 func (ui *UI) SetStatus(status string) {
18-	ui.bs.SetStatus(status)
19+	ui.status = status
20+}
21+
22+func (ui *UI) SetPrompt(prompt string) {
23+	ui.prompt = prompt
24 }
25 
26 func (ui *UI) InputIsCommand() bool {
27@@ -172,13 +178,41 @@ func (ui *UI) InputEnter() (content string) {
28 
29 func (ui *UI) Resize() {
30 	w, h := ui.screen.Size()
31-	ui.e.Resize(w)
32-	ui.bs.Resize(w, h-1)
33+	ui.e.Resize(w - 25 - ui.config.NickColWidth)
34+	ui.bs.ResizeTimeline(w-16, h-2, ui.config.NickColWidth)
35 }
36 
37 func (ui *UI) Draw() {
38-	_, h := ui.screen.Size()
39-	ui.e.Draw(ui.screen, h-1)
40-	ui.bs.Draw(ui.screen)
41+	w, h := ui.screen.Size()
42+
43+	ui.e.Draw(ui.screen, 25+ui.config.NickColWidth, h-1)
44+
45+	ui.bs.DrawTimeline(ui.screen, 16, 0, ui.config.NickColWidth)
46+	ui.bs.DrawVerticalBufferList(ui.screen, 0, 0, 16, h)
47+	ui.drawStatusBar(16, h-2, w-16)
48+
49+	for x := 16; x < 25+ui.config.NickColWidth; x++ {
50+		ui.screen.SetContent(x, h-1, ' ', nil, tcell.StyleDefault)
51+	}
52+	st := tcell.StyleDefault.Foreground(colorFromCode(IdentColor(ui.prompt)))
53+	printIdent(ui.screen, 23, h-1, ui.config.NickColWidth, st, ui.prompt)
54+
55 	ui.screen.Show()
56 }
57+
58+func (ui *UI) drawStatusBar(x0, y, width int) {
59+	st := tcell.StyleDefault.Dim(true)
60+
61+	for x := x0; x < x0+width; x++ {
62+		ui.screen.SetContent(x, y, ' ', nil, st)
63+	}
64+
65+	if ui.status == "" {
66+		return
67+	}
68+
69+	x := x0 + 5 + ui.config.NickColWidth
70+	printString(ui.screen, &x, y, st, "--")
71+	x += 2
72+	printString(ui.screen, &x, y, st, ui.status)
73+}