~taiite/public-inbox

senpai: ui: Introduce a vertical member list on channels v1 APPLIED

Hubert Hirtz: 1
 ui: Introduce a vertical member list on channels
delthas: 1
 ui: Introduce a vertical member list on channels

 7 files changed, 83 insertions(+), 41 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~taiite/public-inbox/patches/23823/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH senpai] ui: Introduce a vertical member list on channels Export this patch

Also, fix the UI timeline clearing too much, as well as the status line.

Also, remove the width in the editor and buffer list constructors. We
were initializing them with wrong values, only to overwrite these values
with correct ones later in Resize().
---
 app.go           | 11 +++++++---
 config.go        | 12 +++++++----
 doc/senpai.5.scd |  7 ++++--
 ui/buffers.go    | 56 +++++++++++++++++++++++++++++++++---------------
 ui/editor.go     |  5 +++--
 ui/ui.go         | 29 +++++++++++++++----------
 6 files changed, 81 insertions(+), 39 deletions(-)

diff --git a/app.go b/app.go
index 4bd12db..4db9e88 100644
--- a/app.go
+++ b/app.go
@@ -60,8 +60,9 @@ func NewApp(cfg Config) (app *App, err error) {
	}

	app.win, err = ui.New(ui.Config{
		NickColWidth: cfg.NickColWidth,
		ChanColWidth: cfg.ChanColWidth,
		NickColWidth:   cfg.NickColWidth,
		ChanColWidth:   cfg.ChanColWidth,
		MemberColWidth: cfg.MemberColWidth,
		AutoComplete: func(cursorIdx int, text []rune) []ui.Completion {
			return app.completions(cursorIdx, text)
		},
@@ -116,7 +117,11 @@ func (app *App) eventLoop() {
		if !app.pasting {
			app.setStatus()
			app.updatePrompt()
			app.win.Draw()
			var currentMembers []irc.Member
			if app.s != nil {
				currentMembers = app.s.Names(app.win.CurrentBuffer())
			}
			app.win.Draw(currentMembers)
		}
	}
}
diff --git a/config.go b/config.go
index 10656f4..a2f70b9 100644
--- a/config.go
+++ b/config.go
@@ -58,10 +58,11 @@ type Config struct {
	NoTypings bool `yaml:"no-typings"`
	Mouse     *bool

	Highlights   []string
	OnHighlight  string `yaml:"on-highlight"`
	NickColWidth int    `yaml:"nick-column-width"`
	ChanColWidth int    `yaml:"chan-column-width"`
	Highlights     []string
	OnHighlight    string `yaml:"on-highlight"`
	NickColWidth   int    `yaml:"nick-column-width"`
	ChanColWidth   int    `yaml:"chan-column-width"`
	MemberColWidth int    `yaml:"member-column-width"`

	Colors struct {
		Prompt Color
@@ -93,6 +94,9 @@ func ParseConfig(buf []byte) (cfg Config, err error) {
	if cfg.ChanColWidth <= 0 {
		cfg.ChanColWidth = 16
	}
	if cfg.MemberColWidth <= 0 {
		cfg.MemberColWidth = 16
	}
	return
}

diff --git a/doc/senpai.5.scd b/doc/senpai.5.scd
index b3edd18..6ad15ba 100644
--- a/doc/senpai.5.scd
+++ b/doc/senpai.5.scd
@@ -72,11 +72,14 @@ on-highlight: |
```

*nick-column-width*
	The number of cell that the column for nicknames occupies in the timeline.
	The number of cells that the column for nicknames occupies in the timeline.
	By default, 16.

*chan-column-width*
	The number of cell that the column for channels occupies.  By default, 16.
	The number of cells that the column for channels occupies.  By default, 16.

*member-column-width*
	The number of cells that the column for members occupies.  By default, 16.

*no-tls*
	Disable TLS encryption.  Defaults to false.
diff --git a/ui/buffers.go b/ui/buffers.go
index 6de3923..a6a8231 100644
--- a/ui/buffers.go
+++ b/ui/buffers.go
@@ -4,6 +4,8 @@ import (
	"strings"
	"time"

	"git.sr.ht/~taiite/senpai/irc"

	"github.com/gdamore/tcell/v2"
)

@@ -177,30 +179,24 @@ type BufferList struct {
	current int
	clicked int

	tlWidth      int
	tlInnerWidth int
	tlHeight     int
	nickColWidth int
}

func NewBufferList(tlWidth, tlHeight, nickColWidth int) BufferList {
// NewBufferList returns a new BufferList.
// Call Resize() once before using it.
func NewBufferList() BufferList {
	return BufferList{
		list:         []buffer{},
		clicked:      -1,
		tlWidth:      tlWidth,
		tlHeight:     tlHeight,
		nickColWidth: nickColWidth,
		list:    []buffer{},
		clicked: -1,
	}
}

func (bs *BufferList) ResizeTimeline(tlWidth, tlHeight, nickColWidth int) {
	bs.tlWidth = tlWidth
func (bs *BufferList) ResizeTimeline(tlInnerWidth, tlHeight int) {
	bs.tlInnerWidth = tlInnerWidth
	bs.tlHeight = tlHeight
}

func (bs *BufferList) tlInnerWidth() int {
	return bs.tlWidth - bs.nickColWidth - 9
}

func (bs *BufferList) To(i int) {
	if 0 <= i {
		bs.current = i
@@ -276,7 +272,7 @@ func (bs *BufferList) AddLine(title string, highlight bool, line Line) {
		line.computeSplitPoints()
		b.lines = append(b.lines, line)
		if idx == bs.current && 0 < b.scrollAmt {
			b.scrollAmt += len(line.NewLines(bs.tlInnerWidth())) + 1
			b.scrollAmt += len(line.NewLines(bs.tlInnerWidth)) + 1
		}
	}

@@ -425,8 +421,34 @@ func (bs *BufferList) DrawVerticalBufferList(screen tcell.Screen, x0, y0, width,
	}
}

func (bs *BufferList) DrawVerticalMemberList(screen tcell.Screen, x0, y0, width, height int, members []irc.Member) {
	st := tcell.StyleDefault

	for y := y0; y < y0+height; y++ {
		for x := x0; x < x0+width; x++ {
			screen.SetContent(x, y, ' ', nil, st)
		}
		screen.SetContent(x0, y, 0x2502, nil, st)
	}

	for i, m := range members {
		st = tcell.StyleDefault
		x := x0 + 1
		y := y0 + i

		if m.PowerLevel != "" {
			printString(screen, &x, y, Styled(string([]rune(m.PowerLevel)[0]), st.Foreground(tcell.ColorGreen)))
		} else {
			x += 1
		}
		name := truncate(m.Name.Name, width-(x-x0), "\u2026")
		printString(screen, &x, y, Styled(name, st))
		y++
	}
}

func (bs *BufferList) DrawTimeline(screen tcell.Screen, x0, y0, nickColWidth int) {
	for x := x0; x < x0+bs.tlWidth; x++ {
	for x := x0; x < x0+bs.tlInnerWidth+nickColWidth+9; x++ {
		for y := y0; y < y0+bs.tlHeight; y++ {
			screen.SetContent(x, y, ' ', nil, tcell.StyleDefault)
		}
@@ -442,7 +464,7 @@ func (bs *BufferList) DrawTimeline(screen tcell.Screen, x0, y0, nickColWidth int
		x1 := x0 + 9 + nickColWidth

		line := &b.lines[i]
		nls := line.NewLines(bs.tlInnerWidth())
		nls := line.NewLines(bs.tlInnerWidth)
		yi -= len(nls) + 1
		if y0+bs.tlHeight <= yi {
			continue
diff --git a/ui/editor.go b/ui/editor.go
index a714587..7e8c96e 100644
--- a/ui/editor.go
+++ b/ui/editor.go
@@ -37,11 +37,12 @@ type Editor struct {
	autoCacheIdx int
}

func NewEditor(width int, autoComplete func(cursorIdx int, text []rune) []Completion) Editor {
// NewEditor returns a new Editor.
// Call Resize() once before using it.
func NewEditor(autoComplete func(cursorIdx int, text []rune) []Completion) Editor {
	return Editor{
		text:         [][]rune{{}},
		textWidth:    []int{0},
		width:        width,
		autoComplete: autoComplete,
	}
}
diff --git a/ui/ui.go b/ui/ui.go
index 03a0aee..e136229 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -5,14 +5,17 @@ import (
	"sync/atomic"
	"time"

	"git.sr.ht/~taiite/senpai/irc"

	"github.com/gdamore/tcell/v2"
)

type Config struct {
	NickColWidth int
	ChanColWidth int
	AutoComplete func(cursorIdx int, text []rune) []Completion
	Mouse        bool
	NickColWidth   int
	ChanColWidth   int
	MemberColWidth int
	AutoComplete   func(cursorIdx int, text []rune) []Completion
	Mouse          bool
}

type UI struct {
@@ -46,7 +49,7 @@ func New(config Config) (ui *UI, err error) {
	}
	ui.screen.EnablePaste()

	w, h := ui.screen.Size()
	_, h := ui.screen.Size()
	ui.screen.Clear()
	ui.screen.ShowCursor(0, h-2)

@@ -59,8 +62,8 @@ func New(config Config) (ui *UI, err error) {
		}
	}()

	ui.bs = NewBufferList(w, h, ui.config.NickColWidth)
	ui.e = NewEditor(w, ui.config.AutoComplete)
	ui.bs = NewBufferList()
	ui.e = NewEditor(ui.config.AutoComplete)
	ui.Resize()

	return
@@ -242,18 +245,20 @@ func (ui *UI) InputClear() bool {

func (ui *UI) Resize() {
	w, h := ui.screen.Size()
	ui.e.Resize(w - 9 - ui.config.ChanColWidth - ui.config.NickColWidth)
	ui.bs.ResizeTimeline(w-ui.config.ChanColWidth, h-2, ui.config.NickColWidth)
	innerWidth := w - 9 - ui.config.ChanColWidth - ui.config.NickColWidth - ui.config.MemberColWidth
	ui.e.Resize(innerWidth)
	ui.bs.ResizeTimeline(innerWidth, h-2)
}

func (ui *UI) Draw() {
func (ui *UI) Draw(members []irc.Member) {
	w, h := ui.screen.Size()

	ui.e.Draw(ui.screen, 9+ui.config.ChanColWidth+ui.config.NickColWidth, h-1)

	ui.bs.DrawTimeline(ui.screen, ui.config.ChanColWidth, 0, ui.config.NickColWidth)
	ui.bs.DrawVerticalBufferList(ui.screen, 0, 0, ui.config.ChanColWidth, h)
	ui.drawStatusBar(ui.config.ChanColWidth, h-2, w-ui.config.ChanColWidth)
	ui.bs.DrawVerticalMemberList(ui.screen, w-ui.config.MemberColWidth, 0, ui.config.MemberColWidth, h, members)
	ui.drawStatusBar(ui.config.ChanColWidth, h-2, w-ui.config.ChanColWidth-ui.config.MemberColWidth)

	for x := ui.config.ChanColWidth; x < 9+ui.config.ChanColWidth+ui.config.NickColWidth; x++ {
		ui.screen.SetContent(x, h-1, ' ', nil, tcell.StyleDefault)
@@ -264,6 +269,8 @@ func (ui *UI) Draw() {
}

func (ui *UI) drawStatusBar(x0, y, width int) {
	width--

	st := tcell.StyleDefault.Dim(true)

	for x := x0; x < x0+width; x++ {
-- 
2.30.0

Re: [PATCH senpai] ui: Introduce a vertical member list on channels Export this patch

Pushed with the following modification:

---
diff --git a/ui/buffers.go b/ui/buffers.go
index f89b642..94e2a4e 100644
--- a/ui/buffers.go
+++ b/ui/buffers.go
@@ -433,10 +433,10 @@ func (bs *BufferList) DrawVerticalMemberList(screen tcell.Screen, x0, y0, width,
       st := tcell.StyleDefault

       for y := y0; y < y0+height; y++ {
               for x := x0; x < x0+width; x++ {
               screen.SetContent(x0, y, 0x2502, nil, st)
               for x := x0 + 1; x < x0+width; x++ {
                       screen.SetContent(x, y, ' ', nil, st)
               }
               screen.SetContent(x0, y, 0x2502, nil, st)
       }

       for i, m := range members {