diff --git a/assets/colors.yml b/assets/colors.yml new file mode 100644 index 0000000..b36993d --- /dev/null +++ b/assets/colors.yml @@ -0,0 +1,53 @@ +default: + background: "#161616" + foreground: "#c7c7c7" + foreground2: "#FFFFFF" + +finder: + cursor: "#8B0000" + title: "#E9CE58" + match: "#90EE90" + highlight: + background: "#3A3A3A" + foreground: "#FFFFFF" + match: "#E0FFFF" + +navigation: + top: + background: "#5F875f" + foreground1: "#FFFFFF" + foreground2: "#151515" + bottom: + background: "#5F87AF" + foreground1: "#FFFFFF" + foreground2: "#151515" + +details: + foreground: "#696969" + +boards: + title: + foreground: "#ecce58" + headers: + background: "#5F87AF" + foreground: "#FFFFFF" + column: + background: "#232323" + foreground: "#ffffff" + highlight: + background: "#484848" + foreground: "#ffffff" + selection: + background: "#8B0000" + foreground: "#ffffff" + +spinner: + accent: "#FF0000" + +alerts: + success: + background: "#F5F5F5" + foreground: "#006400" + error: + background: "#F5F5F5" + foreground: "#8B0000" diff --git a/internal/app/action_bar.go b/internal/app/action_bar.go index 9b4ad09..01d8c3c 100644 --- a/internal/app/action_bar.go +++ b/internal/app/action_bar.go @@ -16,6 +16,7 @@ type ActionBar struct { items []ActionBarItem vAlign int hAlign int + style tcell.Style } const ( @@ -37,6 +38,7 @@ func NewActionBar(vAlign int, hAlign int) *ActionBar { vAlign: vAlign, hAlign: hAlign, items: make([]ActionBarItem, 0, ActionBarMaxItems), + style: DefaultStyle(), } } @@ -104,7 +106,7 @@ func (b *ActionBar) Draw(screen tcell.Screen) { for _, item := range b.items { DrawText(screen, item.x, item.y, item.Text1Style, item.Text1) DrawText(screen, item.x+len(item.Text1), item.y, item.Text2Style, item.Text2) - DrawText(screen, item.x+len(item.Text1)+len(item.Text2), item.y, DefaultStyle, " ") + DrawText(screen, item.x+len(item.Text1)+len(item.Text2), item.y, b.style, " ") } } diff --git a/internal/app/app.go b/internal/app/app.go index 3978249..88c9253 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -36,6 +36,7 @@ type App struct { runOnAppRoutine []func() spinner *SpinnerTCell view View + style tcell.Style } const ( @@ -44,10 +45,8 @@ const ( ) var ( - AppBackground = tcell.NewRGBColor(22, 22, 22) - DefaultStyle = tcell.StyleDefault.Background(AppBackground).Foreground(tcell.NewRGBColor(199, 199, 199)) - appInstance *App - once sync.Once + appInstance *App + once sync.Once ) func CreateNewApp() *App { @@ -68,6 +67,10 @@ func GetApp() *App { return appInstance } +func DefaultStyle() tcell.Style { + return tcell.StyleDefault.Background(Color("default.background")).Foreground(Color("default.foreground")) +} + func initApp() { screen, err := tcell.NewScreen() if err != nil { @@ -82,10 +85,11 @@ func initAppWithScreen(screen tcell.Screen) { } encoding.Register() tcell.SetEncodingFallback(tcell.EncodingFallbackUTF8) + MustLoadColorScheme() if err := screen.Init(); err != nil { log.Fatalf("%+v", err) } - screen.SetStyle(DefaultStyle) + screen.SetStyle(DefaultStyle()) screen.EnableMouse() screen.EnablePaste() screen.Clear() @@ -105,6 +109,7 @@ func initAppWithScreen(screen tcell.Screen) { keepAlive: make(map[interface{}]bool), dirty: true, spinner: s, + style: DefaultStyle(), } } @@ -144,7 +149,7 @@ func (a *App) Render() { time.Sleep(FPSMilliseconds) return } - a.screen.Fill(' ', DefaultStyle) + a.screen.Fill(' ', a.style) if a.loading { a.spinner.Draw(a.screen) } @@ -163,7 +168,7 @@ func (a *App) Close() { } a.closed = true a.screen.DisableMouse() - a.screen.Fill(' ', DefaultStyle) + a.screen.Fill(' ', a.style) a.screen.Show() a.screen.Fini() close(a.keyEvent) @@ -309,7 +314,7 @@ func (a *App) ClearNow() { a.clear() // a.screen.Clear() is preserving terminal buffer (not alternate screen buffer) :/ different then in 1.3 //a.screen.Clear() - a.screen.Fill(' ', DefaultStyle) + a.screen.Fill(' ', a.style) a.screen.HideCursor() } diff --git a/internal/app/colors.go b/internal/app/colors.go new file mode 100644 index 0000000..17bdbd0 --- /dev/null +++ b/internal/app/colors.go @@ -0,0 +1,115 @@ +package app + +import ( + "fmt" + "github.com/gdamore/tcell/v2" + "gopkg.in/yaml.v3" + "os" + "strings" +) + +var ( + schemeMap map[string]interface{} + colorsMap = map[string]tcell.Color{} +) + +func Color(c string) tcell.Color { + if color, ok := colorsMap[c]; ok { + return color + } + parts := strings.Split(c, ".") + var t interface{} + var hex string + t = schemeMap + for _, p := range parts { + if m, ok := t.(map[string]interface{}); ok { + t = m[p] + } + if h, ok := t.(string); ok { + hex = h + } + } + color := tcell.GetColor(hex) + colorsMap[c] = color + return color +} + +func MustLoadColorScheme() map[string]interface{} { + d, _ := os.UserHomeDir() + p := fmt.Sprintf("%s/.fjira/colors.yml", d) + b, err := os.ReadFile(p) + if err != nil { + schemeMap = parseYMLStr(defaultColorsYML()) + return schemeMap + } + schemeMap = parseYMLStr(string(b)) + return schemeMap +} + +func parseYMLStr(y string) map[string]interface{} { + var yml map[string]interface{} + err := yaml.Unmarshal([]byte(y), &yml) + if err != nil { + Error(err.Error()) + return map[string]interface{}{} + } + return yml +} + +func defaultColorsYML() string { + return ` +default: + background: "#161616" + foreground: "#c7c7c7" + foreground2: "#FFFFFF" + +finder: + cursor: "#8B0000" + title: "#E9CE58" + match: "#90EE90" + highlight: + background: "#3A3A3A" + foreground: "#FFFFFF" + match: "#E0FFFF" + +navigation: + top: + background: "#5F875f" + foreground1: "#FFFFFF" + foreground2: "#151515" + bottom: + background: "#5F87AF" + foreground1: "#FFFFFF" + foreground2: "#151515" + +details: + foreground: "#696969" + +boards: + title: + foreground: "#ecce58" + headers: + background: "#5F875f" + foreground: "#FFFFFF" + column: + background: "#232323" + foreground: "#ffffff" + highlight: + background: "#484848" + foreground: "#ffffff" + selection: + background: "#8B0000" + foreground: "#ffffff" + +spinner: + accent: "#FF0000" + +alerts: + success: + background: "#F5F5F5" + foreground: "#006400" + error: + background: "#F5F5F5" + foreground: "#8B0000" +` +} diff --git a/internal/app/colors_test.go b/internal/app/colors_test.go new file mode 100644 index 0000000..2223efc --- /dev/null +++ b/internal/app/colors_test.go @@ -0,0 +1,62 @@ +package app + +import ( + "fmt" + os2 "github.com/mk-5/fjira/internal/os" + "github.com/stretchr/testify/assert" + "os" + "testing" +) + +func TestColor(t *testing.T) { + MustLoadColorScheme() + + type args struct { + c string + } + tests := []struct { + name string + args args + want int32 + }{ + {"should get color from default colors", args{c: "navigation.top.background"}, 6260575}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, Color(tt.args.c).Hex(), "Color(%v)", tt.args.c) + }) + } +} + +func TestMustLoadColorScheme(t *testing.T) { + tests := []struct { + name string + }{ + {"should load color scheme from user directory"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // given + f := ` +navigation: + top: + background: "#0000FF" + foreground1: "#EEEEEE" + +` + tempDir := t.TempDir() + _ = os2.SetUserHomeDir(tempDir) + _ = os.MkdirAll(fmt.Sprintf("%s/.fjira", tempDir), os.ModePerm) + p := fmt.Sprintf("%s/.fjira/colors.yml", tempDir) + err := os.WriteFile(p, []byte(f), 0644) + assert.Nil(t, err) + + // when + MustLoadColorScheme() + + // then + assert.Equalf(t, int32(255), Color("navigation.top.background").Hex(), "MustLoadColorScheme()") + assert.Equalf(t, int32(15658734), Color("navigation.top.foreground1").Hex(), "MustLoadColorScheme()") + }) + } +} diff --git a/internal/app/confirmation.go b/internal/app/confirmation.go index a36fa75..36e0dbf 100644 --- a/internal/app/confirmation.go +++ b/internal/app/confirmation.go @@ -5,10 +5,12 @@ import ( ) type Confirmation struct { - Complete chan bool - message string - screenX int - screenY int + Complete chan bool + message string + screenX int + screenY int + style tcell.Style + questionMarkStyle tcell.Style } const ( @@ -17,10 +19,6 @@ const ( QuestionMark = "? " ) -var ( - QuestionMarkStyle = DefaultStyle.Bold(true).Foreground(tcell.ColorYellowGreen) -) - func Confirm(app *App, message string) bool { confirmation := newConfirmation(message) app.AddDrawable(confirmation) @@ -33,14 +31,16 @@ func Confirm(app *App, message string) bool { func newConfirmation(message string) *Confirmation { return &Confirmation{ - Complete: make(chan bool), - message: message, + Complete: make(chan bool), + message: message, + style: DefaultStyle(), + questionMarkStyle: DefaultStyle().Bold(true).Foreground(Color("finder.title")), } } func (c *Confirmation) Draw(screen tcell.Screen) { - DrawText(screen, 0, c.screenY-2, QuestionMarkStyle, QuestionMark) - DrawText(screen, 2, c.screenY-2, DefaultStyle, c.message) + DrawText(screen, 0, c.screenY-2, c.questionMarkStyle, QuestionMark) + DrawText(screen, 2, c.screenY-2, c.style, c.message) } func (c *Confirmation) Resize(screenX, screenY int) { diff --git a/internal/app/flash.go b/internal/app/flash.go index df257e2..02ae3db 100644 --- a/internal/app/flash.go +++ b/internal/app/flash.go @@ -3,18 +3,12 @@ package app import ( "fmt" "time" - - "github.com/gdamore/tcell/v2" -) - -var ( - errorStyle = DefaultStyle.Foreground(tcell.ColorDarkRed).Background(tcell.ColorWhiteSmoke) - successStyle = DefaultStyle.Foreground(tcell.ColorDarkGreen).Background(tcell.ColorWhiteSmoke) ) func Error(message string) { app := GetApp() errorMessage := fmt.Sprintf("Error! -%s", message) + errorStyle := DefaultStyle().Foreground(Color("alerts.error.foreground")).Background(Color("alerts.error.background")) errorBox := NewTextBox(app.ScreenX/2-len(errorMessage)/2, app.ScreenY-1, errorStyle, errorStyle, errorMessage) GetApp().AddFlash(errorBox, 5*time.Second) } @@ -22,6 +16,7 @@ func Error(message string) { func Success(message string) { app := GetApp() successMessage := fmt.Sprintf("Success! %s", message) + successStyle := DefaultStyle().Foreground(Color("alerts.success.foreground")).Background(Color("alerts.success.background")) successBox := NewTextBox(app.ScreenX/2-len(successMessage)/2, app.ScreenY-1, successStyle, successStyle, successMessage) GetApp().AddFlash(successBox, 3*time.Second) } diff --git a/internal/app/fuzzy_find.go b/internal/app/fuzzy_find.go index ac14fb4..4c0ddee 100644 --- a/internal/app/fuzzy_find.go +++ b/internal/app/fuzzy_find.go @@ -30,6 +30,14 @@ type FuzzyFind struct { supplierDebounce func(f func()) debounceDisabled bool disableFuzzyMatch bool + + boldMatchStyle tcell.Style + cursorStyle tcell.Style + highlightDefault tcell.Style + highlightBold tcell.Style + boldStyle tcell.Style + titleStyle tcell.Style + defaultStyle tcell.Style } type FuzzyFindResult struct { @@ -45,15 +53,6 @@ const ( SearchResultsPivot = 6 ) -var ( - boldMatchStyle = DefaultStyle.Foreground(tcell.ColorLightGreen).Underline(true).Bold(true) - boldRedStyle = DefaultStyle.Foreground(tcell.ColorDarkRed).Bold(true) - highlightDefault = DefaultStyle.Foreground(tcell.ColorWhite).Background(tcell.NewRGBColor(58, 58, 58)) - highlightBold = highlightDefault.Foreground(tcell.ColorLightCyan).Bold(true) - boldStyle = DefaultStyle.Bold(true) - titleStyle = DefaultStyle.Italic(true).Foreground(tcell.NewRGBColor(236, 206, 88)) -) - func NewFuzzyFind(title string, records []string) *FuzzyFind { matchesAll := make(fuzzy.Matches, 0, MaxResults) // TODO - not super optimize way to store results.. @@ -63,6 +62,7 @@ func NewFuzzyFind(title string, records []string) *FuzzyFind { Index: i, }) } + highlightDefaultStyle := DefaultStyle().Foreground(Color("finder.highlight.foreground")).Background(Color("finder.highlight.background")) return &FuzzyFind{ Complete: make(chan FuzzyFindResult), records: records, @@ -78,10 +78,19 @@ func NewFuzzyFind(title string, records []string) *FuzzyFind { MarginBottom: 1, debounceDisabled: false, disableFuzzyMatch: false, + + boldMatchStyle: DefaultStyle().Foreground(Color("finder.match")).Underline(true).Bold(true), + cursorStyle: DefaultStyle().Foreground(Color("finder.cursor")).Bold(true), + highlightDefault: highlightDefaultStyle, + highlightBold: highlightDefaultStyle.Foreground(Color("finder.highlight.match")).Bold(true), + boldStyle: DefaultStyle().Bold(true), + titleStyle: DefaultStyle().Italic(true).Foreground(Color("finder.title")), + defaultStyle: DefaultStyle(), } } func NewFuzzyFindWithProvider(title string, recordsProvider func(query string) []string) *FuzzyFind { + highlightDefaultStyle := DefaultStyle().Foreground(Color("finder.highlight.foreground")).Background(Color("finder.highlight.background")) return &FuzzyFind{ Complete: make(chan FuzzyFindResult), records: nil, @@ -98,6 +107,14 @@ func NewFuzzyFindWithProvider(title string, recordsProvider func(query string) [ MarginBottom: 1, debounceDisabled: false, disableFuzzyMatch: false, + + boldMatchStyle: DefaultStyle().Foreground(Color("finder.match")).Underline(true).Bold(true), + cursorStyle: DefaultStyle().Foreground(Color("finder.cursor")).Bold(true), + highlightDefault: highlightDefaultStyle, + highlightBold: highlightDefaultStyle.Foreground(Color("finder.highlight.match")).Bold(true), + boldStyle: DefaultStyle().Bold(true), + titleStyle: DefaultStyle().Italic(true).Foreground(Color("finder.title")), + defaultStyle: DefaultStyle(), } } @@ -109,11 +126,11 @@ func (f *FuzzyFind) Draw(screen tcell.Screen) { } f.drawRecords(screen) if f.title != "" { - DrawText(screen, 2, f.screenY-ResultsMarginBottom-f.MarginBottom+1, titleStyle, f.title) + DrawText(screen, 2, f.screenY-ResultsMarginBottom-f.MarginBottom+1, f.titleStyle, f.title) } - DrawText(screen, f.screenX-len(f.fuzzyStatus)-2, f.screenY-ResultsMarginBottom-f.MarginBottom+1, titleStyle, f.fuzzyStatus) - DrawText(screen, 0, f.screenY-1-f.MarginBottom, boldStyle, WriteIndicator) - DrawText(screen, 2, f.screenY-1-f.MarginBottom, DefaultStyle, f.query) + DrawText(screen, f.screenX-len(f.fuzzyStatus)-2, f.screenY-ResultsMarginBottom-f.MarginBottom+1, f.titleStyle, f.fuzzyStatus) + DrawText(screen, 0, f.screenY-1-f.MarginBottom, f.boldStyle, WriteIndicator) + DrawText(screen, 2, f.screenY-1-f.MarginBottom, f.defaultStyle, f.query) screen.ShowCursor(2+len(f.query), f.screenY-1-f.MarginBottom) } @@ -231,12 +248,12 @@ func (f *FuzzyFind) drawRecords(screen tcell.Screen) { indexDelta := ClampInt(f.selected-row+SearchResultsPivot, 0, matchesLen-1) for index := indexDelta; index < matchesLen && row > f.MarginTop; index++ { match := f.matches[index] - currentStyleDefault = DefaultStyle - currentStyleBold = boldMatchStyle + currentStyleDefault = f.defaultStyle + currentStyleBold = f.boldMatchStyle if index == f.selected { - DrawText(screen, 0, row, boldRedStyle, WriteIndicator) - currentStyleDefault = highlightDefault - currentStyleBold = highlightBold + DrawText(screen, 0, row, f.cursorStyle, WriteIndicator) + currentStyleDefault = f.highlightDefault + currentStyleBold = f.highlightBold } runeI := 0 for i, s := range match.Str { diff --git a/internal/app/spinner.go b/internal/app/spinner.go index eb1cc86..d3c4821 100644 --- a/internal/app/spinner.go +++ b/internal/app/spinner.go @@ -14,9 +14,9 @@ func NewSimpleSpinner() *SpinnerTCell { return &SpinnerTCell{ spinner: []string{".....", "....", ".."}, styles: []tcell.Style{ - DefaultStyle, DefaultStyle.Foreground(tcell.ColorRed).Bold(true), DefaultStyle, + DefaultStyle(), DefaultStyle().Foreground(Color("spinner.accent")).Bold(true), DefaultStyle(), }, - textStyle: DefaultStyle.Italic(true).Blink(true), + textStyle: DefaultStyle().Italic(true).Blink(true), spinnerIndex: new(int), } } diff --git a/internal/app/text_box.go b/internal/app/text_box.go index 4e12512..20c766c 100644 --- a/internal/app/text_box.go +++ b/internal/app/text_box.go @@ -21,7 +21,7 @@ func NewTextBox(x, y int, style tcell.Style, borderStyle tcell.Style, text strin textStyle: style, borderStyle: borderStyle, text: text, - bgStyle: DefaultStyle, + bgStyle: DefaultStyle(), } } diff --git a/internal/boards/board_view.go b/internal/boards/board_view.go index 642caa6..3c43bf1 100644 --- a/internal/boards/board_view.go +++ b/internal/boards/board_view.go @@ -19,14 +19,6 @@ const ( issueFetchBatchSize = 100 ) -var ( - columnHeaderStyle = ui.TopBarItemDefault - issueStyle = app.DefaultStyle.Background(tcell.NewRGBColor(35, 35, 35)).Foreground(tcell.ColorWhite) - cursorIssueStyle = app.DefaultStyle.Foreground(tcell.ColorWhite).Background(tcell.NewRGBColor(72, 72, 72)) - selectedIssueStyle = app.DefaultStyle.Background(tcell.ColorDarkRed).Foreground(tcell.ColorWhite).Bold(true) - titleStyle = app.DefaultStyle.Italic(true).Foreground(tcell.NewRGBColor(236, 206, 88)) -) - type boardView struct { app.View api jira.Api @@ -54,6 +46,11 @@ type boardView struct { scrollX int scrollY int columnSize int + columnHeaderStyle tcell.Style + issueStyle tcell.Style + highlightIssueStyle tcell.Style + selectedIssueStyle tcell.Style + titleStyle tcell.Style } func NewBoardView(project *jira.Project, boardConfiguration *jira.BoardConfiguration, filterJQL string, api jira.Api) app.View { @@ -105,6 +102,11 @@ func NewBoardView(project *jira.Project, boardConfiguration *jira.BoardConfigura scrollX: 0, highlightedIssue: &jira.Issue{}, columnSize: 28, + columnHeaderStyle: app.DefaultStyle().Background(app.Color("boards.headers.background")).Foreground(app.Color("boards.headers.foreground")), + issueStyle: app.DefaultStyle().Background(app.Color("boards.column.background")).Foreground(app.Color("boards.column.foreground")), + highlightIssueStyle: app.DefaultStyle().Foreground(app.Color("boards.highlight.foreground")).Background(app.Color("boards.highlight.background")), + selectedIssueStyle: app.DefaultStyle().Background(app.Color("boards.selection.background")).Foreground(app.Color("boards.selection.foreground")).Bold(true), + titleStyle: app.DefaultStyle().Italic(true).Foreground(app.Color("boards.title.foreground")), } } @@ -123,18 +125,18 @@ func (b *boardView) Draw(screen tcell.Screen) { continue } if b.highlightedIssue.Id == issue.Id { - var style = &cursorIssueStyle + var style = &b.highlightIssueStyle if b.issueSelected { - style = &selectedIssueStyle + style = &b.selectedIssueStyle } app.DrawTextLimited(screen, x-b.scrollX, y+topMargin-b.scrollY, x+b.columnSize-b.scrollX, y+1+topMargin, *style, b.issuesSummaries[issue.Id]) continue } - app.DrawTextLimited(screen, x-b.scrollX, y+topMargin-b.scrollY, x+b.columnSize-b.scrollX, y+1+topMargin, issueStyle, b.issuesSummaries[issue.Id]) + app.DrawTextLimited(screen, x-b.scrollX, y+topMargin-b.scrollY, x+b.columnSize-b.scrollX, y+1+topMargin, b.issueStyle, b.issuesSummaries[issue.Id]) } if b.highlightedIssue != nil { - app.DrawText(screen, 0, 1, titleStyle, app.WriteIndicator) - app.DrawText(screen, 2, 1, titleStyle, b.issuesSummaries[b.highlightedIssue.Id]) + app.DrawText(screen, 0, 1, b.titleStyle, app.WriteIndicator) + app.DrawText(screen, 2, 1, b.titleStyle, b.issuesSummaries[b.highlightedIssue.Id]) } if !b.issueSelected { b.bottomBar.Draw(screen) @@ -241,7 +243,7 @@ func (b *boardView) HandleKeyEvent(ev *tcell.EventKey) { func (b *boardView) drawColumnsHeaders(screen tcell.Screen) { b.tmpX = 0 for _, column := range b.columns { - app.DrawText(screen, b.tmpX-b.scrollX, topMargin, columnHeaderStyle, centerString(column, b.columnSize)) + app.DrawText(screen, b.tmpX-b.scrollX, topMargin, b.columnHeaderStyle, centerString(column, b.columnSize)) b.tmpX += b.columnSize + 1 } } diff --git a/internal/comments/parser.go b/internal/comments/parser.go index 295178d..23abddd 100644 --- a/internal/comments/parser.go +++ b/internal/comments/parser.go @@ -15,7 +15,7 @@ func ParseCommentsFromIssue(issue *jira.Issue, limitX, limitY int) []Comment { for _, comment := range issue.Fields.Comment.Comments { title := fmt.Sprintf("%s, %s", comment.Created, comment.Author.DisplayName) body := fmt.Sprintf("\n%s", comment.Body) - lines := app.DrawTextLimited(nil, 0, 0, limitX, limitY, app.DefaultStyle, comment.Body) + 2 + lines := app.DrawTextLimited(nil, 0, 0, limitX, limitY, app.DefaultStyle(), comment.Body) + 2 cs = append(cs, Comment{ Title: title, Body: body, diff --git a/internal/fjira/fjira.go b/internal/fjira/fjira.go index 53e6f36..084fd36 100644 --- a/internal/fjira/fjira.go +++ b/internal/fjira/fjira.go @@ -78,7 +78,7 @@ func CreateNewFjira(settings *workspaces.WorkspaceSettings) *Fjira { func (f *Fjira) Run(args *CliArgs) { x := app.ClampInt(f.app.ScreenX/2-18, 0, f.app.ScreenX) y := app.ClampInt(f.app.ScreenY/2-4, 0, f.app.ScreenY) - welcomeText := app.NewText(x, y, app.DefaultStyle, WelcomeMessage) + welcomeText := app.NewText(x, y, app.DefaultStyle(), WelcomeMessage) f.app.AddDrawable(welcomeText) f.registerGoTos() go f.bootstrap(args) diff --git a/internal/issues/issue.go b/internal/issues/issue.go index 6ac2944..ef40f19 100644 --- a/internal/issues/issue.go +++ b/internal/issues/issue.go @@ -31,10 +31,11 @@ type issueView struct { labelsLen int comments []comments.Comment lastY int + boxTitleStyle tcell.Style + defaultStyle tcell.Style } var ( - boxTitleStyle = app.DefaultStyle.Foreground(tcell.ColorDimGrey) issueNavItems = []ui.NavItemConfig{ ui.NavItemConfig{Action: ui.ActionStatusChange, Text1: ui.MessageChangeStatus, Text2: "[s]", Rune: 's'}, ui.NavItemConfig{Action: ui.ActionAssigneeChange, Text1: ui.MessageAssignUser, Text2: "[a]", Rune: 'a'}, @@ -60,17 +61,19 @@ func NewIssueView(issue *jira.Issue, goBackFn func(), api jira.Api) app.View { labelsLen := len(ls) return &issueView{ - api: api, - bottomBar: bottomBar, - topBar: issueActionBar, - issue: issue, - scrollY: 0, - body: issue.Fields.Description, - comments: cs, - labels: ls, - labelsLen: labelsLen, - summaryLen: len(issue.Fields.Summary), - goBackFn: goBackFn, + api: api, + bottomBar: bottomBar, + topBar: issueActionBar, + issue: issue, + scrollY: 0, + body: issue.Fields.Description, + comments: cs, + labels: ls, + labelsLen: labelsLen, + summaryLen: len(issue.Fields.Summary), + goBackFn: goBackFn, + boxTitleStyle: app.DefaultStyle().Foreground(app.Color("details.foreground")), + defaultStyle: app.DefaultStyle(), } } @@ -83,29 +86,29 @@ func (view *issueView) Destroy() { func (view *issueView) Draw(screen tcell.Screen) { if view.fuzzyFind == nil { - app.DrawBox(screen, 1, 2-view.scrollY, view.summaryLen+4, 4-view.scrollY, boxTitleStyle) - app.DrawText(screen, 2, 2-view.scrollY, boxTitleStyle, ui.MessageSummary) - app.DrawText(screen, 3, 3-view.scrollY, app.DefaultStyle, view.issue.Fields.Summary) + app.DrawBox(screen, 1, 2-view.scrollY, view.summaryLen+4, 4-view.scrollY, view.boxTitleStyle) + app.DrawText(screen, 2, 2-view.scrollY, view.boxTitleStyle, ui.MessageSummary) + app.DrawText(screen, 3, 3-view.scrollY, view.defaultStyle, view.issue.Fields.Summary) view.lastY = 2 - view.scrollY + 2 if view.labels != "" { - app.DrawBox(screen, 1, view.lastY+1, view.labelsLen+4, view.lastY+3, boxTitleStyle) - app.DrawText(screen, 2, view.lastY+1, boxTitleStyle, ui.MessageLabels) - app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.lastY+2, app.DefaultStyle, view.labels) + app.DrawBox(screen, 1, view.lastY+1, view.labelsLen+4, view.lastY+3, view.boxTitleStyle) + app.DrawText(screen, 2, view.lastY+1, view.boxTitleStyle, ui.MessageLabels) + app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.lastY+2, view.defaultStyle, view.labels) view.lastY = view.lastY + 3 } - app.DrawBox(screen, 1, view.lastY+1, view.descriptionLimitX+4, view.lastY+1+view.descriptionLines+4, boxTitleStyle) - app.DrawText(screen, 2, view.lastY+1, boxTitleStyle, ui.MessageDescription) - app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.descriptionLimitY, app.DefaultStyle, view.body) + app.DrawBox(screen, 1, view.lastY+1, view.descriptionLimitX+4, view.lastY+1+view.descriptionLines+4, view.boxTitleStyle) + app.DrawText(screen, 2, view.lastY+1, view.boxTitleStyle, ui.MessageDescription) + app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.descriptionLimitY, view.defaultStyle, view.body) view.lastY = view.lastY + view.descriptionLines + 6 for _, comment := range view.comments { - app.DrawBox(screen, 1, view.lastY+1, view.descriptionLimitX+4, view.lastY+1+comment.Lines+2, boxTitleStyle) - app.DrawText(screen, 2, view.lastY+1, boxTitleStyle, comment.Title) - app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.descriptionLimitY, app.DefaultStyle, comment.Body) + app.DrawBox(screen, 1, view.lastY+1, view.descriptionLimitX+4, view.lastY+1+comment.Lines+2, view.boxTitleStyle) + app.DrawText(screen, 2, view.lastY+1, view.boxTitleStyle, comment.Title) + app.DrawTextLimited(screen, 3, view.lastY+2, view.descriptionLimitX, view.descriptionLimitY, view.defaultStyle, comment.Body) view.lastY = view.lastY + 1 + comment.Lines + 3 } } @@ -127,7 +130,7 @@ func (view *issueView) Update() { func (view *issueView) Resize(screenX, screenY int) { view.descriptionLimitX = app.ClampInt(int(math.Floor(float64(screenX)*0.9)), 1, 10000) view.descriptionLimitY = 1000 - view.descriptionLines = app.DrawTextLimited(nil, 0, 0, view.descriptionLimitX, view.descriptionLimitY, app.DefaultStyle, view.body) + 1 + view.descriptionLines = app.DrawTextLimited(nil, 0, 0, view.descriptionLimitX, view.descriptionLimitY, view.defaultStyle, view.body) + 1 commentsLines := 0 view.comments = comments.ParseCommentsFromIssue(view.issue, view.descriptionLimitX, view.descriptionLimitY) for _, comment := range view.comments { diff --git a/internal/ui/navigation.go b/internal/ui/navigation.go index 3503a89..ebd9992 100644 --- a/internal/ui/navigation.go +++ b/internal/ui/navigation.go @@ -23,16 +23,6 @@ const ( ActionAddLabel ActionSelect ActionUnselect - ActionNew - ActionDelete -) - -var ( - BottomBarItemDefaultStyle = app.DefaultStyle.Background(tcell.NewRGBColor(95, 135, 175)).Foreground(tcell.ColorWhite) - BottomBarActionBarItemBold = app.DefaultStyle.Bold(true).Foreground(tcell.ColorDarkKhaki) - BottomBarActionBarKeyBold = BottomBarItemDefaultStyle.Foreground(tcell.NewRGBColor(21, 21, 21)) - TopBarItemDefault = app.DefaultStyle.Background(tcell.NewRGBColor(95, 135, 95)).Foreground(tcell.ColorWhite) - TopBarItemBold = TopBarItemDefault.Foreground(app.AppBackground) // DarkOrange looks good here as well ) type NavItemConfig struct { @@ -48,8 +38,8 @@ func CreateBottomActionBar(text1 string, text2 string) *app.ActionBar { actionBar.AddItemWithStyles( text1, text2, - BottomBarItemDefaultStyle, - BottomBarActionBarItemBold, + bottomBarItemDefaultStyle(), + bottomBarActionBarItemBold(), ) return actionBar } @@ -59,7 +49,7 @@ func CreateTopActionBar(text1 string, text2 string) *app.ActionBar { actionBar.AddItemWithStyles( text1, text2, - TopBarItemDefault, TopBarItemBold, + topBarItemDefault(), topBarItemBold(), ) return actionBar } @@ -71,8 +61,8 @@ func CreateBottomActionBarWithItems(items []NavItemConfig) *app.ActionBar { Id: int(i.Action), Text1: i.Text1, Text2: i.Text2, - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: i.Key, TriggerRune: i.Rune, }) @@ -87,8 +77,8 @@ func CreateTopActionBarWithItems(items []NavItemConfig) *app.ActionBar { Id: int(i.Action), Text1: i.Text1, Text2: i.Text2, - Text1Style: TopBarItemDefault, - Text2Style: TopBarItemBold, + Text1Style: topBarItemDefault(), + Text2Style: topBarItemBold(), TriggerKey: i.Key, TriggerRune: i.Rune, }) @@ -116,8 +106,8 @@ func NewCancelBarItem() *app.ActionBarItem { Id: int(ActionCancel), Text1: "Cancel ", Text2: "[ESC]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: tcell.KeyEscape, } } @@ -126,8 +116,8 @@ func CreateScrollBarItem() *app.ActionBarItem { return &app.ActionBarItem{ Text1: MessageScroll, Text2: "[↑↓]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: -1, TriggerRune: -1, } @@ -137,8 +127,8 @@ func CreateArrowsNavigateItem() *app.ActionBarItem { return &app.ActionBarItem{ Text1: MessageNavigate, Text2: "[←→↑↓]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: -1, TriggerRune: -1, } @@ -148,8 +138,8 @@ func CreateMoveArrowsItem() *app.ActionBarItem { return &app.ActionBarItem{ Text1: MessageMoveIssue, Text2: "[←→]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: -1, TriggerRune: -1, } @@ -160,8 +150,8 @@ func CreateSelectItem() *app.ActionBarItem { Id: int(ActionSelect), Text1: MessageSelect, Text2: "[enter]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: tcell.KeyEnter, TriggerRune: -1, } @@ -172,8 +162,8 @@ func CreateUnSelectItem() *app.ActionBarItem { Id: int(ActionUnselect), Text1: MessageUnselect, Text2: "[enter]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: tcell.KeyEnter, TriggerRune: -1, } @@ -184,8 +174,8 @@ func NewYesBarItem() *app.ActionBarItem { Id: int(ActionYes), Text1: MessageYes, Text2: "[y]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerRune: 'y', } } @@ -195,8 +185,8 @@ func NewOpenBarItem() *app.ActionBarItem { Id: int(ActionOpen), Text1: MessageOpen, Text2: "[o]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerRune: 'o', } } @@ -206,19 +196,28 @@ func NewSaveBarItem() *app.ActionBarItem { Id: int(ActionYes), Text1: MessageSave, Text2: "[F1]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, + Text1Style: bottomBarItemDefaultStyle(), + Text2Style: bottomBarActionBarKeyBold(), TriggerKey: tcell.KeyF1, } } -func NewDeleteItem() *app.ActionBarItem { - return &app.ActionBarItem{ - Id: int(ActionDelete), - Text1: MessageDelete, - Text2: "[F2]", - Text1Style: BottomBarItemDefaultStyle, - Text2Style: BottomBarActionBarKeyBold, - TriggerKey: tcell.KeyF2, - } +func bottomBarItemDefaultStyle() tcell.Style { + return app.DefaultStyle().Background(app.Color("navigation.bottom.background")).Foreground(app.Color("navigation.bottom.foreground1")) +} + +func bottomBarActionBarItemBold() tcell.Style { + return app.DefaultStyle().Bold(true).Foreground(app.Color("navigation.bottom.foreground2")) +} + +func bottomBarActionBarKeyBold() tcell.Style { + return bottomBarItemDefaultStyle().Foreground(app.Color("navigation.bottom.foreground2")) +} + +func topBarItemDefault() tcell.Style { + return app.DefaultStyle().Background(app.Color("navigation.top.background")).Foreground(app.Color("navigation.top.foreground1")) +} + +func topBarItemBold() tcell.Style { + return topBarItemDefault().Foreground(app.Color("navigation.top.foreground2")) // DarkOrange looks good here as well } diff --git a/internal/ui/text_writer_view.go b/internal/ui/text_writer_view.go index d07a265..460ad53 100644 --- a/internal/ui/text_writer_view.go +++ b/internal/ui/text_writer_view.go @@ -14,6 +14,7 @@ type TextWriterView struct { buffer bytes.Buffer text string headerStyle tcell.Style + style tcell.Style args TextWriterArgs } @@ -43,7 +44,8 @@ func NewTextWriterView(args *TextWriterArgs) app.View { bottomBar: bottomBar, text: "", args: *args, - headerStyle: app.DefaultStyle.Foreground(tcell.ColorWhite).Underline(true), + headerStyle: app.DefaultStyle().Foreground(app.Color("default.foreground2")).Underline(true), + style: app.DefaultStyle(), } } @@ -57,7 +59,7 @@ func (view *TextWriterView) Destroy() { func (view *TextWriterView) Draw(screen tcell.Screen) { app.DrawText(screen, 1, 2, view.headerStyle, view.args.Header) - app.DrawTextLimited(screen, 1, 4, view.args.MaxLength, 100, app.DefaultStyle, view.text) + app.DrawTextLimited(screen, 1, 4, view.args.MaxLength, 100, view.style, view.text) view.bottomBar.Draw(screen) }