~taiite/senpai

db5a4b730f72a66c60074a0854f64f05c1bf6c1d — delthas a month ago 82acaac
Show/hide the channel & member list with F7/F8

This patch is inspired and modified from a patch by mooff.

Also this switches the default configuration to *display* the channel &
member list by default.
4 files changed, 123 insertions(+), 60 deletions(-)

M app.go
M config.go
M doc/senpai.1.scd
M ui/ui.go
M app.go => app.go +17 -11
@@ 122,9 122,11 @@ func NewApp(cfg Config) (app *App, err error) {
	mouse := cfg.Mouse

	app.win, err = ui.New(ui.Config{
		NickColWidth:   cfg.NickColWidth,
		ChanColWidth:   cfg.ChanColWidth,
		MemberColWidth: cfg.MemberColWidth,
		NickColWidth:     cfg.NickColWidth,
		ChanColWidth:     cfg.ChanColWidth,
		ChanColEnabled:   cfg.ChanColEnabled,
		MemberColWidth:   cfg.MemberColWidth,
		MemberColEnabled: cfg.MemberColEnabled,
		AutoComplete: func(cursorIdx int, text []rune) []ui.Completion {
			return app.completions(cursorIdx, text)
		},


@@ 432,9 434,9 @@ func (app *App) handleMouseEvent(ev *tcell.EventMouse) {
	x, y := ev.Position()
	w, _ := app.win.Size()
	if ev.Buttons()&tcell.WheelUp != 0 {
		if x < app.cfg.ChanColWidth {
		if x < app.win.ChannelWidth() {
			app.win.ScrollChannelUpBy(4)
		} else if x > w-app.cfg.MemberColWidth {
		} else if x > w-app.win.MemberWidth() {
			app.win.ScrollMemberUpBy(4)
		} else {
			app.win.ScrollUpBy(4)


@@ 442,27 444,27 @@ func (app *App) handleMouseEvent(ev *tcell.EventMouse) {
		}
	}
	if ev.Buttons()&tcell.WheelDown != 0 {
		if x < app.cfg.ChanColWidth {
		if x < app.win.ChannelWidth() {
			app.win.ScrollChannelDownBy(4)
		} else if x > w-app.cfg.MemberColWidth {
		} else if x > w-app.win.MemberWidth() {
			app.win.ScrollMemberDownBy(4)
		} else {
			app.win.ScrollDownBy(4)
		}
	}
	if ev.Buttons()&tcell.ButtonPrimary != 0 {
		if x < app.cfg.ChanColWidth {
		if x < app.win.ChannelWidth() {
			app.win.ClickBuffer(y + app.win.ChannelOffset())
		} else if x > w-app.cfg.MemberColWidth {
		} else if x > w-app.win.MemberWidth() {
			app.win.ClickMember(y + app.win.MemberOffset())
		}
	}
	if ev.Buttons() == 0 {
		if x < app.cfg.ChanColWidth {
		if x < app.win.ChannelWidth() {
			if i := y + app.win.ChannelOffset(); i == app.win.ClickedBuffer() {
				app.win.GoToBufferNo(i)
			}
		} else if x > w-app.cfg.MemberColWidth {
		} else if x > w-app.win.MemberWidth() {
			if i := y + app.win.MemberOffset(); i == app.win.ClickedMember() {
				netID, target := app.win.CurrentBuffer()
				s := app.sessions[netID]


@@ 580,6 582,10 @@ func (app *App) handleKeyEvent(ev *tcell.EventKey) {
		}
	case tcell.KeyEscape:
		app.win.CloseOverlay()
	case tcell.KeyF7:
		app.win.ToggleChannelList()
	case tcell.KeyF8:
		app.win.ToggleMemberList()
	case tcell.KeyCR, tcell.KeyLF:
		netID, buffer := app.win.CurrentBuffer()
		input := app.win.InputEnter()

M config.go => config.go +41 -27
@@ 62,11 62,13 @@ type Config struct {
	Typings bool
	Mouse   bool

	Highlights      []string
	OnHighlightPath string
	NickColWidth    int
	ChanColWidth    int
	MemberColWidth  int
	Highlights       []string
	OnHighlightPath  string
	NickColWidth     int
	ChanColWidth     int
	ChanColEnabled   bool
	MemberColWidth   int
	MemberColEnabled bool

	Colors ConfigColors



@@ 83,20 85,22 @@ func DefaultHighlightPath() (string, error) {

func Defaults() (cfg Config, err error) {
	cfg = Config{
		Addr:            "",
		Nick:            "",
		Real:            "",
		User:            "",
		Password:        nil,
		TLS:             true,
		Channels:        nil,
		Typings:         true,
		Mouse:           true,
		Highlights:      nil,
		OnHighlightPath: "",
		NickColWidth:    16,
		ChanColWidth:    0,
		MemberColWidth:  0,
		Addr:             "",
		Nick:             "",
		Real:             "",
		User:             "",
		Password:         nil,
		TLS:              true,
		Channels:         nil,
		Typings:          true,
		Mouse:            true,
		Highlights:       nil,
		OnHighlightPath:  "",
		NickColWidth:     14,
		ChanColWidth:     16,
		ChanColEnabled:   true,
		MemberColWidth:   16,
		MemberColEnabled: true,
		Colors: ConfigColors{
			Prompt: Color(tcell.ColorDefault),
		},


@@ 204,23 208,33 @@ func unmarshal(filename string, cfg *Config) (err error) {
						return err
					}
				case "channels":
					var channels string
					if err := child.ParseParams(&channels); err != nil {
					var channelsStr string
					if err := child.ParseParams(&channelsStr); err != nil {
						return err
					}

					if cfg.ChanColWidth, err = strconv.Atoi(channels); err != nil {
					channels, err := strconv.Atoi(channelsStr)
					if err != nil {
						return err
					}
					if channels == 0 {
						cfg.ChanColEnabled = false
					} else {
						cfg.ChanColWidth = channels
					}
				case "members":
					var members string
					if err := child.ParseParams(&members); err != nil {
					var membersStr string
					if err := child.ParseParams(&membersStr); err != nil {
						return err
					}

					if cfg.MemberColWidth, err = strconv.Atoi(members); err != nil {
					members, err := strconv.Atoi(membersStr)
					if err != nil {
						return err
					}
					if members == 0 {
						cfg.MemberColEnabled = false
					} else {
						cfg.MemberColWidth = members
					}
				default:
					return fmt.Errorf("unknown directive %q", child.Name)
				}

M doc/senpai.1.scd => doc/senpai.1.scd +6 -0
@@ 109,6 109,12 @@ of messages are in the timeline:
*CTRL-L*
	Refresh the window.

*F7*
	Show/hide the vertical channel list.

*F8*
	Show/hide the vertical member list.

# COMMANDS

If you type and send a message that starts with a slash (*/*), it will instead

M ui/ui.go => ui/ui.go +59 -22
@@ 11,12 11,14 @@ import (
)

type Config struct {
	NickColWidth   int
	ChanColWidth   int
	MemberColWidth int
	AutoComplete   func(cursorIdx int, text []rune) []Completion
	Mouse          bool
	MergeLine      func(former *Line, addition Line)
	NickColWidth     int
	ChanColWidth     int
	ChanColEnabled   bool
	MemberColWidth   int
	MemberColEnabled bool
	AutoComplete     func(cursorIdx int, text []rune) []Completion
	Mouse            bool
	MergeLine        func(former *Line, addition Line)
}

type UI struct {


@@ 33,12 35,21 @@ type UI struct {
	channelOffset int
	memberClicked int
	memberOffset  int

	channelWidth int
	memberWidth  int
}

func New(config Config) (ui *UI, err error) {
	ui = &UI{
		config: config,
	}
	if config.ChanColEnabled {
		ui.channelWidth = config.ChanColWidth
	}
	if config.MemberColEnabled {
		ui.memberWidth = config.MemberColWidth
	}

	ui.screen, err = tcell.NewScreen()
	if err != nil {


@@ 177,6 188,32 @@ func (ui *UI) MemberOffset() int {
	return ui.memberOffset
}

func (ui *UI) ChannelWidth() int {
	return ui.channelWidth
}

func (ui *UI) MemberWidth() int {
	return ui.memberWidth
}

func (ui *UI) ToggleChannelList() {
	if ui.channelWidth == 0 {
		ui.channelWidth = ui.config.ChanColWidth
	} else {
		ui.channelWidth = 0
	}
	ui.Resize()
}

func (ui *UI) ToggleMemberList() {
	if ui.memberWidth == 0 {
		ui.memberWidth = ui.config.MemberColWidth
	} else {
		ui.memberWidth = 0
	}
	ui.Resize()
}

func (ui *UI) ScrollMemberUpBy(n int) {
	ui.memberOffset -= n
	if ui.memberOffset < 0 {


@@ 353,9 390,9 @@ func (ui *UI) InputBackSearch() {

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


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

	if ui.config.ChanColWidth == 0 {
	if ui.channelWidth == 0 {
		ui.e.Draw(ui.screen, 9+ui.config.NickColWidth, h-2)
	} else {
		ui.e.Draw(ui.screen, 9+ui.config.ChanColWidth+ui.config.NickColWidth, h-1)
		ui.e.Draw(ui.screen, 9+ui.channelWidth+ui.config.NickColWidth, h-1)
	}

	ui.bs.DrawTimeline(ui.screen, ui.config.ChanColWidth, 0, ui.config.NickColWidth)
	if ui.config.ChanColWidth == 0 {
		ui.bs.DrawHorizontalBufferList(ui.screen, 0, h-1, w-ui.config.MemberColWidth)
	ui.bs.DrawTimeline(ui.screen, ui.channelWidth, 0, ui.config.NickColWidth)
	if ui.channelWidth == 0 {
		ui.bs.DrawHorizontalBufferList(ui.screen, 0, h-1, w-ui.memberWidth)
	} else {
		ui.bs.DrawVerticalBufferList(ui.screen, 0, 0, ui.config.ChanColWidth, h, &ui.channelOffset)
		ui.bs.DrawVerticalBufferList(ui.screen, 0, 0, ui.channelWidth, h, &ui.channelOffset)
	}
	if ui.config.MemberColWidth != 0 {
		ui.drawVerticalMemberList(ui.screen, w-ui.config.MemberColWidth, 0, ui.config.MemberColWidth, h, members, &ui.memberOffset)
	if ui.memberWidth != 0 {
		ui.drawVerticalMemberList(ui.screen, w-ui.memberWidth, 0, ui.memberWidth, h, members, &ui.memberOffset)
	}
	if ui.config.ChanColWidth == 0 {
		ui.drawStatusBar(ui.config.ChanColWidth, h-3, w-ui.config.MemberColWidth)
	if ui.channelWidth == 0 {
		ui.drawStatusBar(ui.channelWidth, h-3, w-ui.memberWidth)
	} else {
		ui.drawStatusBar(ui.config.ChanColWidth, h-2, w-ui.config.ChanColWidth-ui.config.MemberColWidth)
		ui.drawStatusBar(ui.channelWidth, h-2, w-ui.channelWidth-ui.memberWidth)
	}

	if ui.config.ChanColWidth == 0 {
	if ui.channelWidth == 0 {
		for x := 0; x < 9+ui.config.NickColWidth; x++ {
			ui.screen.SetContent(x, h-2, ' ', nil, tcell.StyleDefault)
		}
		printIdent(ui.screen, 7, h-2, ui.config.NickColWidth, ui.prompt)
	} else {
		for x := ui.config.ChanColWidth; x < 9+ui.config.ChanColWidth+ui.config.NickColWidth; x++ {
		for x := ui.channelWidth; x < 9+ui.channelWidth+ui.config.NickColWidth; x++ {
			ui.screen.SetContent(x, h-1, ' ', nil, tcell.StyleDefault)
		}
		printIdent(ui.screen, ui.config.ChanColWidth+7, h-1, ui.config.NickColWidth, ui.prompt)
		printIdent(ui.screen, ui.channelWidth+7, h-1, ui.config.NickColWidth, ui.prompt)
	}

	ui.screen.Show()