// Drawing the picker: the popup that lists the agent's commands or the // project's files while a "/" or "@" word is being typed. Local coordinates, // like everything else in view_draw.go. package acp import ( "strings" "rickub.com/turbo-editors/turbo-core/theme" "rickub.com/turbo-editors/turbo-core/ui" ) // drawPicker paints the popup just above the rule, over the bottom of the // conversation, when there is something to list. func (v *View) drawPicker(p *ui.Painter, th *theme.Theme) { choices := v.refresh() room := v.transcriptHeight() if len(choices) == 0 || room < 3 { v.picker.bounds = ui.Rect{} return } height := min(min(len(choices), pickerRows)+2, room) width := min(max(widestChoice(choices)+4, pickerMinWidth), p.Size().W) bounds := ui.Rect{X: 0, Y: room - height, W: width, H: height} v.picker.bounds = bounds frame := th.Style(theme.KeyCompletionFrame) for row := range height { p.HLine(bounds.X, bounds.Y+row, width, ' ', th.Style(theme.KeyCompletionItem)) } ui.DrawFrame(p, bounds, ui.FrameSingle, frame) p.TextLimited(bounds.X+2, bounds.Y, max(width-4, 0), v.pickerTitle(), frame) for row := range height - 2 { index := v.picker.top + row if index >= len(choices) { break } v.drawChoice(p, th, row, choices[index], index == v.picker.selected) } } // pickerTitle names what is being listed, on the frame. func (v *View) pickerTitle() string { if _, word := v.typedWord(); strings.HasPrefix(word, string(mentionRune)) { return " files " } return " commands " } // widestChoice is the width of the longest line the popup will show. func widestChoice(choices []Choice) int { widest := 0 for _, choice := range choices { widest = max(widest, len([]rune(choiceText(choice)))) } return widest } // choiceText is one line of the popup as plain text: the label, the // description, and the hint in angle brackets. func choiceText(choice Choice) string { text := choice.Label if choice.Detail != "" { text += " " + choice.Detail } if choice.Hint != "" { text += " <" + choice.Hint + ">" } return text } // drawChoice paints one line of the popup — row rows below the frame's top, // inside the bounds drawPicker just recorded: the label in the item colour, // the rest in the detail colour, the whole row highlighted when it is the one. func (v *View) drawChoice(p *ui.Painter, th *theme.Theme, row int, choice Choice, selected bool) { style := th.Style(theme.KeyCompletionItem) detail := th.Style(theme.KeyCompletionDetail) if selected { style = th.Style(theme.KeyCompletionSelected) detail = style } bounds := v.picker.bounds x, y := bounds.X+1, bounds.Y+1+row width := bounds.W - 2 p.HLine(x, y, width, ' ', style) end := p.TextLimited(x, y, width, choice.Label, style) rest := strings.TrimPrefix(choiceText(choice), choice.Label) if rest != "" && end < x+width { p.TextLimited(end, y, x+width-end, rest, detail) } }