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

Table is scrolling before selected row reaches the top #428

Closed
dzeleniak opened this issue Nov 6, 2023 · 3 comments · Fixed by #429
Closed

Table is scrolling before selected row reaches the top #428

dzeleniak opened this issue Nov 6, 2023 · 3 comments · Fixed by #429

Comments

@dzeleniak
Copy link
Contributor

dzeleniak commented Nov 6, 2023

This is a copy of an issue created on bubbletea.

"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)
}

266444965-fa022f25-e8de-481a-8f6d-e5067b2072fb

@dzeleniak
Copy link
Contributor Author

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()
}

@maxatome
Copy link

maxatome commented Nov 6, 2023

Just as a note, #363 reports a similar problem but when scrolling down. I don't know if it is the same bug behind the scene, so just in case...

@dzeleniak
Copy link
Contributor Author

Thanks for the heads up! I'll take a look at this tomorrow.

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

Successfully merging a pull request may close this issue.

2 participants