feat: wip tui updates
This commit is contained in:
parent
5d7e9d79c4
commit
dde2d98a84
5 changed files with 158 additions and 24 deletions
30
tui/app.go
30
tui/app.go
|
|
@ -1,6 +1,7 @@
|
||||||
package tui
|
package tui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
@ -11,13 +12,19 @@ type (
|
||||||
Size Size
|
Size Size
|
||||||
Header HeaderModel
|
Header HeaderModel
|
||||||
Screen tea.Model
|
Screen tea.Model
|
||||||
|
Screens []tea.Model
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewApp() AppModel {
|
func NewApp() AppModel {
|
||||||
|
screens := []tea.Model{
|
||||||
|
NewHomeScreen(),
|
||||||
|
NewNotificationScreen(),
|
||||||
|
}
|
||||||
return AppModel{
|
return AppModel{
|
||||||
Header: NewHeader(),
|
Header: NewHeader(),
|
||||||
Screen: NewHomeScreen(),
|
Screen: screens[0],
|
||||||
|
Screens: screens,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +37,11 @@ func (m AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.WindowSizeMsg:
|
case tea.WindowSizeMsg:
|
||||||
m.Size = NewSizeFromWindow(msg)
|
m.SetSize(msg.Width, msg.Height)
|
||||||
|
case SwitchHomeScreenMsg:
|
||||||
|
m.Screen = m.Screens[0]
|
||||||
|
case SwitchNotificationScreenMsg:
|
||||||
|
m.Screen = m.Screens[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Header, headerCmd = m.Header.Update(msg)
|
m.Header, headerCmd = m.Header.Update(msg)
|
||||||
|
|
@ -39,8 +50,23 @@ func (m AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, tea.Batch(cmd, headerCmd, screenCmd)
|
return m, tea.Batch(cmd, headerCmd, screenCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *AppModel) SetSize(width, height int) {
|
||||||
|
m.Size = NewSize(width, height)
|
||||||
|
m.Header.SetWidth(width)
|
||||||
|
screenHeight := height - m.Header.Size.Height
|
||||||
|
switch screen := m.Screen.(type) {
|
||||||
|
case Sized:
|
||||||
|
m.Screen = screen.SetSize(width, screenHeight)
|
||||||
|
case SizedWidth:
|
||||||
|
m.Screen = screen.SetWidth(width)
|
||||||
|
case SizedHeight:
|
||||||
|
m.Screen = screen.SetHeight(screenHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m AppModel) View() string {
|
func (m AppModel) View() string {
|
||||||
return strings.Join([]string{
|
return strings.Join([]string{
|
||||||
|
fmt.Sprintf("%d x %d", m.Size.Width, m.Size.Height),
|
||||||
m.Header.View(),
|
m.Header.View(),
|
||||||
m.Screen.View(),
|
m.Screen.View(),
|
||||||
}, "\n")
|
}, "\n")
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,15 @@ import (
|
||||||
|
|
||||||
type (
|
type (
|
||||||
HeaderModel struct{
|
HeaderModel struct{
|
||||||
|
Size Size
|
||||||
Tabs []HeaderTabModel
|
Tabs []HeaderTabModel
|
||||||
Width int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HeaderTabModel struct{
|
HeaderTabModel struct{
|
||||||
Current bool
|
Size Size
|
||||||
|
Active bool
|
||||||
Name string
|
Name string
|
||||||
|
Key rune
|
||||||
Unread int
|
Unread int
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -23,16 +25,16 @@ type (
|
||||||
func NewHeader() HeaderModel {
|
func NewHeader() HeaderModel {
|
||||||
return HeaderModel{
|
return HeaderModel{
|
||||||
Tabs: []HeaderTabModel {
|
Tabs: []HeaderTabModel {
|
||||||
NewHeaderTab("Home", 10, true),
|
NewHeaderTab("Home", 'H', 10, true),
|
||||||
NewHeaderTab("Notifications", 3, false),
|
NewHeaderTab("Notifications", 'N', 3, false),
|
||||||
NewHeaderTab("Lists", 5, false),
|
NewHeaderTab("Lists", 'L', 5, false),
|
||||||
NewHeaderTab("Private", 0, false),
|
NewHeaderTab("Private", 'P', 0, false),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeaderTab(name string, unread int, current bool) HeaderTabModel {
|
func NewHeaderTab(name string, key rune, unread int, current bool) HeaderTabModel {
|
||||||
return HeaderTabModel{ Name: name, Unread: unread, Current: current }
|
return HeaderTabModel{ Name: name, Key: key, Unread: unread, Active: current }
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -59,6 +61,7 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
headerTabStyle = lipgloss.NewStyle().
|
headerTabStyle = lipgloss.NewStyle().
|
||||||
|
Padding(0, 1).
|
||||||
Border(tabBorder, true)
|
Border(tabBorder, true)
|
||||||
|
|
||||||
activeHeaderTabStyle = headerTabStyle.
|
activeHeaderTabStyle = headerTabStyle.
|
||||||
|
|
@ -72,43 +75,80 @@ func (m HeaderModel) Init() tea.Cmd {
|
||||||
func (m HeaderModel) Update(msg tea.Msg) (HeaderModel, tea.Cmd) {
|
func (m HeaderModel) Update(msg tea.Msg) (HeaderModel, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg.(type) {
|
||||||
case Size:
|
case SwitchHomeScreenMsg:
|
||||||
m.Width = msg.Width
|
m.SetActive(0)
|
||||||
|
case SwitchNotificationScreenMsg:
|
||||||
|
m.SetActive(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *HeaderModel) SetActive(tabIndex int) {
|
||||||
|
for i := range m.Tabs {
|
||||||
|
m.Tabs[i].Active = i == tabIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HeaderModel) SetWidth(width int) {
|
||||||
|
tabWidth := width / len(m.Tabs)
|
||||||
|
var maxTabHeight int
|
||||||
|
for i := range m.Tabs {
|
||||||
|
m.Tabs[i].SetSize(tabWidth)
|
||||||
|
maxTabHeight = max(maxTabHeight, m.Tabs[i].Size.Height)
|
||||||
|
}
|
||||||
|
m.Size = Size{
|
||||||
|
Width: width,
|
||||||
|
Height: 3+maxTabHeight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (m HeaderModel) View() string {
|
func (m HeaderModel) View() string {
|
||||||
header := lipgloss.NewStyle().
|
header := lipgloss.NewStyle().
|
||||||
Padding(1, 0).
|
Padding(1, 0).
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
Width(m.Width)
|
Width(m.Size.Width)
|
||||||
|
|
||||||
renderedTabs := make([]string, len(m.Tabs))
|
renderedTabs := make([]string, len(m.Tabs))
|
||||||
for i, tab := range m.Tabs {
|
for i, tab := range m.Tabs {
|
||||||
renderedTabs[i] = tab.View()
|
renderedTabs[i] = tab.View()
|
||||||
}
|
}
|
||||||
|
joinedTabs := lipgloss.JoinHorizontal(lipgloss.Bottom, renderedTabs...)
|
||||||
|
|
||||||
return header.Render(renderedTabs...)
|
return header.Render(joinedTabs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m HeaderTabModel) Init() tea.Cmd {
|
func (m HeaderTabModel) Init() tea.Cmd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m HeaderTabModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m HeaderTabModel) Update(msg tea.Msg) (HeaderTabModel, tea.Cmd) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m HeaderTabModel) View() string {
|
func (m *HeaderTabModel) SetSize(width int) {
|
||||||
content := lipgloss.JoinHorizontal(lipgloss.Center, m.Name, fmt.Sprintf("(%d)", m.Unread))
|
m.Size = Size{
|
||||||
|
Width: width,
|
||||||
|
Height: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if m.Current {
|
func (m HeaderTabModel) View() string {
|
||||||
return lipgloss.NewStyle().Underline(true).Render(content)
|
contentWidth := m.Size.Width - 4
|
||||||
|
content := fmt.Sprintf("[%c] %s - %d", m.Key, m.Name, m.Unread)
|
||||||
|
if len(content) > contentWidth {
|
||||||
|
content = fmt.Sprintf("[%c] %d", m.Key, m.Unread)
|
||||||
|
if len(content) > contentWidth {
|
||||||
|
content = fmt.Sprintf("[%c]", m.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content = lipgloss.PlaceHorizontal(contentWidth, lipgloss.Center, content)
|
||||||
|
|
||||||
|
if m.Active {
|
||||||
|
return activeHeaderTabStyle.Render(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return headerTabStyle.Render(content)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
tui/home.go
13
tui/home.go
|
|
@ -11,8 +11,14 @@ type (
|
||||||
HomeScreenModel struct{
|
HomeScreenModel struct{
|
||||||
Size Size
|
Size Size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwitchHomeScreenMsg struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func SwitchHomeScreen() tea.Msg {
|
||||||
|
return SwitchHomeScreenMsg{}
|
||||||
|
}
|
||||||
|
|
||||||
func NewHomeScreen() HomeScreenModel {
|
func NewHomeScreen() HomeScreenModel {
|
||||||
return HomeScreenModel{}
|
return HomeScreenModel{}
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +35,8 @@ func (m HomeScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "q", "ctrl-c":
|
case "q", "ctrl-c":
|
||||||
cmd = tea.Quit
|
cmd = tea.Quit
|
||||||
|
case "N":
|
||||||
|
cmd = SwitchNotificationScreen
|
||||||
}
|
}
|
||||||
case Size:
|
case Size:
|
||||||
m.Size = msg
|
m.Size = msg
|
||||||
|
|
@ -37,6 +45,11 @@ func (m HomeScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
return m, cmd
|
return m, cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m HomeScreenModel) SetSize(width, height int) HomeScreenModel {
|
||||||
|
m.Size = NewSize(width, height)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
func (m HomeScreenModel) View() string {
|
func (m HomeScreenModel) View() string {
|
||||||
header := lipgloss.NewStyle().
|
header := lipgloss.NewStyle().
|
||||||
Align(lipgloss.Center).
|
Align(lipgloss.Center).
|
||||||
|
|
|
||||||
43
tui/notifications.go
Normal file
43
tui/notifications.go
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package tui
|
||||||
|
|
||||||
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
|
type (
|
||||||
|
NotificationScreenModel struct{
|
||||||
|
Size Size
|
||||||
|
}
|
||||||
|
|
||||||
|
SwitchNotificationScreenMsg struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func SwitchNotificationScreen() tea.Msg {
|
||||||
|
return SwitchNotificationScreenMsg{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotificationScreen() NotificationScreenModel {
|
||||||
|
return NotificationScreenModel{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m NotificationScreenModel) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m NotificationScreenModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
var cmd tea.Cmd
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "q", "ctrl-c":
|
||||||
|
cmd = tea.Quit
|
||||||
|
case "H":
|
||||||
|
cmd = SwitchHomeScreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m NotificationScreenModel) View() string {
|
||||||
|
return "notification screen"
|
||||||
|
}
|
||||||
16
tui/size.go
16
tui/size.go
|
|
@ -2,9 +2,21 @@ package tui
|
||||||
|
|
||||||
import tea "github.com/charmbracelet/bubbletea"
|
import tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
||||||
type Size struct{
|
type (
|
||||||
|
Size struct{
|
||||||
Width, Height int
|
Width, Height int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sized interface{
|
||||||
|
SetSize(width, height int) tea.Model
|
||||||
|
}
|
||||||
|
SizedWidth interface{
|
||||||
|
SetWidth(width int) tea.Model
|
||||||
|
}
|
||||||
|
SizedHeight interface{
|
||||||
|
SetHeight(height int) tea.Model
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func NewSize(width, height int) Size {
|
func NewSize(width, height int) Size {
|
||||||
return Size { Width: width, Height: height }
|
return Size { Width: width, Height: height }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue