Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viewport is scrolling before selected row reaches the top #821

Closed
developer1105 opened this issue Sep 7, 2023 · 1 comment
Closed

Viewport is scrolling before selected row reaches the top #821

developer1105 opened this issue Sep 7, 2023 · 1 comment

Comments

@developer1105
Copy link

Hello,
Here is a code sample that uses the table component.
Something I noticed is that when I press the down arrow key, going
from the first item to the last, everything works as expected.
Going in the reverse direction, that is, going from the last item
to the first one (via up arrow) the viewport is scrolled down
before the selected line matches the top of the viewport. Check
the animated gif on the entry labeled as Poland.
What can be done so that going from the first item to last item
and the opposite way (last to first) behaves exactly the same way?

package main

import (
	"os"
	"fmt"
        "strings"

	"github.com/charmbracelet/bubbles/table"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
)

var selectedRepo string = ""

var baseStyle = lipgloss.NewStyle().
	BorderStyle(lipgloss.DoubleBorder()).
	BorderForeground(lipgloss.Color("56"))

type model struct {
	table  table.Model
	width  int
	height int
}

func (m model) Init() tea.Cmd { return nil }

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.width = msg.Width
		m.height = msg.Height	
	case tea.KeyMsg:
		switch msg.String() {
		case "esc", "q":
			return m, tea.Quit
		case "enter":
		    selectedRepo = m.table.SelectedRow()[0]
		    return m, tea.Quit
                }
	}
	m.table, cmd = m.table.Update(msg)
	return m, cmd
}

func (m model) View() string {
	if m.width == 0 { return "" }
	table := baseStyle.Render(m.table.View())
	return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, table)
}

func center(s string, w int) string {
    if len(s) >= w { return s }
    n := w - len(s)
    div := n / 2
    return strings.Repeat(" ", div) + s + strings.Repeat(" ", div)
}
 
func main() {
    centeredTitle := center("Press [ENTER] to choose repository or [ESC]/[Q] to quit", 71)
    
	columns := []table.Column{
		{Title: centeredTitle, Width: 71},
	}

	rows := []table.Row{
            {"                    Brazil  devuan.c3sl.ufpr.br"},
            {"                  Bulgaria  dev1.ipacct.com"},
            {"                  Bulgaria  devuan.ipacct.com/devuan"},
            {"                    Canada  mishka.snork.ca/devuan"},
            {"                     Chile  devuan.dcc.uchile.cl"},
            {"                   Denmark  mirrors.dotsrc.org/devuan"},
            {"                   England  devuan-mirror.thorcom.net"},
            {"                   Finland  devuan.packet-gain.de"},
            {"                    France  pkgmaster.devuan.org"},
            {"                   Germany  devuan.bio.lmu.de"},
            {"                   Germany  devuan.sedf.de"},
            {"                   Germany  dist-mirror.fem.tu-ilmenau.de/devuan"},
            {"                   Germany  ftp.fau.de/devuan"},
            {"                   Germany  mirror.checkdomain.de/devuan"},
            {"                   Germany  mirror.stinpriza.org/devuan"},
            {"                   Hungary  quantum-mirror.hu/mirrors/pub/devuan"},
            {"                     India  dev1.ipacct.in"},
            {"                     India  devuan.ipacct.in/devuan"},
            {"                     Japan  devuan.m10k.jp"},
            {"               Netherlands  mirror.koddos.net/devuan"},
            {"               Netherlands  mirror.vpgrp.io/devuan"},
            {"               Netherlands  sledjhamr.org/devuan"},
            {"               New Zealand  deb.devuan.nz"},
            {"                    Poland  devuan.krypto-it.pl/devuan/devuan"},
            {"                    Poland  devuan.sakamoto.pl/packages"},
            {"                     Spain  repo.ifca.es/devuan"},
            {"                    Sweden  devuan.keff.org"},
            {"               Switzerland  devuan.planetcobalt.net"},
            {"               Switzerland  mirror.ungleich.ch/mirror/packages/devuan"},
            {"                    Taiwan  tw1.mirror.blendbyte.net/devuan"},
            {"                   Ukraine  mirror.mirohost.net/devuan"},
            {"  United States of America  dev.beard.ly/devuan"},
            {"  United States of America  devuan.slipfox.xyz"},
            {"  United States of America  mirrors.ocf.berkeley.edu/devuan"},
            {"                   Uruguay  espejito.fder.edu.uy/devuan"},
	}

	t := table.New(
		table.WithColumns(columns),
		table.WithRows(rows),
		table.WithFocused(true),
		table.WithHeight(13),
	)

	s := table.DefaultStyles()
	s.Header = s.Header.
		BorderStyle(lipgloss.NormalBorder()).
		BorderForeground(lipgloss.Color("56")).
		BorderBottom(true).
		Bold(true)
	s.Selected = s.Selected.
		Foreground(lipgloss.Color("229")).
		Background(lipgloss.Color("13")).
		Bold(false)
	t.SetStyles(s)

	m := model{t, 0, 0}
	if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil {
		fmt.Println("Error running program:", err)
		os.Exit(1)
	}

    if selectedRepo == "" {
        fmt.Printf("No repository was chosen\n")
    	os.Exit(0)
    }

    var repo  string
    var parts []string
    repo  = strings.TrimLeft(selectedRepo, " ")
    parts = strings.Split(repo, "  ")
    repo  = parts[1]
    fmt.Printf("%s\n", repo)
}

Gif that describes it visually:
Scroll

@dzeleniak
Copy link

dzeleniak commented Nov 6, 2023

I have done a little digging on this issue.

It looks like the problem is in the bubbles package on the move-up command. Upon investigation it looks like the error is in the MoveUp function.

The following code fixes the issue.

bubbles/table/table.go

// MoveUp moves the selection up by any number of rows.
// It can not go above the first row.
func (m *Model) MoveUp(n int) {
	m.cursor = clamp(m.cursor-n, 0, len(m.rows)-1)
	switch {
	case m.start == 0:
		m.viewport.SetYOffset(clamp(m.viewport.YOffset, 0, m.cursor))
	case m.start < m.viewport.Height:
		m.viewport.YOffset = clamp(clamp(m.viewport.YOffset+n, 0, m.cursor), 0, m.viewport.Height)
	case m.viewport.YOffset >= 1:
		m.viewport.YOffset = clamp(m.viewport.YOffset+n, 1, m.viewport.Height)
	}
	m.UpdateViewport()
}

I will copy this issue over to the bubbles repo and open a PR to fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants