package main import ( "citrons.xyz/talk/client/clipboard" "citrons.xyz/talk/client/window" "citrons.xyz/talk/tui" "citrons.xyz/talk/proto" "zgo.at/termfo/keys" "strings" ) func (a *application) getWin() window.Window { win := a.windowCache.Open(a.currentWindow) if win == nil { return emptyWindow {a.currentWindow} } return win } func (a *application) pushPrompt(p window.Prompt) { a.prompts = append(a.prompts, p) } func (a *application) removePrompt(p window.Prompt) { for i := len(a.prompts) - 1; i >= 0; i-- { if p != a.prompts[i] { continue } if i < len(a.prompts) - 1 { a.prompts = append(a.prompts[:i], a.prompts[i + 1:]...) } else { a.prompts = a.prompts[:i] } break } } func (a *application) getPrompt() window.Prompt { if len(a.prompts) > 0 { return a.prompts[len(a.prompts) - 1] } return a.getWin() } func (a *application) onInput(ev tui.Event) { tui.Selected = "input" win := a.getWin() prompt := a.getPrompt() prompt.Input().Update(ev) wm := ev.Key.WithoutMods() if wm > '0' && wm <= '9' && ev.Key & keys.Alt != 0 { i := wm - '1' if int(i) < len(a.channelList) { a.goTo(a.channelList[i].location) } } if ev.Key != 0 { switch cl := a.currentWindow.(type) { case channelLocation: switch w := a.windowCache.Get(cl).(type) { case (*channelWindow): w.fingersPresent() } } } buf := win.Buffer() scroll := tui.Size().Height - 5 switch ev.Key { case keys.PageUp: buf.Scroll(scroll) case keys.PageDown: buf.Scroll(-scroll) case keys.Enter: input := prompt.Input() if !input.IsEmpty() { is, text := isCommand(input.Text()) if !is { prompt.Send(text) } else { a.processCommand(text) input.SetText("") } } case 'c' | keys.Ctrl: sel := prompt.Input().Selection() if sel != "" { clipboard.Get().Copy(sel) } case 'v' | keys.Ctrl: if globalApp.activePaste == nil { globalApp.activePaste = clipboard.Get().Paste() } case 'x' | keys.Ctrl: sel := prompt.Input().Selection() if sel != "" { clipboard.Get().Copy(sel) prompt.Input().Write("") } case keys.Up | keys.Alt, keys.Down | keys.Alt: var dir int if ev.Key == keys.Up | keys.Alt { dir = -1 } else { dir = 1 } var loc channelLocation switch w := a.currentWindow.(type) { case channelLocation: loc = w } switch { case a.channelList.contains(loc): index := a.channelList.traverse(dir) if index >= 0 && index < a.channelList.Len() { a.channelScroll.Set(index - 5) break } fallthrough case a.dmList.contains(loc): index := a.dmList.traverse(dir) if index >= 0 && index < a.dmList.Len() { a.dmScroll.Set(index - 5) } else { index = a.channelList.traverse(dir) a.channelScroll.Set(index - 5) } default: if ev.Key == keys.Down | keys.Alt { a.channelList.traverse(0) a.channelScroll.Set(0) } else { a.dmList.traverse(-1) a.dmScroll.Set(a.dmList.Len() - 5) } } case 'p' | keys.Ctrl: a.traverseHistory(-1) case 'n' | keys.Ctrl: a.traverseHistory(1) case '0' | keys.Alt: a.goTo(cmdWindowLocation{}) case 'l' | keys.Ctrl: a.cmdWindow.clearPreview() } } func (a *application) onPaste(text string) { a.getPrompt().Input().Write(text) } func (a *application) logUserUpdate(uid string, update proto.Object) { u := a.cache.Get(uid) if u == nil { return } switch { case update.Fields["status"] != "": lastIndex++ a.cmdWindow.Buffer().Add(userStatusMsg { lastIndex, uid, u.Fields[""], update.Fields["status"], }) case update.Fields[""] != "": lastIndex++ a.cmdWindow.Buffer().Add(nameChangeMsg { lastIndex, uid, u.Fields[""], update.Fields[""], }) } } func (a *application) showNickBox() { tui.Push("username", tui.Box {Width: tui.TextSize, Height: tui.TextSize}) tui.Text("[", nil) name := a.getNick() name = string([]rune(name)[:8]) tui.Text(name, nil) tui.Text("] ", nil) tui.Pop() } func (a *application) showWindow() { tui.Push("window", tui.Box {Width: tui.Fill, Height: tui.Fill}) if a.currentWindow != (cmdWindowLocation{}) { a.cmdWindow.showPreview() } win := a.getWin() prompt := a.getPrompt() win.Buffer().Show("buffer") tui.Push("input border", tui.Box { Width: tui.Fill, Height: 1, Dir: tui.Left, Style: &tui.Style {Bg: tui.White, Fg: tui.Black}, }) if win.Buffer().ScrollPos() > 0 { tui.Push("", tui.Box {Width: tui.TextSize, Height: 1, NoWrap: true}) if win.Buffer().AtTop() { tui.Text("[TOP]", nil) } else { tui.Text("[MORE]", nil) } tui.Pop() } tui.Push("", tui.Box {Width: tui.Fill, Height: 1}) tui.Pop() tui.Push("status", tui.Box {Width: tui.TextSize, Height: 1, NoWrap: true}) prompt.ShowStatusLine() tui.Pop() tui.Pop() tui.Push("replying to", tui.Box {Width: tui.Fill, Height: tui.Children}) if prompt == win { win.ShowComposingReply() } tui.Pop() tui.Push("input container", tui.Box { Width: tui.Fill, Height: tui.Children, Dir: tui.Right, }) a.showNickBox() input := prompt.Input() priv := input.Private if strings.HasPrefix(input.Text(), "/") { input.Private = strings.HasPrefix(input.Text(), "/password ") } input.Show("input") input.Private = priv tui.Pop() tui.Pop() } func (a *application) showChannelLists() { tui.Push("", tui.Box { Width: tui.Children, Height: tui.Fill, Dir: tui.Down, }) a.channelList.show(&a.channelScroll, func(cl channelLocation) { switch { case tui.MenuOption("list"): w := globalApp.windowCache.Open(cl) switch w.(type) { case *channelWindow: w.(*channelWindow).userList(func(msg userListMsg) { globalApp.cmdWindow.Buffer().Add(msg) }) } case tui.MenuOption("leave"): w := globalApp.windowCache.Open(cl) switch w.(type) { case *channelWindow: defer w.(*channelWindow).leaveChannel() } if cl == globalApp.currentWindow { globalApp.goTo(cmdWindowLocation {}) } } }) tui.Push("", tui.Box {Width: 12, Height: 1}) tui.Text(strings.Repeat("─", 12), nil) tui.Pop() a.dmList.show(&a.dmScroll, func(cl channelLocation) {}) tui.Pop() } func (a *application) show() { s := tui.Size() tui.Push("", tui.Box { Width: tui.BoxSize(s.Width), Height: tui.BoxSize(s.Height), Dir: tui.Right, }) a.showChannelLists() tui.Push("border", tui.Box {Width: 1, Height: tui.Fill}) tui.Text(strings.Repeat("│", s.Height / 2), nil) tui.Text("┤", nil) tui.Text(strings.Repeat("│", s.Height / 2), nil) tui.Pop() a.showWindow() }