mirror of
https://github.com/Valkyrie00/bold-brew.git
synced 2026-03-14 14:25:53 +01:00
feat: new UI layout management (#6)
* created new components * enhanced new layout * improved theme
This commit is contained in:
parent
74884eaabf
commit
7d2c082ea5
13 changed files with 600 additions and 362 deletions
|
|
@ -2,6 +2,8 @@ package services
|
|||
|
||||
import (
|
||||
"bbrew/internal/models"
|
||||
"bbrew/internal/ui"
|
||||
"bbrew/internal/ui/theme"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
|
@ -22,35 +24,40 @@ type AppServiceInterface interface {
|
|||
}
|
||||
|
||||
type AppService struct {
|
||||
app *tview.Application
|
||||
app *tview.Application
|
||||
layout ui.LayoutInterface
|
||||
theme *theme.Theme
|
||||
|
||||
// Data
|
||||
packages *[]models.Formula
|
||||
filteredPackages *[]models.Formula
|
||||
showOnlyInstalled bool
|
||||
brewVersion string
|
||||
|
||||
// Services
|
||||
BrewService BrewServiceInterface
|
||||
CommandService CommandServiceInterface
|
||||
SelfUpdateService SelfUpdateServiceInterface
|
||||
LayoutService LayoutServiceInterface
|
||||
}
|
||||
|
||||
var NewAppService = func() AppServiceInterface {
|
||||
return &AppService{
|
||||
app: tview.NewApplication(), // Initialize the application
|
||||
func NewAppService() AppServiceInterface {
|
||||
app := tview.NewApplication()
|
||||
themeService := theme.NewTheme()
|
||||
|
||||
appService := &AppService{
|
||||
app: app,
|
||||
theme: themeService,
|
||||
layout: ui.NewLayout(themeService),
|
||||
|
||||
packages: new([]models.Formula),
|
||||
filteredPackages: new([]models.Formula),
|
||||
showOnlyInstalled: false, // Default to show all packages
|
||||
showOnlyInstalled: false,
|
||||
brewVersion: "-",
|
||||
|
||||
// Services
|
||||
BrewService: NewBrewService(),
|
||||
CommandService: NewCommandService(),
|
||||
SelfUpdateService: NewSelfUpdateService(),
|
||||
LayoutService: NewLayoutService(),
|
||||
}
|
||||
|
||||
return appService
|
||||
}
|
||||
|
||||
func (s *AppService) GetApp() *tview.Application {
|
||||
|
|
@ -73,14 +80,14 @@ func (s *AppService) Boot() (err error) {
|
|||
}
|
||||
|
||||
func (s *AppService) updateHomeBrew() {
|
||||
s.LayoutService.SetNotificationMessageWarning("Updating Homebrew formulae...")
|
||||
s.layout.GetNotifier().ShowWarning("Updating Homebrew formulae...")
|
||||
if err := s.CommandService.UpdateHomebrew(); err != nil {
|
||||
s.LayoutService.SetNotificationMessageError("Could not update Homebrew formulae")
|
||||
s.layout.GetNotifier().ShowError("Could not update Homebrew formulae")
|
||||
return
|
||||
}
|
||||
|
||||
// Clear loading message and update results
|
||||
s.LayoutService.SetNotificationMessageSuccess("Homebrew formulae updated successfully")
|
||||
s.layout.GetNotifier().ShowSuccess("Homebrew formulae updated successfully")
|
||||
s.forceRefreshResults()
|
||||
}
|
||||
|
||||
|
|
@ -149,32 +156,23 @@ func (s *AppService) setDetails(info *models.Formula) {
|
|||
installedVersion, info.Versions.Stable, packagePrefix, dependencies, installedOnRequest, info.Outdated,
|
||||
)
|
||||
|
||||
s.LayoutService.GetDetailsView().SetText(
|
||||
fmt.Sprintf("%s\n\n%s", generalInfo, installInfo),
|
||||
)
|
||||
s.layout.GetDetails().SetContent(fmt.Sprintf("%s\n\n%s", generalInfo, installInfo))
|
||||
return
|
||||
}
|
||||
|
||||
s.LayoutService.GetDetailsView().SetText("")
|
||||
s.layout.GetDetails().SetContent("")
|
||||
}
|
||||
|
||||
func (s *AppService) forceRefreshResults() {
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
_ = s.BrewService.LoadAllFormulae()
|
||||
s.search(s.LayoutService.GetSearchField().GetText(), false)
|
||||
s.search(s.layout.GetSearch().Field().GetText(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func (s *AppService) setResults(data *[]models.Formula, scrollToTop bool) {
|
||||
headers := []string{"Name", "Version", "Description"}
|
||||
s.LayoutService.GetResultTable().Clear()
|
||||
|
||||
for i, header := range headers {
|
||||
s.LayoutService.GetResultTable().SetCell(0, i, tview.NewTableCell(header).
|
||||
SetTextColor(tcell.ColorBlue).
|
||||
SetAlign(tview.AlignLeft).
|
||||
SetSelectable(false))
|
||||
}
|
||||
s.layout.GetTable().Clear()
|
||||
s.layout.GetTable().SetTableHeaders("Name", "Version", "Description")
|
||||
|
||||
for i, info := range *data {
|
||||
version := info.Versions.Stable
|
||||
|
|
@ -192,21 +190,21 @@ func (s *AppService) setResults(data *[]models.Formula, scrollToTop bool) {
|
|||
versionCell.SetTextColor(tcell.ColorOrange)
|
||||
}
|
||||
|
||||
s.LayoutService.GetResultTable().SetCell(i+1, 0, nameCell.SetExpansion(0))
|
||||
s.LayoutService.GetResultTable().SetCell(i+1, 1, versionCell.SetExpansion(0))
|
||||
s.LayoutService.GetResultTable().SetCell(i+1, 2, tview.NewTableCell(info.Description).SetSelectable(true).SetExpansion(1))
|
||||
s.layout.GetTable().View().SetCell(i+1, 0, nameCell.SetExpansion(0))
|
||||
s.layout.GetTable().View().SetCell(i+1, 1, versionCell.SetExpansion(0))
|
||||
s.layout.GetTable().View().SetCell(i+1, 2, tview.NewTableCell(info.Description).SetSelectable(true).SetExpansion(1))
|
||||
}
|
||||
|
||||
// Update the details view with the first item in the list
|
||||
if len(*data) > 0 {
|
||||
if scrollToTop {
|
||||
s.LayoutService.GetResultTable().Select(1, 0)
|
||||
s.LayoutService.GetResultTable().ScrollToBeginning()
|
||||
s.layout.GetTable().View().Select(1, 0)
|
||||
s.layout.GetTable().View().ScrollToBeginning()
|
||||
s.setDetails(&(*data)[0])
|
||||
}
|
||||
|
||||
// Update the filter counter
|
||||
s.LayoutService.UpdateFilterCounterView(len(*s.packages), len(*s.filteredPackages))
|
||||
s.layout.GetSearch().UpdateCounter(len(*s.packages), len(*s.filteredPackages))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -215,12 +213,8 @@ func (s *AppService) setResults(data *[]models.Formula, scrollToTop bool) {
|
|||
|
||||
func (s *AppService) BuildApp() {
|
||||
// Build the layout
|
||||
s.LayoutService.SetNotificationView()
|
||||
s.LayoutService.SetHeaderView(AppName, AppVersion, s.brewVersion)
|
||||
s.LayoutService.SetLegendView()
|
||||
s.LayoutService.SetDetailsView()
|
||||
s.LayoutService.SetBuildOutputView()
|
||||
s.LayoutService.SetFilterCounterView()
|
||||
s.layout.Setup()
|
||||
s.layout.GetHeader().Update(AppName, AppVersion, s.brewVersion)
|
||||
|
||||
// Evaluate if there is a new version available
|
||||
go func() {
|
||||
|
|
@ -230,7 +224,7 @@ func (s *AppService) BuildApp() {
|
|||
if latestVersion, err := s.SelfUpdateService.CheckForUpdates(ctx); err == nil && latestVersion != AppVersion {
|
||||
s.app.QueueUpdateDraw(func() {
|
||||
AppVersion = fmt.Sprintf("%s ([orange]New Version Available: %s[-])", AppVersion, latestVersion)
|
||||
s.LayoutService.UpdateHeaderView(AppName, AppVersion, s.brewVersion)
|
||||
s.layout.GetHeader().Update(AppName, AppVersion, s.brewVersion)
|
||||
})
|
||||
}
|
||||
}()
|
||||
|
|
@ -241,26 +235,23 @@ func (s *AppService) BuildApp() {
|
|||
s.setDetails(&(*s.filteredPackages)[row-1])
|
||||
}
|
||||
}
|
||||
s.LayoutService.SetResultTable(tableSelectionChangedFunc)
|
||||
s.layout.GetTable().View().SetSelectionChangedFunc(tableSelectionChangedFunc)
|
||||
|
||||
// Search field section
|
||||
inputDoneFunc := func(key tcell.Key) {
|
||||
if key == tcell.KeyEnter || key == tcell.KeyEscape {
|
||||
s.app.SetFocus(s.LayoutService.GetResultTable())
|
||||
s.app.SetFocus(s.layout.GetTable().View())
|
||||
}
|
||||
}
|
||||
changedFunc := func(text string) {
|
||||
s.search(text, true)
|
||||
}
|
||||
s.LayoutService.SetSearchField(inputDoneFunc, changedFunc)
|
||||
|
||||
// Set the grid layout (final step)
|
||||
s.LayoutService.SetGrid()
|
||||
s.layout.GetSearch().SetHandlers(inputDoneFunc, changedFunc)
|
||||
|
||||
// Add key event handler and set the root view
|
||||
s.app.SetInputCapture(s.handleKeyEventInput)
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true)
|
||||
s.app.SetFocus(s.LayoutService.GetResultTable())
|
||||
s.app.SetRoot(s.layout.Root(), true)
|
||||
s.app.SetFocus(s.layout.GetTable().View())
|
||||
|
||||
go s.updateHomeBrew() // Update Async the Homebrew formulae
|
||||
s.setResults(s.packages, true) // Set the results
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"bbrew/internal/models"
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func (s *AppService) handleKeyEventInput(event *tcell.EventKey) *tcell.EventKey {
|
||||
if s.LayoutService.GetSearchField().HasFocus() {
|
||||
if s.layout.GetSearch().Field().HasFocus() {
|
||||
return event
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +28,8 @@ func (s *AppService) handleKeyEventInput(event *tcell.EventKey) *tcell.EventKey
|
|||
},
|
||||
tcell.KeyCtrlU: s.handleUpdateAllPackagesEvent,
|
||||
tcell.KeyEsc: func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
s.app.SetRoot(s.layout.Root(), true)
|
||||
s.app.SetFocus(s.layout.GetTable().View())
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +42,7 @@ func (s *AppService) handleKeyEventInput(event *tcell.EventKey) *tcell.EventKey
|
|||
}
|
||||
|
||||
func (s *AppService) handleSearchFieldEvent() {
|
||||
s.app.SetFocus(s.LayoutService.GetSearchField())
|
||||
s.app.SetFocus(s.layout.GetSearch().Field())
|
||||
}
|
||||
|
||||
func (s *AppService) handleQuitEvent() {
|
||||
|
|
@ -49,102 +52,92 @@ func (s *AppService) handleQuitEvent() {
|
|||
func (s *AppService) handleFilterPackagesEvent() {
|
||||
s.showOnlyInstalled = !s.showOnlyInstalled
|
||||
if s.showOnlyInstalled {
|
||||
s.LayoutService.GetSearchField().SetLabel("Search (Installed): ")
|
||||
s.layout.GetSearch().Field().SetLabel("Search (Installed): ")
|
||||
} else {
|
||||
s.LayoutService.GetSearchField().SetLabel("Search (All): ")
|
||||
s.layout.GetSearch().Field().SetLabel("Search (All): ")
|
||||
}
|
||||
|
||||
s.search(s.LayoutService.GetSearchField().GetText(), true)
|
||||
s.search(s.layout.GetSearch().Field().GetText(), true)
|
||||
}
|
||||
|
||||
func (s *AppService) showModal(text string, confirmFunc func(), cancelFunc func()) {
|
||||
modal := s.layout.GetModal().Build(text, func() {
|
||||
s.app.SetRoot(s.layout.Root(), true)
|
||||
confirmFunc()
|
||||
}, func() {
|
||||
s.app.SetRoot(s.layout.Root(), true)
|
||||
cancelFunc()
|
||||
})
|
||||
s.app.SetRoot(modal, true)
|
||||
}
|
||||
|
||||
func (s *AppService) handleInstallPackageEvent() {
|
||||
row, _ := s.LayoutService.GetResultTable().GetSelection()
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.filteredPackages)[row-1]
|
||||
modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to install the package: %s?", info.Name), func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
s.LayoutService.GetOutputView().Clear()
|
||||
go func() {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Installing %s...", info.Name))
|
||||
if err := s.CommandService.InstallPackage(info, s.app, s.LayoutService.GetOutputView()); err != nil {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Failed to install %s", info.Name))
|
||||
return
|
||||
}
|
||||
|
||||
s.LayoutService.SetNotificationMessageSuccess(fmt.Sprintf("%s Installed", info.Name))
|
||||
s.forceRefreshResults()
|
||||
}()
|
||||
}, func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
})
|
||||
s.app.SetRoot(modal, true).SetFocus(modal)
|
||||
s.showModal(
|
||||
fmt.Sprintf("Are you sure you want to install the package: %s?", info.Name),
|
||||
s.createModalConfirmHandler(info, "Installing", s.CommandService.InstallPackage, "Installed"),
|
||||
s.resetViewAfterModal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AppService) handleRemovePackageEvent() {
|
||||
row, _ := s.LayoutService.GetResultTable().GetSelection()
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.filteredPackages)[row-1]
|
||||
modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to remove the package: %s?", info.Name), func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
s.LayoutService.GetOutputView().Clear()
|
||||
go func() {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Removing %s...", info.Name))
|
||||
if err := s.CommandService.RemovePackage(info, s.app, s.LayoutService.GetOutputView()); err != nil {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Failed to remove %s", info.Name))
|
||||
return
|
||||
}
|
||||
|
||||
s.LayoutService.SetNotificationMessageSuccess(fmt.Sprintf("%s Removed", info.Name))
|
||||
s.forceRefreshResults()
|
||||
}()
|
||||
}, func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
})
|
||||
s.app.SetRoot(modal, true).SetFocus(modal)
|
||||
s.showModal(
|
||||
fmt.Sprintf("Are you sure you want to remove the package: %s?", info.Name),
|
||||
s.createModalConfirmHandler(info, "Removing", s.CommandService.RemovePackage, "Removed"),
|
||||
s.resetViewAfterModal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AppService) handleUpdatePackageEvent() {
|
||||
row, _ := s.LayoutService.GetResultTable().GetSelection()
|
||||
row, _ := s.layout.GetTable().View().GetSelection()
|
||||
if row > 0 {
|
||||
info := (*s.filteredPackages)[row-1]
|
||||
modal := s.LayoutService.GenerateModal(fmt.Sprintf("Are you sure you want to update the package: %s?", info.Name), func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
s.LayoutService.GetOutputView().Clear()
|
||||
go func() {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Updating %s...", info.Name))
|
||||
if err := s.CommandService.UpdatePackage(info, s.app, s.LayoutService.GetOutputView()); err != nil {
|
||||
s.LayoutService.SetNotificationMessageWarning(fmt.Sprintf("Failed to update %s", info.Name))
|
||||
return
|
||||
}
|
||||
|
||||
s.LayoutService.SetNotificationMessageSuccess(fmt.Sprintf("%s Updated", info.Name))
|
||||
s.forceRefreshResults()
|
||||
}()
|
||||
}, func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
})
|
||||
s.app.SetRoot(modal, true).SetFocus(modal)
|
||||
s.showModal(
|
||||
fmt.Sprintf("Are you sure you want to update the package: %s?", info.Name),
|
||||
s.createModalConfirmHandler(info, "Updating", s.CommandService.UpdatePackage, "Updated"),
|
||||
s.resetViewAfterModal,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AppService) handleUpdateAllPackagesEvent() {
|
||||
modal := s.LayoutService.GenerateModal("Are you sure you want to update all packages?", func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
s.LayoutService.GetOutputView().Clear()
|
||||
s.showModal("Are you sure you want to update all packages?", func() {
|
||||
s.layout.GetDetails().Clear()
|
||||
go func() {
|
||||
s.LayoutService.SetNotificationMessageWarning("Updating all packages...")
|
||||
if err := s.CommandService.UpdateAllPackages(s.app, s.LayoutService.GetOutputView()); err != nil {
|
||||
s.LayoutService.SetNotificationMessageWarning("Failed to update all packages")
|
||||
s.layout.GetNotifier().ShowWarning("Updating all packages...")
|
||||
if err := s.CommandService.UpdateAllPackages(s.app, s.layout.GetDetails().View()); err != nil {
|
||||
s.layout.GetNotifier().ShowError("Failed to update all packages")
|
||||
return
|
||||
}
|
||||
|
||||
s.LayoutService.SetNotificationMessageSuccess("Updated all packages")
|
||||
s.layout.GetNotifier().ShowSuccess("Updated all packages")
|
||||
s.forceRefreshResults()
|
||||
}()
|
||||
}, func() {
|
||||
s.app.SetRoot(s.LayoutService.GetGrid(), true).SetFocus(s.LayoutService.GetResultTable())
|
||||
})
|
||||
s.app.SetRoot(modal, true).SetFocus(modal)
|
||||
}, s.resetViewAfterModal)
|
||||
}
|
||||
|
||||
func (s *AppService) resetViewAfterModal() {
|
||||
s.app.SetFocus(s.layout.GetTable().View())
|
||||
}
|
||||
|
||||
func (s *AppService) createModalConfirmHandler(info models.Formula, actionName string, action func(models.Formula, *tview.Application, *tview.TextView) error, completedAction string) func() {
|
||||
return func() {
|
||||
s.resetViewAfterModal()
|
||||
s.layout.GetOutput().Clear()
|
||||
go func() {
|
||||
s.layout.GetNotifier().ShowWarning(fmt.Sprintf("%s %s...", actionName, info.Name))
|
||||
if err := action(info, s.app, s.layout.GetOutput().View()); err != nil {
|
||||
s.layout.GetNotifier().ShowError(fmt.Sprintf("Failed to %s %s", actionName, info.Name))
|
||||
return
|
||||
}
|
||||
s.layout.GetNotifier().ShowSuccess(fmt.Sprintf("%s %s", info.Name, completedAction))
|
||||
s.forceRefreshResults()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,243 +0,0 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type LayoutServiceInterface interface {
|
||||
GetGrid() *tview.Grid
|
||||
SetGrid()
|
||||
|
||||
GetNotificationView() *tview.TextView
|
||||
SetNotificationView()
|
||||
SetNotificationMessageSuccess(message string)
|
||||
SetNotificationMessageWarning(message string)
|
||||
SetNotificationMessageError(message string)
|
||||
ClearNotificationMessage()
|
||||
|
||||
GetHeaderView() *tview.TextView
|
||||
SetHeaderView(name, version, brewVersion string)
|
||||
UpdateHeaderView(name, version, brewVersion string)
|
||||
|
||||
GetLegendView() *tview.TextView
|
||||
SetLegendView()
|
||||
|
||||
GetResultTable() *tview.Table
|
||||
SetResultTable(selectionChanged func(row, column int))
|
||||
|
||||
GetDetailsView() *tview.TextView
|
||||
SetDetailsView()
|
||||
|
||||
GetOutputView() *tview.TextView
|
||||
SetBuildOutputView()
|
||||
|
||||
GetSearchField() *tview.InputField
|
||||
SetSearchField(done func(key tcell.Key), changed func(text string))
|
||||
|
||||
GetFilterCounterView() *tview.TextView
|
||||
SetFilterCounterView()
|
||||
UpdateFilterCounterView(total, filtered int)
|
||||
|
||||
GenerateModal(text string, confirmFunc func(), cancelFunc func()) *tview.Modal
|
||||
}
|
||||
|
||||
type LayoutService struct {
|
||||
header *tview.TextView
|
||||
notification *tview.TextView
|
||||
legend *tview.TextView
|
||||
table *tview.Table
|
||||
detailsView *tview.TextView
|
||||
outputView *tview.TextView
|
||||
searchField *tview.InputField
|
||||
filterCounter *tview.TextView
|
||||
grid *tview.Grid
|
||||
}
|
||||
|
||||
var NewLayoutService = func() LayoutServiceInterface {
|
||||
return &LayoutService{}
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetGrid() *tview.Grid {
|
||||
return s.grid
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetGrid() {
|
||||
headerContent := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(s.header, 0, 1, false).
|
||||
AddItem(s.notification, 0, 1, false)
|
||||
|
||||
searchRow := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(s.searchField, 0, 1, false).
|
||||
AddItem(s.filterCounter, 0, 1, false)
|
||||
|
||||
filtersArea := tview.NewFrame(searchRow).
|
||||
SetBorders(0, 0, 0, 0, 3, 3)
|
||||
|
||||
tableFrame := tview.NewFrame(s.table).
|
||||
SetBorders(0, 0, 0, 0, 3, 3)
|
||||
|
||||
leftColumn := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(filtersArea, 2, 0, false). // Fixed height of 3 rows
|
||||
AddItem(tableFrame, 0, 4, false)
|
||||
|
||||
rightColumn := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(s.detailsView, 0, 2, false).
|
||||
AddItem(s.outputView, 0, 1, false)
|
||||
|
||||
mainContent := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(leftColumn, 0, 2, false).
|
||||
AddItem(rightColumn, 0, 1, false)
|
||||
|
||||
s.grid = tview.NewGrid().
|
||||
SetRows(1, 0, 1).
|
||||
SetColumns(0).
|
||||
SetBorders(true).
|
||||
AddItem(headerContent, 0, 0, 1, 1, 0, 0, false).
|
||||
AddItem(mainContent, 1, 0, 1, 1, 0, 0, true).
|
||||
AddItem(s.legend, 2, 0, 1, 1, 0, 0, false)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetHeaderView() *tview.TextView {
|
||||
return s.header
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetHeaderView(name, version, brewVersion string) {
|
||||
s.header = tview.NewTextView().
|
||||
SetText(fmt.Sprintf(" %s %s - %s", name, version, brewVersion)).
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignLeft)
|
||||
}
|
||||
|
||||
func (s *LayoutService) UpdateHeaderView(name, version, brewVersion string) {
|
||||
s.GetHeaderView().SetText(fmt.Sprintf(" %s %s - %s", name, version, brewVersion))
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetLegendView() *tview.TextView {
|
||||
return s.legend
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetLegendView() {
|
||||
legendText := tview.Escape(
|
||||
"[/] Search | " +
|
||||
"[f] Filter Installed | " +
|
||||
"[i] Install | " +
|
||||
"[u] Update | " +
|
||||
"[ctrl+u] Update All | " +
|
||||
"[r] Remove | " +
|
||||
"[Esc] Back to Table | " +
|
||||
"[q] Quit",
|
||||
)
|
||||
|
||||
s.legend = tview.NewTextView().
|
||||
SetText(legendText).
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignCenter)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetResultTable() *tview.Table {
|
||||
return s.table
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetResultTable(selectionChanged func(row, column int)) {
|
||||
s.table = tview.NewTable().
|
||||
SetBorders(false).
|
||||
SetSelectable(true, false).
|
||||
SetFixed(1, 0).
|
||||
SetSelectionChangedFunc(selectionChanged)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetNotificationView() *tview.TextView {
|
||||
return s.notification
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetNotificationView() {
|
||||
s.notification = tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignRight)
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetNotificationMessageSuccess(message string) {
|
||||
s.setNotificationMessage(message, tcell.ColorGreen)
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetNotificationMessageWarning(message string) {
|
||||
s.setNotificationMessage(message, tcell.ColorOrange)
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetNotificationMessageError(message string) {
|
||||
s.setNotificationMessage(message, tcell.ColorRed)
|
||||
}
|
||||
|
||||
func (s *LayoutService) setNotificationMessage(message string, color tcell.Color) {
|
||||
s.GetNotificationView().SetTextColor(color).SetText(fmt.Sprintf(" %s ", message))
|
||||
}
|
||||
|
||||
func (s *LayoutService) ClearNotificationMessage() {
|
||||
s.GetNotificationView().Clear()
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetDetailsView() *tview.TextView {
|
||||
return s.detailsView
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetDetailsView() {
|
||||
s.detailsView = tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft)
|
||||
s.detailsView.SetTitle("Details").SetTitleColor(tcell.ColorYellowGreen).SetTitleAlign(tview.AlignLeft).SetBorder(true)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetOutputView() *tview.TextView {
|
||||
return s.outputView
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetBuildOutputView() {
|
||||
s.outputView = tview.NewTextView().SetDynamicColors(true).SetTextAlign(tview.AlignLeft)
|
||||
s.outputView.SetBorder(true).SetTitle("Output").SetTitleColor(tcell.ColorYellowGreen).SetTitleAlign(tview.AlignLeft)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetSearchField() *tview.InputField {
|
||||
return s.searchField
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetSearchField(done func(key tcell.Key), changed func(text string)) {
|
||||
s.searchField = tview.NewInputField().
|
||||
SetLabel("Search (All): ").
|
||||
SetFieldBackgroundColor(tcell.ColorBlack).
|
||||
SetFieldTextColor(tcell.ColorWhite).
|
||||
SetLabelColor(tcell.ColorYellow).
|
||||
SetFieldWidth(30).
|
||||
SetDoneFunc(done).
|
||||
SetChangedFunc(changed)
|
||||
}
|
||||
|
||||
func (s *LayoutService) GetFilterCounterView() *tview.TextView {
|
||||
return s.filterCounter
|
||||
}
|
||||
|
||||
func (s *LayoutService) SetFilterCounterView() {
|
||||
s.filterCounter = tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignRight)
|
||||
}
|
||||
|
||||
func (s *LayoutService) UpdateFilterCounterView(total, filtered int) {
|
||||
s.filterCounter.SetText(fmt.Sprintf("Total: %d | Filtered: %d", total, filtered))
|
||||
}
|
||||
|
||||
func (s *LayoutService) GenerateModal(text string, confirmFunc func(), cancelFunc func()) *tview.Modal {
|
||||
return tview.NewModal().
|
||||
SetText(text).
|
||||
AddButtons([]string{"Confirm", "Cancel"}).
|
||||
SetBackgroundColor(tcell.ColorDarkSlateGray).
|
||||
SetTextColor(tcell.ColorWhite).
|
||||
SetButtonBackgroundColor(tcell.ColorGray).
|
||||
SetButtonTextColor(tcell.ColorWhite).
|
||||
SetDoneFunc(func(_ int, buttonLabel string) {
|
||||
if buttonLabel == "Confirm" {
|
||||
confirmFunc()
|
||||
} else if buttonLabel == "Cancel" {
|
||||
cancelFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
38
internal/ui/components/details.go
Normal file
38
internal/ui/components/details.go
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Details struct {
|
||||
view *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewDetails(theme *theme.Theme) *Details {
|
||||
details := &Details{
|
||||
view: tview.NewTextView(),
|
||||
theme: theme,
|
||||
}
|
||||
|
||||
details.view.SetDynamicColors(true)
|
||||
details.view.SetTextAlign(tview.AlignLeft)
|
||||
details.view.SetTitle("Details")
|
||||
details.view.SetTitleColor(theme.TitleColor)
|
||||
details.view.SetTitleAlign(tview.AlignLeft)
|
||||
details.view.SetBorder(true)
|
||||
return details
|
||||
}
|
||||
|
||||
func (d *Details) SetContent(text string) {
|
||||
d.view.SetText(text)
|
||||
}
|
||||
|
||||
func (d *Details) View() *tview.TextView {
|
||||
return d.view
|
||||
}
|
||||
|
||||
func (d *Details) Clear() {
|
||||
d.view.Clear()
|
||||
}
|
||||
31
internal/ui/components/header.go
Normal file
31
internal/ui/components/header.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"fmt"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
view *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewHeader(theme *theme.Theme) *Header {
|
||||
header := &Header{
|
||||
view: tview.NewTextView(),
|
||||
theme: theme,
|
||||
}
|
||||
|
||||
header.view.SetDynamicColors(true)
|
||||
header.view.SetTextAlign(tview.AlignLeft)
|
||||
return header
|
||||
}
|
||||
|
||||
func (h *Header) Update(name, version, brewVersion string) {
|
||||
h.view.SetText(fmt.Sprintf(" %s %s - %s", name, version, brewVersion))
|
||||
}
|
||||
|
||||
func (h *Header) View() *tview.TextView {
|
||||
return h.view
|
||||
}
|
||||
47
internal/ui/components/legend.go
Normal file
47
internal/ui/components/legend.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Legend struct {
|
||||
view *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewLegend(theme *theme.Theme) *Legend {
|
||||
legendText := tview.Escape(
|
||||
"[/] Search | " +
|
||||
"[f] Filter Installed | " +
|
||||
"[i] Install | " +
|
||||
"[u] Update | " +
|
||||
"[ctrl+u] Update All | " +
|
||||
"[r] Remove | " +
|
||||
"[Esc] Back to Table | " +
|
||||
"[q] Quit",
|
||||
)
|
||||
|
||||
legendView := tview.NewTextView().
|
||||
SetText(legendText).
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignCenter).
|
||||
SetTextColor(theme.LegendColor)
|
||||
|
||||
return &Legend{
|
||||
view: legendView,
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Legend) View() *tview.TextView {
|
||||
return l.view
|
||||
}
|
||||
|
||||
func (l *Legend) SetText(text string) {
|
||||
l.view.SetText(text)
|
||||
}
|
||||
|
||||
func (l *Legend) Clear() {
|
||||
l.view.Clear()
|
||||
}
|
||||
44
internal/ui/components/modal.go
Normal file
44
internal/ui/components/modal.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Modal struct {
|
||||
view *tview.Modal
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewModal(theme *theme.Theme) *Modal {
|
||||
modal := tview.NewModal().
|
||||
SetBackgroundColor(theme.ModalBgColor).
|
||||
SetTextColor(theme.DefaultTextColor).
|
||||
SetButtonBackgroundColor(theme.ButtonBgColor).
|
||||
SetButtonTextColor(theme.ButtonTextColor)
|
||||
|
||||
return &Modal{
|
||||
view: modal,
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Modal) View() *tview.Modal {
|
||||
return m.view
|
||||
}
|
||||
|
||||
func (m *Modal) Build(text string, confirmFunc func(), cancelFunc func()) *tview.Modal {
|
||||
m.view.ClearButtons()
|
||||
m.view.
|
||||
SetText(text).
|
||||
AddButtons([]string{"Confirm", "Cancel"}).
|
||||
SetDoneFunc(func(buttonIndex int, _ string) {
|
||||
if buttonIndex == 0 {
|
||||
confirmFunc()
|
||||
} else if buttonIndex == 1 {
|
||||
cancelFunc()
|
||||
}
|
||||
})
|
||||
|
||||
return m.view
|
||||
}
|
||||
43
internal/ui/components/notifier.go
Normal file
43
internal/ui/components/notifier.go
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"fmt"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Notifier struct {
|
||||
view *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewNotifier(theme *theme.Theme) *Notifier {
|
||||
notifierView := tview.NewTextView().
|
||||
SetDynamicColors(true).
|
||||
SetTextAlign(tview.AlignRight)
|
||||
|
||||
return &Notifier{
|
||||
view: notifierView,
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) View() *tview.TextView {
|
||||
return n.view
|
||||
}
|
||||
|
||||
func (n *Notifier) ShowSuccess(message string) {
|
||||
n.view.SetTextColor(n.theme.SuccessColor).SetText(fmt.Sprintf(" %s ", message))
|
||||
}
|
||||
|
||||
func (n *Notifier) ShowWarning(message string) {
|
||||
n.view.SetTextColor(n.theme.WarningColor).SetText(fmt.Sprintf(" %s ", message))
|
||||
}
|
||||
|
||||
func (n *Notifier) ShowError(message string) {
|
||||
n.view.SetTextColor(n.theme.ErrorColor).SetText(fmt.Sprintf(" %s ", message))
|
||||
}
|
||||
|
||||
func (n *Notifier) Clear() {
|
||||
n.view.Clear()
|
||||
}
|
||||
49
internal/ui/components/ouput.go
Normal file
49
internal/ui/components/ouput.go
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Output struct {
|
||||
view *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewOutput(theme *theme.Theme) *Output {
|
||||
output := &Output{
|
||||
view: tview.NewTextView(),
|
||||
theme: theme,
|
||||
}
|
||||
|
||||
output.view.SetDynamicColors(true)
|
||||
output.view.SetScrollable(true)
|
||||
output.view.SetWrap(true)
|
||||
output.view.SetTextAlign(tview.AlignLeft)
|
||||
output.view.SetBorder(true)
|
||||
output.view.SetTitle("Output")
|
||||
output.view.SetTitleColor(theme.TitleColor)
|
||||
output.view.SetTitleAlign(tview.AlignLeft)
|
||||
return output
|
||||
}
|
||||
|
||||
func (o *Output) View() *tview.TextView {
|
||||
return o.view
|
||||
}
|
||||
|
||||
func (o *Output) Clear() {
|
||||
o.view.Clear()
|
||||
}
|
||||
|
||||
func (o *Output) Write(text string) {
|
||||
o.view.SetText(text)
|
||||
}
|
||||
|
||||
func (o *Output) Append(text string) {
|
||||
currentText := o.view.GetText(true)
|
||||
o.view.SetText(currentText + text)
|
||||
}
|
||||
|
||||
func (o *Output) ScrollToEnd() {
|
||||
o.view.ScrollToEnd()
|
||||
}
|
||||
48
internal/ui/components/search.go
Normal file
48
internal/ui/components/search.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"fmt"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Search struct {
|
||||
field *tview.InputField
|
||||
counter *tview.TextView
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewSearch(theme *theme.Theme) *Search {
|
||||
search := &Search{
|
||||
field: tview.NewInputField(),
|
||||
counter: tview.NewTextView(),
|
||||
theme: theme,
|
||||
}
|
||||
|
||||
search.field.SetLabel("Search (All): ")
|
||||
search.field.SetFieldBackgroundColor(theme.DefaultBgColor)
|
||||
search.field.SetFieldTextColor(theme.DefaultTextColor)
|
||||
search.field.SetLabelColor(theme.LabelColor)
|
||||
search.field.SetFieldWidth(30)
|
||||
search.counter.SetDynamicColors(true)
|
||||
search.counter.SetTextAlign(tview.AlignRight)
|
||||
return search
|
||||
}
|
||||
|
||||
func (s *Search) SetHandlers(done func(key tcell.Key), changed func(text string)) {
|
||||
s.field.SetDoneFunc(done)
|
||||
s.field.SetChangedFunc(changed)
|
||||
}
|
||||
|
||||
func (s *Search) UpdateCounter(total, filtered int) {
|
||||
s.counter.SetText(fmt.Sprintf("Total: %d | Filtered: %d", total, filtered))
|
||||
}
|
||||
|
||||
func (s *Search) Field() *tview.InputField {
|
||||
return s.field
|
||||
}
|
||||
|
||||
func (s *Search) Counter() *tview.TextView {
|
||||
return s.counter
|
||||
}
|
||||
46
internal/ui/components/table.go
Normal file
46
internal/ui/components/table.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package components
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Table struct {
|
||||
view *tview.Table
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewTable(theme *theme.Theme) *Table {
|
||||
table := &Table{
|
||||
view: tview.NewTable(),
|
||||
theme: theme,
|
||||
}
|
||||
table.view.SetBorders(false)
|
||||
table.view.SetSelectable(true, false)
|
||||
table.view.SetFixed(1, 0)
|
||||
return table
|
||||
}
|
||||
|
||||
func (t *Table) SetSelectionHandler(handler func(row, column int)) {
|
||||
t.view.SetSelectionChangedFunc(handler)
|
||||
}
|
||||
|
||||
func (t *Table) View() *tview.Table {
|
||||
return t.view
|
||||
}
|
||||
|
||||
func (t *Table) Clear() {
|
||||
t.view.Clear()
|
||||
}
|
||||
|
||||
func (t *Table) SetTableHeaders(headers ...string) {
|
||||
for i, header := range headers {
|
||||
t.view.SetCell(0, i, &tview.TableCell{
|
||||
Text: header,
|
||||
NotSelectable: true,
|
||||
Align: tview.AlignLeft,
|
||||
Color: t.theme.TableHeaderColor,
|
||||
BackgroundColor: t.theme.DefaultBgColor,
|
||||
})
|
||||
}
|
||||
}
|
||||
112
internal/ui/layout.go
Normal file
112
internal/ui/layout.go
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"bbrew/internal/ui/components"
|
||||
"bbrew/internal/ui/theme"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type LayoutInterface interface {
|
||||
Setup()
|
||||
Root() tview.Primitive
|
||||
|
||||
GetHeader() *components.Header
|
||||
GetSearch() *components.Search
|
||||
GetTable() *components.Table
|
||||
GetDetails() *components.Details
|
||||
GetOutput() *components.Output
|
||||
GetLegend() *components.Legend
|
||||
GetNotifier() *components.Notifier
|
||||
GetModal() *components.Modal
|
||||
}
|
||||
|
||||
type Layout struct {
|
||||
mainContent *tview.Grid
|
||||
header *components.Header
|
||||
search *components.Search
|
||||
table *components.Table
|
||||
details *components.Details
|
||||
output *components.Output
|
||||
legend *components.Legend
|
||||
notifier *components.Notifier
|
||||
modal *components.Modal
|
||||
theme *theme.Theme
|
||||
}
|
||||
|
||||
func NewLayout(theme *theme.Theme) *Layout {
|
||||
return &Layout{
|
||||
mainContent: tview.NewGrid(),
|
||||
header: components.NewHeader(theme),
|
||||
search: components.NewSearch(theme),
|
||||
table: components.NewTable(theme),
|
||||
details: components.NewDetails(theme),
|
||||
output: components.NewOutput(theme),
|
||||
legend: components.NewLegend(theme),
|
||||
notifier: components.NewNotifier(theme),
|
||||
modal: components.NewModal(theme),
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Layout) setupLayout() {
|
||||
// Header
|
||||
headerContent := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(l.header.View(), 0, 1, false).
|
||||
AddItem(l.notifier.View(), 0, 1, false)
|
||||
|
||||
// Search and filters
|
||||
searchRow := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(l.search.Field(), 0, 1, false).
|
||||
AddItem(l.search.Counter(), 0, 1, false)
|
||||
|
||||
filtersArea := tview.NewFrame(searchRow).
|
||||
SetBorders(0, 0, 0, 0, 3, 3)
|
||||
|
||||
tableFrame := tview.NewFrame(l.table.View()).
|
||||
SetBorders(0, 0, 0, 0, 3, 3)
|
||||
|
||||
// Left column with search and table
|
||||
leftColumn := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(filtersArea, 2, 0, false).
|
||||
AddItem(tableFrame, 0, 4, false)
|
||||
|
||||
// Right column with details and output
|
||||
rightColumn := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(l.details.View(), 0, 2, false).
|
||||
AddItem(l.output.View(), 0, 1, false)
|
||||
|
||||
// Central content
|
||||
mainContent := tview.NewFlex().SetDirection(tview.FlexColumn).
|
||||
AddItem(leftColumn, 0, 2, false).
|
||||
AddItem(rightColumn, 0, 1, false)
|
||||
|
||||
// Footer
|
||||
footerContent := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(l.legend.View(), 0, 1, false)
|
||||
|
||||
// Final layout
|
||||
l.mainContent.
|
||||
SetRows(1, 0, 1).
|
||||
SetColumns(0).
|
||||
SetBorders(true).
|
||||
AddItem(headerContent, 0, 0, 1, 1, 0, 0, false).
|
||||
AddItem(mainContent, 1, 0, 1, 1, 0, 0, true).
|
||||
AddItem(footerContent, 2, 0, 1, 1, 0, 0, false)
|
||||
}
|
||||
|
||||
func (l *Layout) Setup() {
|
||||
l.setupLayout()
|
||||
}
|
||||
|
||||
func (l *Layout) Root() tview.Primitive {
|
||||
return l.mainContent
|
||||
}
|
||||
|
||||
func (l *Layout) GetHeader() *components.Header { return l.header }
|
||||
func (l *Layout) GetSearch() *components.Search { return l.search }
|
||||
func (l *Layout) GetTable() *components.Table { return l.table }
|
||||
func (l *Layout) GetDetails() *components.Details { return l.details }
|
||||
func (l *Layout) GetOutput() *components.Output { return l.output }
|
||||
func (l *Layout) GetLegend() *components.Legend { return l.legend }
|
||||
func (l *Layout) GetNotifier() *components.Notifier { return l.notifier }
|
||||
func (l *Layout) GetModal() *components.Modal { return l.modal }
|
||||
39
internal/ui/theme/theme.go
Normal file
39
internal/ui/theme/theme.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package theme
|
||||
|
||||
import "github.com/gdamore/tcell/v2"
|
||||
|
||||
type Theme struct {
|
||||
DefaultTextColor tcell.Color
|
||||
DefaultBgColor tcell.Color
|
||||
WarningColor tcell.Color
|
||||
SuccessColor tcell.Color
|
||||
ErrorColor tcell.Color
|
||||
|
||||
TitleColor tcell.Color
|
||||
LabelColor tcell.Color
|
||||
ButtonBgColor tcell.Color
|
||||
ButtonTextColor tcell.Color
|
||||
|
||||
ModalBgColor tcell.Color
|
||||
LegendColor tcell.Color
|
||||
TableHeaderColor tcell.Color
|
||||
}
|
||||
|
||||
func NewTheme() *Theme {
|
||||
return &Theme{
|
||||
DefaultTextColor: tcell.ColorWhite,
|
||||
DefaultBgColor: tcell.ColorBlack,
|
||||
WarningColor: tcell.ColorYellow,
|
||||
SuccessColor: tcell.ColorGreen,
|
||||
ErrorColor: tcell.ColorRed,
|
||||
|
||||
TitleColor: tcell.ColorYellowGreen,
|
||||
LabelColor: tcell.ColorYellow,
|
||||
ButtonBgColor: tcell.ColorGray,
|
||||
ButtonTextColor: tcell.ColorWhite,
|
||||
|
||||
ModalBgColor: tcell.ColorDarkSlateGray,
|
||||
LegendColor: tcell.ColorWhite,
|
||||
TableHeaderColor: tcell.ColorBlue,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue