commit 65237b8
delthas
·
2023-12-27 13:55:48 +0000 UTC
parent 1ad07b5
Mention pressing escape to close /search
4 files changed,
+51,
-24
M
app.go
+1,
-1
1@@ -1027,7 +1027,7 @@ func (app *App) handleIRCEvent(netID string, ev interface{}) {
2 app.messageBounds[boundKey{netID, ev.Target}] = boundsNew
3 }
4 case irc.SearchEvent:
5- app.win.OpenOverlay()
6+ app.win.OpenOverlay("Press Escape to close the search results")
7 lines := make([]ui.Line, 0, len(ev.Messages))
8 for _, m := range ev.Messages {
9 _, line := app.formatMessage(s, m)
+22,
-6
1@@ -16,6 +16,8 @@ type Completion struct {
2
3 // Editor is the text field where the user writes messages and commands.
4 type Editor struct {
5+ colors ConfigColors
6+
7 // text contains the written runes. An empty slice means no text is written.
8 text [][]rune
9
10@@ -56,8 +58,9 @@ type Editor struct {
11
12 // NewEditor returns a new Editor.
13 // Call Resize() once before using it.
14-func NewEditor(autoComplete func(cursorIdx int, text []rune) []Completion) Editor {
15+func NewEditor(colors ConfigColors, autoComplete func(cursorIdx int, text []rune) []Completion) Editor {
16 return Editor{
17+ colors: colors,
18 text: [][]rune{{}},
19 history: [][]rune{},
20 textWidth: []int{0},
21@@ -452,11 +455,20 @@ func (e *Editor) bumpOldestChange() {
22 }
23 }
24
25-func (e *Editor) Draw(screen tcell.Screen, x0, y int) {
26+func (e *Editor) Draw(screen tcell.Screen, x0, y int, hint string) {
27 st := tcell.StyleDefault
28
29 x := x0
30 i := e.offsetIdx
31+ text := e.text[e.lineIdx]
32+ showCursor := true
33+
34+ if len(text) == 0 && len(hint) > 0 && !e.backsearch {
35+ i = 0
36+ text = []rune(hint)
37+ st = st.Foreground(e.colors.Status)
38+ showCursor = false
39+ }
40
41 autoStart := -1
42 autoEnd := -1
43@@ -466,8 +478,8 @@ func (e *Editor) Draw(screen tcell.Screen, x0, y int) {
44 autoEnd = e.autoCache[e.autoCacheIdx].EndIdx
45 }
46
47- for i < len(e.text[e.lineIdx]) && x < x0+e.width {
48- r := e.text[e.lineIdx][i]
49+ for i < len(text) && x < x0+e.width {
50+ r := text[i]
51 s := st
52 if e.backsearch && i < e.cursorIdx && i >= e.cursorIdx-len(e.backsearchPattern) {
53 s = s.Underline(true)
54@@ -533,8 +545,12 @@ func (e *Editor) Draw(screen tcell.Screen, x0, y int) {
55 }
56 }
57
58- cursorX := x0 + e.textWidth[e.cursorIdx] - e.textWidth[e.offsetIdx]
59- screen.ShowCursor(cursorX, y)
60+ if showCursor {
61+ cursorX := x0 + e.textWidth[e.cursorIdx] - e.textWidth[e.offsetIdx]
62+ screen.ShowCursor(cursorX, y)
63+ } else {
64+ screen.HideCursor()
65+ }
66 }
67
68 // runeOffset returns the lowercase version of a rune
+5,
-5
1@@ -51,7 +51,7 @@ func assertEditorEq(t *testing.T, actual, expected Editor) {
2 }
3
4 func TestOneLetter(t *testing.T) {
5- e := NewEditor(nil)
6+ e := NewEditor(ConfigColors{}, nil)
7 e.Resize(5)
8 e.PutRune('h')
9 assertEditorEq(t, e, Editor{
10@@ -64,7 +64,7 @@ func TestOneLetter(t *testing.T) {
11 }
12
13 func TestFourLetters(t *testing.T) {
14- e := NewEditor(nil)
15+ e := NewEditor(ConfigColors{}, nil)
16 e.Resize(5)
17 e.PutRune('h')
18 e.PutRune('e')
19@@ -74,7 +74,7 @@ func TestFourLetters(t *testing.T) {
20 }
21
22 func TestOneLeft(t *testing.T) {
23- e := NewEditor(nil)
24+ e := NewEditor(ConfigColors{}, nil)
25 e.Resize(5)
26 e.PutRune('h')
27 e.PutRune('l')
28@@ -86,7 +86,7 @@ func TestOneLeft(t *testing.T) {
29 }
30
31 func TestOneRem(t *testing.T) {
32- e := NewEditor(nil)
33+ e := NewEditor(ConfigColors{}, nil)
34 e.Resize(5)
35 e.PutRune('h')
36 e.PutRune('l')
37@@ -98,7 +98,7 @@ func TestOneRem(t *testing.T) {
38 }
39
40 func TestLeftAndRem(t *testing.T) {
41- e := NewEditor(nil)
42+ e := NewEditor(ConfigColors{}, nil)
43 e.Resize(5)
44 e.PutRune('h')
45 e.PutRune('l')
M
ui/ui.go
+23,
-12
1@@ -6,8 +6,9 @@ import (
2 "sync/atomic"
3 "time"
4
5- "git.sr.ht/~delthas/senpai/irc"
6 "github.com/gdamore/tcell/v2"
7+
8+ "git.sr.ht/~delthas/senpai/irc"
9 )
10
11 type Config struct {
12@@ -36,11 +37,12 @@ type UI struct {
13 exit atomic.Value // bool
14 config Config
15
16- bs BufferList
17- e Editor
18- prompt StyledString
19- status string
20- title string
21+ bs BufferList
22+ e Editor
23+ prompt StyledString
24+ status string
25+ title string
26+ overlayHint string
27
28 channelOffset int
29 memberClicked int
30@@ -97,7 +99,7 @@ func New(config Config) (ui *UI, err error) {
31 }()
32
33 ui.bs = NewBufferList(config.Colors, ui.config.MergeLine)
34- ui.e = NewEditor(ui.config.AutoComplete)
35+ ui.e = NewEditor(config.Colors, ui.config.AutoComplete)
36 ui.Resize()
37
38 return
39@@ -263,8 +265,9 @@ func (ui *UI) IsAtTop() bool {
40 return ui.bs.IsAtTop()
41 }
42
43-func (ui *UI) OpenOverlay() {
44+func (ui *UI) OpenOverlay(hint string) {
45 ui.bs.OpenOverlay()
46+ ui.overlayHint = hint
47 }
48
49 func (ui *UI) CloseOverlay() {
50@@ -489,22 +492,30 @@ func (ui *UI) Draw(members []irc.Member) {
51 ui.drawStatusBar(ui.channelWidth, h-2, w-ui.channelWidth-ui.memberWidth)
52 }
53
54+ prompt := ui.prompt
55+ if ui.bs.HasOverlay() && ui.e.TextLen() == 0 {
56+ prompt = Styled(">", tcell.StyleDefault.Foreground(ui.config.Colors.Prompt))
57+ }
58 if ui.channelWidth == 0 {
59 for x := 0; x < 9+ui.config.NickColWidth; x++ {
60 ui.screen.SetContent(x, h-2, ' ', nil, tcell.StyleDefault)
61 }
62- printIdent(ui.screen, 7, h-2, ui.config.NickColWidth, ui.prompt)
63+ printIdent(ui.screen, 7, h-2, ui.config.NickColWidth, prompt)
64 } else {
65 for x := ui.channelWidth; x < 9+ui.channelWidth+ui.config.NickColWidth; x++ {
66 ui.screen.SetContent(x, h-1, ' ', nil, tcell.StyleDefault)
67 }
68- printIdent(ui.screen, ui.channelWidth+7, h-1, ui.config.NickColWidth, ui.prompt)
69+ printIdent(ui.screen, ui.channelWidth+7, h-1, ui.config.NickColWidth, prompt)
70 }
71
72+ var hint string
73+ if ui.bs.HasOverlay() {
74+ hint = ui.overlayHint
75+ }
76 if ui.channelWidth == 0 {
77- ui.e.Draw(ui.screen, 9+ui.config.NickColWidth, h-2)
78+ ui.e.Draw(ui.screen, 9+ui.config.NickColWidth, h-2, hint)
79 } else {
80- ui.e.Draw(ui.screen, 9+ui.channelWidth+ui.config.NickColWidth, h-1)
81+ ui.e.Draw(ui.screen, 9+ui.channelWidth+ui.config.NickColWidth, h-1, hint)
82 }
83
84 ui.screen.Show()