package tui import ( "strings" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/ansi" ) func absInt(a int) int { if a >= 0 { return a } return -a } // StackLine 行堆叠,输出字符串的长度为down的长度,x表示up向右横移的字符数 func StackLine(up, down string, x int) string { lup, ldown := ansi.StringWidth(up), ansi.StringWidth(down) if x >= 0 { if x >= ldown { return down } result := ansi.Truncate(down, x, "") if x+lup >= ldown { result += ansi.Truncate(up, ldown-x, "") } else { result += up result += ansi.TruncateLeft(down, x+lup, "") } return result } else { if absInt(x) >= lup { return down } result := ansi.TruncateLeft(up, absInt(x), "") l := ansi.StringWidth(result) if l < ldown { result += ansi.TruncateLeft(down, lup+x, "") } else { result = ansi.Truncate(result, ldown, "") } return result } } // Stack 堆叠两个字符串,输出的字符串尺寸与down的尺寸一致 func Stack(up, down string, x, y int) string { downHeight := lipgloss.Height(down) downLines := strings.Split(strings.Trim(down, "\n"), "\n") upLines := strings.Split(strings.Trim(up, "\n"), "\n") result := make([]string, downHeight) if y >= 0 { if y >= downHeight { result = downLines } else { for k, v := range downLines { if k-y >= 0 && k-y < len(upLines) { result[k] = StackLine(upLines[k-y], v, x) } else { result[k] = downLines[k] } } } } else { if absInt(y) > len(upLines) { result = downLines } else { for k, v := range downLines { if k-y < len(upLines) { result[k] = StackLine(upLines[k-y], v, x) } else { result[k] = downLines[k] } } } } r := strings.Join(result, "\n") if down[len(down)-1] == '\n' { r += "\n" } return r } // StackPosition 按照位置堆叠 func StackPosition(up, down string, vPos, hPos lipgloss.Position) string { upW, upH := lipgloss.Size(up) downW, downH := lipgloss.Size(down) switch vPos { case lipgloss.Top: switch hPos { case lipgloss.Left: // 上左 return Stack(up, down, 0, 0) case lipgloss.Center: // 上中 return Stack(up, down, (downW-upW)/2, 0) case lipgloss.Right: // 上右 return Stack(up, down, downW-upW, 0) } case lipgloss.Center: switch hPos { case lipgloss.Left: // 中左 return Stack(up, down, 0, (downH-upH)/2) case lipgloss.Center: // 中中 return Stack(up, down, (downW-upW)/2, (downH-upH)/2) case lipgloss.Right: // 中右 return Stack(up, down, downW-upW, (downH-upH)/2) } case lipgloss.Bottom: switch hPos { case lipgloss.Left: // 下左 return Stack(up, down, 0, downH-upH) case lipgloss.Center: // 下中 return Stack(up, down, (downW-upW)/2, downH-upH) case lipgloss.Right: // 下右 return Stack(up, down, downW-upW, downH-upH) } } return "" } // FormatStr 格式化字符串,输出的字符串可视长度为length,hPos表示是左对齐还是右对齐 // str必须为单行字符串,没有\n func FormatStr(str string, length int, hPos lipgloss.Position) string { realLen := lipgloss.Width(str) if realLen < length { switch hPos { case lipgloss.Left: return str + strings.Repeat(" ", length-realLen) case lipgloss.Right: return strings.Repeat(" ", length-realLen) + str } } else if realLen > length { // 需要截断 switch hPos { case lipgloss.Left: // 左对齐 return ansi.Truncate(str, length, "") case lipgloss.Right: // 右对齐 return ansi.TruncateLeft(str, realLen-length, "") } } return str }