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

fix: wrapping and height calculations #485

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ea80025
feat: set group with from size msg
bashbunni Nov 20, 2024
a748847
feat: handle WindowSizeMsg + base height on rendered content
bashbunni Nov 20, 2024
152c087
feat: render prefix before newlines
bashbunni Nov 20, 2024
ae0674b
feat: update viewport on resize, cursor != height so no scroll...
bashbunni Nov 20, 2024
c586d40
fix: always update viewport height
bashbunni Nov 21, 2024
18a0ca9
fix: add option prefix
bashbunni Nov 21, 2024
d206fc6
fix(multiselect): width calculation
bashbunni Nov 21, 2024
b41c5e7
fix: calculate offset from previous field heights
bashbunni Nov 22, 2024
98f01b6
feat: add calculate wrapping helper
bashbunni Nov 22, 2024
ea01c18
fix(select): wrap select field
bashbunni Nov 22, 2024
262ac14
feat: make group set field widths accounting for styles
bashbunni Nov 25, 2024
4ff8255
feat: set width
bashbunni Nov 26, 2024
c5e8fb8
feat: stack buttons in narrow window
bashbunni Nov 26, 2024
b1e6469
feat: add Theme function to Fields
bashbunni Nov 26, 2024
ec82a9b
feat: calculate width based on field theme
bashbunni Nov 26, 2024
75664f1
fix(group): fix height calculation
bashbunni Nov 26, 2024
70db481
feat: remove FullWidth helper (not needed)
bashbunni Nov 26, 2024
c9016a4
fix(group): fix off by one at top for overflowed content
bashbunni Nov 26, 2024
5164a77
fix(group): add gap height to calculations
bashbunni Nov 26, 2024
682d2c7
fix: don't wrap inline content
bashbunni Nov 26, 2024
f8b3922
Revert "fix: calculate offset from previous field heights"
bashbunni Nov 27, 2024
d5b26eb
Merge branch 'main' into wrap-group-scope
bashbunni Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 38 additions & 24 deletions field_confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,47 +220,68 @@
}

func (c *Confirm) activeStyles() *FieldStyles {
theme := c.theme
if theme == nil {
theme = ThemeCharm()
}
theme := c.Theme()
if c.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (c *Confirm) Theme() *Theme {
theme := c.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}

// View renders the confirm field.
func (c *Confirm) View() string {
styles := c.activeStyles()

var sb strings.Builder
sb.WriteString(styles.Title.Render(c.title.val))
if c.inline {
sb.WriteString(styles.Title.Render(c.title.val))
} else {
sb.WriteString(styles.Title.Width(c.width).Render(c.title.val))
}

if c.err != nil {
sb.WriteString(styles.ErrorIndicator.String())
}

description := styles.Description.Render(c.description.val)

if !c.inline && (c.description.val != "" || c.description.fn != nil) {
sb.WriteString("\n")
}
sb.WriteString(description)

if !c.inline {
sb.WriteString(styles.Description.Width(c.width).Render(c.description.val))
sb.WriteString("\n")
sb.WriteString("\n")
} else {
sb.WriteString(styles.Description.Render(c.description.val))
}

var negative string
var affirmative string

// Calculate padding to make buttons equal. The narrowest button has padding
// added to its right.
negativePad := styles.BlurredButton.GetHorizontalPadding() / 2

Check failure on line 270 in field_confirm.go

View workflow job for this annotation

GitHub Actions / lint-soft

Magic number: 2, in <operation> detected (mnd)
affirmativePad := styles.BlurredButton.GetHorizontalPadding() / 2

Check failure on line 271 in field_confirm.go

View workflow job for this annotation

GitHub Actions / lint-soft

Magic number: 2, in <operation> detected (mnd)
if lipgloss.Width(c.negative) < lipgloss.Width(c.affirmative) {
negativePad += (lipgloss.Width(c.affirmative) - lipgloss.Width(c.negative))
} else if lipgloss.Width(c.affirmative) < lipgloss.Width(c.negative) {
affirmativePad += (lipgloss.Width(c.negative) - lipgloss.Width(c.affirmative))
}

if c.negative != "" {
if c.accessor.Get() {
affirmative = styles.FocusedButton.Render(c.affirmative)
negative = styles.BlurredButton.Render(c.negative)
affirmative = styles.FocusedButton.PaddingRight(affirmativePad).Render(c.affirmative)
negative = styles.BlurredButton.PaddingRight(negativePad).Render(c.negative)
} else {
affirmative = styles.BlurredButton.Render(c.affirmative)
negative = styles.FocusedButton.Render(c.negative)
affirmative = styles.BlurredButton.PaddingRight(affirmativePad).Render(c.affirmative)
negative = styles.FocusedButton.PaddingRight(negativePad).Render(c.negative)
}
c.keymap.Reject.SetHelp("n", c.negative)
} else {
Expand All @@ -269,20 +290,13 @@
}

c.keymap.Accept.SetHelp("y", c.affirmative)

buttonsRow := lipgloss.JoinHorizontal(c.buttonAlignment, affirmative, negative)

promptWidth := lipgloss.Width(sb.String())
buttonsWidth := lipgloss.Width(buttonsRow)

renderWidth := promptWidth
if buttonsWidth > renderWidth {
renderWidth = buttonsWidth
// Align the buttons vertically if the window is too narrow.
if c.width < lipgloss.Width(buttonsRow) {
buttonsRow = lipgloss.JoinVertical(lipgloss.Left, affirmative, negative)
}

style := lipgloss.NewStyle().Width(renderWidth).Align(c.buttonAlignment)

sb.WriteString(style.Render(buttonsRow))
sb.WriteString(buttonsRow)
return styles.Base.Render(sb.String())
}

Expand Down
14 changes: 10 additions & 4 deletions field_filepicker.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,22 @@ func (f *FilePicker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (f *FilePicker) activeStyles() *FieldStyles {
theme := f.theme
if theme == nil {
theme = ThemeCharm()
}
theme := f.Theme()
if f.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (f *FilePicker) Theme() *Theme {
theme := f.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}

// View renders the file field.
func (f *FilePicker) View() string {
styles := f.activeStyles()
Expand Down
22 changes: 16 additions & 6 deletions field_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,16 +366,22 @@ func (i *Input) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (i *Input) activeStyles() *FieldStyles {
theme := i.theme
if theme == nil {
theme = ThemeCharm()
}
theme := i.Theme()
if i.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (i *Input) Theme() *Theme {
theme := i.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}

// View renders the input field.
func (i *Input) View() string {
styles := i.activeStyles()
Expand All @@ -396,15 +402,19 @@ func (i *Input) View() string {

var sb strings.Builder
if i.title.val != "" || i.title.fn != nil {
sb.WriteString(styles.Title.Render(i.title.val))
if !i.inline {
sb.WriteString(styles.Title.Width(i.width).Render(i.title.val))
sb.WriteString("\n")
} else {
sb.WriteString(styles.Title.Render(i.title.val))
}
}
if i.description.val != "" || i.description.fn != nil {
sb.WriteString(styles.Description.Render(i.description.val))
if !i.inline {
sb.WriteString(styles.Description.Width(i.width).Render(i.description.val))
sb.WriteString("\n")
} else {
sb.WriteString(styles.Description.Render(i.description.val))
}
}
sb.WriteString(i.textinput.View())
Expand Down
28 changes: 18 additions & 10 deletions field_multiselect.go
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ func (m *MultiSelect[T]) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m *MultiSelect[T]) updateViewportHeight() {
// If no height is set size the viewport to the number of options.
if m.height <= 0 {
m.viewport.Height = len(m.options.val)
m.viewport.Height = lipgloss.Height(m.optionsView())
return
}

Expand Down Expand Up @@ -501,16 +501,22 @@ func (m *MultiSelect[T]) updateValue() {
}

func (m *MultiSelect[T]) activeStyles() *FieldStyles {
theme := m.theme
if theme == nil {
theme = ThemeCharm()
}
theme := m.Theme()
if m.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (m *MultiSelect[T]) Theme() *Theme {
theme := m.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}

func (m *MultiSelect[T]) titleView() string {
if m.title.val == "" {
return ""
Expand All @@ -522,9 +528,9 @@ func (m *MultiSelect[T]) titleView() string {
if m.filtering {
sb.WriteString(m.filter.View())
} else if m.filter.Value() != "" {
sb.WriteString(styles.Title.Render(m.title.val) + styles.Description.Render("/"+m.filter.Value()))
sb.WriteString(styles.Title.Width(m.width).Render(m.title.val) + styles.Description.Render("/"+m.filter.Value()))
} else {
sb.WriteString(styles.Title.Render(m.title.val))
sb.WriteString(styles.Title.Width(m.width).Render(m.title.val))
}
if m.err != nil {
sb.WriteString(styles.ErrorIndicator.String())
Expand All @@ -533,7 +539,7 @@ func (m *MultiSelect[T]) titleView() string {
}

func (m *MultiSelect[T]) descriptionView() string {
return m.activeStyles().Description.Render(m.description.val)
return m.activeStyles().Description.Width(m.width).Render(m.description.val)
}

func (m *MultiSelect[T]) optionsView() string {
Expand All @@ -555,13 +561,15 @@ func (m *MultiSelect[T]) optionsView() string {
} else {
sb.WriteString(strings.Repeat(" ", lipgloss.Width(c)))
}
// Calculate width constraints.
contentWidth := m.width - lipgloss.Width(c) - max(lipgloss.Width(styles.SelectedPrefix.String()), lipgloss.Width(styles.UnselectedPrefix.String()))

if m.filteredOptions[i].selected {
sb.WriteString(styles.SelectedPrefix.String())
sb.WriteString(styles.SelectedOption.Render(option.Key))
sb.WriteString(styles.SelectedOption.Width(contentWidth).Render(option.Key))
} else {
sb.WriteString(styles.UnselectedPrefix.String())
sb.WriteString(styles.UnselectedOption.Render(option.Key))
sb.WriteString(styles.UnselectedOption.Width(contentWidth).Render(option.Key))
}
if i < len(m.options.val)-1 {
sb.WriteString("\n")
Expand Down
14 changes: 10 additions & 4 deletions field_note.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,22 @@ func (n *Note) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}

func (n *Note) activeStyles() *FieldStyles {
theme := n.theme
if theme == nil {
theme = ThemeCharm()
}
theme := n.Theme()
if n.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (n *Note) Theme() *Theme {
theme := n.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}

// View renders the note field.
func (n *Note) View() string {
styles := n.activeStyles()
Expand Down
32 changes: 22 additions & 10 deletions field_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ func (s *Select[T]) updateValue() {
func (s *Select[T]) updateViewportHeight() {
// If no height is set size the viewport to the number of options.
if s.height <= 0 {
s.viewport.Height = len(s.options.val)
s.viewport.Height = lipgloss.Height(s.optionsView())
return
}

Expand All @@ -514,16 +514,22 @@ func (s *Select[T]) updateViewportHeight() {
}

func (s *Select[T]) activeStyles() *FieldStyles {
theme := s.theme
if theme == nil {
theme = ThemeCharm()
}
theme := s.Theme()
if s.focused {
return &theme.Focused
}
return &theme.Blurred
}

// Theme returns the theme of the field.
func (s *Select[T]) Theme() *Theme {
theme := s.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}
Comment on lines +526 to +531
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
theme := s.theme
if theme == nil {
theme = ThemeCharm()
}
return theme
}
if s.theme != nil {
return s.theme
}
return ThemeCharm()
}

feels a bit better I think 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so true, I can update this for all fields


func (s *Select[T]) titleView() string {
var (
styles = s.activeStyles()
Expand All @@ -532,9 +538,11 @@ func (s *Select[T]) titleView() string {
if s.filtering {
sb.WriteString(s.filter.View())
} else if s.filter.Value() != "" && !s.inline {
sb.WriteString(styles.Title.Render(s.title.val) + styles.Description.Render("/"+s.filter.Value()))
} else {
sb.WriteString(styles.Title.Width(s.width).Render(s.title.val) + styles.Description.Width(s.width).Render("/"+s.filter.Value()))
} else if s.inline {
sb.WriteString(styles.Title.Render(s.title.val))
} else {
sb.WriteString(styles.Title.Width(s.width).Render(s.title.val))
}
if s.err != nil {
sb.WriteString(styles.ErrorIndicator.String())
Expand All @@ -543,7 +551,10 @@ func (s *Select[T]) titleView() string {
}

func (s *Select[T]) descriptionView() string {
return s.activeStyles().Description.Render(s.description.val)
if s.inline {
return s.activeStyles().Description.Render(s.description.val)
}
return s.activeStyles().Description.Width(s.width).Render(s.description.val)
}

func (s *Select[T]) optionsView() string {
Expand All @@ -552,6 +563,7 @@ func (s *Select[T]) optionsView() string {
c = styles.SelectSelector.String()
sb strings.Builder
)
width := s.width - lipgloss.Width(c) - max(lipgloss.Width(styles.SelectedOption.String()), lipgloss.Width(styles.UnselectedOption.String()))

if s.options.loading && time.Since(s.options.loadingStart) > spinnerShowThreshold {
s.spinner.Style = s.activeStyles().MultiSelectSelector.UnsetString()
Expand All @@ -572,9 +584,9 @@ func (s *Select[T]) optionsView() string {

for i, option := range s.filteredOptions {
if s.selected == i {
sb.WriteString(c + styles.SelectedOption.Render(option.Key))
sb.WriteString(c + styles.SelectedOption.Width(width).Render(option.Key))
} else {
sb.WriteString(strings.Repeat(" ", lipgloss.Width(c)) + styles.UnselectedOption.Render(option.Key))
sb.WriteString(strings.Repeat(" ", lipgloss.Width(c)) + styles.UnselectedOption.Width(width).Render(option.Key))
}
if i < len(s.options.val)-1 {
sb.WriteString("\n")
Expand Down
Loading
Loading