diff --git a/colors/blue.yml b/colors/blue.yml new file mode 100644 index 0000000..7196ea6 --- /dev/null +++ b/colors/blue.yml @@ -0,0 +1,52 @@ +default: + text: white, navy +editor: + comment: bold yellow + icomment: bold white + origin: bold lcyan + tearline: bold lcyan + tagline: bold lcyan + kludge: bold gray +dialog: + border: bold white + item: white + selection: bold white, dcyan + title: bold red +messageHeader: + title: bold white, dcyan + border: bold white + header: bold white + item: white + highlight: bold white + selection: default, dcyan + window: default, default +messageList: + border: silver + header: bold green + title: bold white, dcyan + prompt: white + item: silver + highlight: bold lcyan + selection: bold white, dcyan +areaList: + border: silver + header: bold green + title: bold white, dcyan + prompt: white + item: silver + highlight: bold lcyan + selection: bold white, dcyan +areaListModal: + border: silver + header: bold green + title: bold white, dcyan + prompt: white + item: silver + highlight: bold lcyan + selection: bold white, dcyan +statusbar: + text: bold white, navy +help: + border: bold white + title: bold yellow, green + text: default diff --git a/colors/default.yml b/colors/default.yml new file mode 100644 index 0000000..8cdb88d --- /dev/null +++ b/colors/default.yml @@ -0,0 +1,56 @@ +# +# Default Color Scheme +# Use as reference when building your own one +# +default: + text: silver, black +editor: + comment: bold yellow + icomment: bold white + origin: bold white + tearline: bold white + tagline: bold white + kludge: bold gray +dialog: + border: bold red + item: bold silver + selection: bold silver, navy + title: bold yellow +messageHeader: + title: bold yellow + border: bold blue + header: bold silver + item: silver + highlight: bold silver + selection: silver, navy + window: default, default +messageList: + border: red + header: bold yellow + title: bold yellow + prompt: silver + item: silver + highlight: bold default + selection: bold white, navy +areaList: + border: blue + header: bold yellow + title: bold yellow + prompt: silver + item: silver + highlight: bold silver + selection: white, navy +areaListModal: + border: red + header: bold yellow + title: bold yellow + prompt: silver + item: silver + highlight: bold silver + selection: white, navy +statusbar: + text: bold white, navy +help: + border: bold blue + title: bold yellow + text: default diff --git a/gossiped.example.yml b/gossiped.example.yml index ae7e1fa..f244ef1 100644 --- a/gossiped.example.yml +++ b/gossiped.example.yml @@ -6,6 +6,8 @@ areafile: log: ./app.log template: gossiped.tpl origin: Just Origin +# Uncomment to enable blue colorscheme +#colorscheme: ./colors/blue.yml tearline: '' chrs: default: CP866 2 # http://ftsc.org/docs/fts-5003.001 @@ -17,13 +19,7 @@ areas: basetype: msg # msg, squish, jam - name: utf-8 chrs: UTF-8 4 -colors: - editor: - comment: bold yellow - icomment: bold white - origin: bold green - tearline: bold green - tagline: bold green - kludge: bold gray sorting: areas: unread # unread, default +statusbar: + clock: true diff --git a/gossiped.go b/gossiped.go index a182730..c37af8f 100644 --- a/gossiped.go +++ b/gossiped.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "github.com/askovpen/gossiped/pkg/areasconfig" "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/ui" @@ -52,24 +53,25 @@ func main() { return } } - + log.Println(fmt.Sprintf("reading configuration from %s", fn)) err := config.Read(fn) if err != nil { - log.Print(err) + log.Println(err) return } f, _ := os.OpenFile(config.Config.Log, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644) defer f.Close() log.SetOutput(f) log.SetFlags(log.LstdFlags | log.Lmicroseconds) + log.Print("reading areas") err = areasconfig.Read() if err != nil { log.Print(err) return } // ui.App, err = gocui.NewGui(gocui.OutputNormal) + log.Print("starting ui") app := ui.NewApp() - log.Print("start") if err = app.Run(); err != nil { log.Print("started ui") log.Print(err) diff --git a/pkg/config/city.go b/pkg/config/city.go new file mode 100644 index 0000000..39c9cae --- /dev/null +++ b/pkg/config/city.go @@ -0,0 +1,25 @@ +package config + +import ( + "gopkg.in/yaml.v3" + "io/ioutil" +) + +func readCity() { + yamlFile, err := ioutil.ReadFile("city.yaml") + if err != nil { + return + } + err = yaml.Unmarshal(yamlFile, &city) + if err != nil { + return + } +} + +// GetCity return city +func GetCity(sa string) string { + if val, ok := city[sa]; ok { + return val + } + return "unknown" +} diff --git a/pkg/config/colorparser.go b/pkg/config/colorparser.go new file mode 100644 index 0000000..fc23a31 --- /dev/null +++ b/pkg/config/colorparser.go @@ -0,0 +1,41 @@ +package config + +import ( + "log" + "regexp" + "strings" +) + +// ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object +// Colorschemes are made up of color-link statements linking a color group to a list of colors +// For example, color-link keyword (blue,red) makes all keywords have a blue foreground and +// red background +// Todo: Implement to read Golded schemes in future +func ParseColorscheme(text string) ColorScheme { + parser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`) + + lines := strings.Split(text, "\n") + + c := make(ColorScheme) + + for _, line := range lines { + if strings.TrimSpace(line) == "" || + strings.TrimSpace(line)[0] == '#' { + // Ignore this line + continue + } + matches := parser.FindSubmatch([]byte(line)) + if len(matches) == 3 { + link := string(matches[1]) + colors := string(matches[2]) + style, _ := StringToStyle(colors) + c[link] = style + if link == "default" { + style = StyleDefault + } + } else { + log.Println("Color-link statement is not valid:", line) + } + } + return c +} diff --git a/pkg/config/colorscheme.go b/pkg/config/colorscheme.go new file mode 100644 index 0000000..8719983 --- /dev/null +++ b/pkg/config/colorscheme.go @@ -0,0 +1,336 @@ +package config + +import ( + "errors" + "fmt" + "github.com/gdamore/tcell/v2" + "gopkg.in/yaml.v3" + "log" + "os" + "strconv" + "strings" +) + +const ( + ColorAreaStatusBar = "statusbar" + ColorAreaDialog = "dialog" + ColorAreaMessageList = "messageList" + ColorAreaMessageHeader = "messageHeader" + ColorAreaAreaList = "areaList" + ColorAreaAreaListModal = "areaListModal" + ColorAreaEditor = "editor" + ColorAreaHelp = "help" + ColorAreaDefault = "default" +) +const ( + ColorElementHeader = "header" + ColorElementSelection = "selection" + ColorElementTitle = "title" + ColorElementItem = "item" + ColorElementHighlight = "highlight" + ColorElementBorder = "border" + ColorElementText = "text" + ColorElementPrompt = "prompt" + ColorElementWindow = "window" +) +const ( + StyleUnderline = "underline" + StyleBold = "bold" + StyleReverse = "reverse" +) + +type ( + ColorScheme map[string]tcell.Style + ColorSchemeMap map[string]*ColorScheme + DefaultColorsMap map[string]*ColorMap +) + +var ( + uiColors = ColorSchemeMap{} + uiDefaultColors = DefaultColorsMap{ + ColorAreaDefault: { + ColorElementText: "silver, black", + }, + ColorAreaAreaList: { + ColorElementBorder: "blue", + ColorElementHeader: "bold yellow", + ColorElementTitle: "bold yellow", + ColorElementSelection: "white, navy", + ColorElementItem: "silver", + ColorElementHighlight: "bold silver", + ColorElementPrompt: "silver", + }, + ColorAreaAreaListModal: { + ColorElementBorder: "red", + ColorElementHeader: "bold yellow", + ColorElementTitle: "bold yellow", + ColorElementSelection: "white, navy", + ColorElementItem: "silver", + ColorElementHighlight: "bold silver", + ColorElementPrompt: "silver", + }, + ColorAreaMessageList: { + ColorElementSelection: "bold white, navy", + ColorElementHeader: "bold yellow", + ColorElementTitle: "bold yellow", + ColorElementItem: "silver", + ColorElementBorder: "red", + ColorElementHighlight: "bold default", + }, + ColorAreaEditor: { + "comment": "bold yellow", + "icomment": "bold white", + "origin": "bold white", + "tearline": "bold white", + "tagline": "bold white", + "kludge": "bold gray", + }, + ColorAreaHelp: { + ColorElementBorder: "bold blue", + ColorElementTitle: "bold yellow", + ColorElementText: "default", + }, + ColorAreaMessageHeader: { + ColorElementItem: "silver", + ColorElementHighlight: "bold silver", + ColorElementHeader: "bold silver", + ColorElementSelection: "silver, navy", + ColorElementBorder: "bold blue", + ColorElementTitle: "bold yellow", + ColorElementWindow: "default", + }, + ColorAreaDialog: { + ColorElementItem: "bold silver", + ColorElementSelection: "bold silver, navy", + ColorElementTitle: "bold yellow", + ColorElementBorder: "bold red", + }, + ColorAreaStatusBar: { + ColorElementText: "bold white, navy", + }, + } + + styleToMask = map[string]tcell.AttrMask{ + "B": tcell.AttrBold, + "U": tcell.AttrUnderline, + "I": tcell.AttrItalic, + "L": tcell.AttrBlink, + "D": tcell.AttrDim, + "S": tcell.AttrStrikeThrough, + "R": tcell.AttrReverse, + } +) + +func ProduceColorMapFromConfig(colorArea string, fallbackColors *ColorMap) (*ColorMap, error) { + var out = make(ColorMap) + var validKeys = make(map[string]bool) + if fallbackColors != nil { + for k, v := range *fallbackColors { + validKeys[k] = true + out[k] = v + } + } + var fallback = out + if Config.Colors[colorArea] == nil || len(Config.Colors[colorArea]) == 0 { + return &fallback, nil + } + var validation error = nil + for element, colorValue := range Config.Colors[colorArea] { + colorValue = strings.ToLower(strings.TrimSpace(colorValue)) + if !validKeys[element] { + validation = errors.Join( + validation, + errors.New("not valid element for area (element: "+element+", area: "+colorArea+")"), + ) + continue + } + if _, err := StringToStyle(colorValue); err != nil { + validation = errors.Join( + validation, + errors.New(err.Error()+" (element: "+element+", area: "+colorArea+")"), + ) + continue + } + out[element] = colorValue + } + return &out, validation +} + +// ProduceColorSchemeFromConfig +// colorArea: node name in gossiped.yml +// defaultColors: pointer to default ColorMap values +// returns pointer to ColorScheme object +func ProduceColorSchemeFromConfig(colorArea string, defaultColors *ColorMap) *ColorScheme { + scheme := ColorScheme{} + colors, err := ProduceColorMapFromConfig(colorArea, defaultColors) + if err != nil { + log.Println("Color parse errors: ", err) + } + for colorType, colorValue := range *colors { + scheme[colorType], _ = StringToStyle(colorValue) + } + return &scheme +} + +// StringToColor returns a tcell color from a string representation of a color +func StringToColor(str string) tcell.Color { + if num, err := strconv.Atoi(str); err == nil { + if num > 255 || num < 0 { + return tcell.ColorDefault + } + return tcell.PaletteColor(num) + } + return tcell.GetColor(str) +} + +// StringToStyle returns a style from a string +// The strings must be in the format "extra foregroundcolor,backgroundcolor" +// The 'extra' can be bold, reverse, or underline +func StringToStyle(str string) (tcell.Style, error) { + var errStack error + str = strings.ToLower(strings.TrimSpace(str)) + + if len(str) == 0 { + errStack = errors.New("empty color value") + return StyleDefault, errStack + } + + var fg, bg string + var split = strings.Split(str, ",") + if len(split) > 1 { + fg, bg = split[0], split[1] + } else { + fg = split[0] + } + fg = strings.TrimSpace(fg) + bg = strings.TrimSpace(bg) + + var styles = "" + var splitFg = strings.Split(fg, " ") + if len(splitFg) > 1 { + styles = strings.TrimSpace(splitFg[0]) + fg = strings.TrimSpace(splitFg[1]) + } else { + fg = strings.TrimSpace(splitFg[0]) + } + + var fgColor, bgColor, _ = StyleDefault.Decompose() + + if fg != "" && fg != "default" { + if _, ok := tcell.ColorNames[fg]; !ok { + errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown foreground color name \"%s\"", fg))) + } + fgColor = StringToColor(fg) + } + if bg != "" && bg != "default" { + if _, ok := tcell.ColorNames[bg]; !ok { + errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown background color name \"%s\"", bg))) + } + bgColor = StringToColor(bg) + } + + style := StyleDefault.Foreground(fgColor).Background(bgColor) + var splitStyles = strings.Split(styles, "|") + for _, v := range splitStyles { + v = strings.TrimSpace(v) + if v == StyleReverse { + style = style.Reverse(true) + } else if v == StyleUnderline { + style = style.Underline(true) + } else if v == StyleBold { + style = style.Bold(true) + } else if v != "" { + errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown style \"%s\"", v))) + } + } + return style, errStack +} + +// GetColor takes in a syntax group and returns the colorscheme's style for that group +func (colorscheme ColorScheme) GetColor(color string) tcell.Style { + st := StyleDefault + if color == "" { + return st + } + groups := strings.Split(color, ".") + if len(groups) > 1 { + curGroup := "" + for i, g := range groups { + if i != 0 { + curGroup += "." + } + curGroup += g + if style, ok := colorscheme[curGroup]; ok { + st = style + } + } + } else if style, ok := colorscheme[color]; ok { + st = style + } else { + st, _ = StringToStyle(color) + } + return st +} + +func MaskToStringStyle(attrMask tcell.AttrMask) string { + style := "" + for s, v := range styleToMask { + if (attrMask & v) != 0 { + style = style + strings.ToLower(s) + } + } + return style +} + +// GetColors for config section +func GetColors(section string) *ColorScheme { + if uiColors[section] == nil { + uiColors[section] = ProduceColorSchemeFromConfig(section, uiDefaultColors[section]) + } + return uiColors[section] +} + +func GetElementStyle(section string, element string) tcell.Style { + colors := GetColors(section) + value, ok := (*colors)[element] + if !ok { + return StyleDefault + } + return value +} + +func FormatTextWithStyle(text string, style tcell.Style) string { + fg, bg, attrs := style.Decompose() + return fmt.Sprintf("[%s:%s:%s]%s", fg.String(), bg.String(), MaskToStringStyle(attrs), text) +} + +// initColorAliases() +// append aliases to tcell +func initColorAliases() { + tcell.ColorNames["cyan"] = tcell.ColorDarkCyan + tcell.ColorNames["lcyan"] = tcell.ColorLightCyan + tcell.ColorNames["dcyan"] = tcell.ColorDarkCyan +} + +// readColors() +func readColors() error { + initColorAliases() + if Config.Colorscheme != "" { + yamlColors, err := os.ReadFile(Config.Colorscheme) + if err != nil { + return errors.New(fmt.Sprintf("cannot read color scheme file: %s", Config.Colorscheme)) + } + colorsBackup := Config.Colors + err = yaml.Unmarshal(yamlColors, &Config.Colors) + if err != nil { + log.Println(fmt.Sprintf("errors during read of color scheme file: %s", Config.Colorscheme)) + log.Println(fmt.Sprintf("yaml unmarshal errors: %v", err)) + Config.Colors = colorsBackup + } else { + log.Println(fmt.Sprintf("color scheme read successfully from file: %s", Config.Colorscheme)) + } + } + StyleDefault = GetElementStyle(ColorAreaDefault, ColorElementText) + StyleDefault.Attributes(tcell.AttrNone) + return nil +} diff --git a/pkg/ui/editor/colorscheme_test.go b/pkg/config/colorscheme_test.go similarity index 77% rename from pkg/ui/editor/colorscheme_test.go rename to pkg/config/colorscheme_test.go index e90ed24..29ed61c 100644 --- a/pkg/ui/editor/colorscheme_test.go +++ b/pkg/config/colorscheme_test.go @@ -1,8 +1,7 @@ -package editor +package config import ( "fmt" - "github.com/askovpen/gossiped/pkg/config" . "github.com/franela/goblin" "github.com/gdamore/tcell/v2" "regexp" @@ -11,13 +10,17 @@ import ( ) var ( - lineSplitter = regexp.MustCompile(`[\r\n]+`) + lineSplitter *regexp.Regexp ) +func init() { + lineSplitter = regexp.MustCompile(`[\r\n]+`) +} + func TestProduceColorMapFromConfig(t *testing.T) { g := Goblin(t) g.Describe("Check ProduceColorMapFromConfig()", func() { - config.Config.Colors = map[string]config.ColorMap{ + Config.Colors = map[string]ColorMap{ "editor": { "comment": "bold yellow,red", "icomment": "bold red", @@ -28,7 +31,7 @@ func TestProduceColorMapFromConfig(t *testing.T) { }, } g.It("override for existing color definitions", func() { - colors := config.ColorMap{ + colors := ColorMap{ "comment": "bold yellow,red", "icomment": "bold white", "origin": "bold white", @@ -36,47 +39,47 @@ func TestProduceColorMapFromConfig(t *testing.T) { "tagline": "bold white", "kludge": "bold gray", } - var produced, err = ProduceColorMapFromConfig(ConfigColorArea, &colors) + var produced, err = ProduceColorMapFromConfig("editor", &colors) g.Assert(err).IsNil() - g.Assert(produced["kludge"]).Equal("bold red") + g.Assert((*produced)["kludge"]).Equal("bold red") }) g.It("override for non-existing color definitions", func() { - colors := config.ColorMap{ + colors := ColorMap{ "comment": "bold yellow", } var errorsGot, errorsExpected = map[string]bool{}, map[string]bool{} - var area = ConfigColorArea + var area = "editor" var produced, err = ProduceColorMapFromConfig(area, &colors) g.Assert(err).IsNotNil() for _, i := range lineSplitter.Split(err.Error(), -1) { errorsGot[strings.TrimSpace(i)] = true } - for k, _ := range config.Config.Colors[area] { + for k := range Config.Colors[area] { if colors[k] == "" { errString := "not valid element for area (element: " + k + ", area: " + area + ")" errorsExpected[errString] = true } } g.Assert(errorsGot).Equal(errorsExpected) - g.Assert(produced["kludge"]).Equal("") + g.Assert((*produced)["kludge"]).Equal("") }) g.It("fallback mode - empty config area", func() { - colors := config.ColorMap{ + colors := ColorMap{ "comment": "bold yellow", } var produced, err = ProduceColorMapFromConfig("random-area", &colors) g.Assert(err).IsNil() g.Assert(produced).IsNotNil() - g.Assert(produced["kludge"]).Equal("") - g.Assert(produced["comment"]).Equal("bold yellow") + g.Assert((*produced)["kludge"]).Equal("") + g.Assert((*produced)["comment"]).Equal("bold yellow") }) g.It("invalid config values - empty value ", func() { - config.Config.Colors = map[string]config.ColorMap{ + Config.Colors = map[string]ColorMap{ "fictive": { "comment": "", }, } - colors := config.ColorMap{ + colors := ColorMap{ "comment": "bold yellow", } var area = "fictive" @@ -87,7 +90,7 @@ func TestProduceColorMapFromConfig(t *testing.T) { for _, i := range lineSplitter.Split(err.Error(), -1) { errorsGot[strings.TrimSpace(i)] = true } - for k, _ := range config.Config.Colors[area] { + for k := range Config.Colors[area] { if colors[k] != "" { errString := "empty color value (element: " + k + ", area: " + area + ")" errorsExpected[errString] = true @@ -98,37 +101,6 @@ func TestProduceColorMapFromConfig(t *testing.T) { }) } -func TestProduceColorSchemeFromConfig(t *testing.T) { - g := Goblin(t) - g.Describe("Check ProduceColorSchemeFromConfig", func() { - g.It("fallback mode (config values are absent)", func() { - config.Config.Colors = map[string]config.ColorMap{ - "editor": nil, - } - scheme := ProduceColorSchemeFromConfig() - for k, _ := range defaultColors { - g.Assert(scheme[k]).Equal(GetColor(defaultColors[k])) - } - }) - g.It("normal mode (config values are present)", func() { - config.Config.Colors = map[string]config.ColorMap{ - "editor": { - "comment": "bold yellow,red", - "icomment": "bold red", - "origin": "bold red", - "tearline": "bold red", - "tagline": "bold red", - "kludge": "bold red", - }, - } - scheme := ProduceColorSchemeFromConfig() - for k, _ := range defaultColors { - g.Assert(scheme[k]).Equal(GetColor(config.Config.Colors["editor"][k])) - } - }) - }) -} - func TestStringToStyle(t *testing.T) { g := Goblin(t) g.Describe("Check StringToStyle", func() { @@ -169,3 +141,47 @@ func TestStringToStyle(t *testing.T) { }) }) } + +func getDefaultColorsForEditor() ColorMap { + return ColorMap{ + "comment": "bold yellow", + "icomment": "bold white", + "origin": "bold white", + "tearline": "bold white", + "tagline": "bold white", + "kludge": "bold gray", + } +} + +func TestProduceColorSchemeFromConfig(t *testing.T) { + g := Goblin(t) + g.Describe("Check ProduceColorSchemeFromConfig", func() { + g.It("fallback mode (config values are absent)", func() { + Config.Colors = map[string]ColorMap{ + "editor": nil, + } + var defaultColors = getDefaultColorsForEditor() + scheme := ProduceColorSchemeFromConfig("editor", &defaultColors) + for k := range defaultColors { + g.Assert((*scheme)[k]).Equal((*scheme).GetColor(defaultColors[k])) + } + }) + g.It("normal mode (config values are present)", func() { + var defaultColors = getDefaultColorsForEditor() + Config.Colors = map[string]ColorMap{ + "editor": { + "comment": "bold yellow,red", + "icomment": "bold red", + "origin": "bold red", + "tearline": "bold red", + "tagline": "bold red", + "kludge": "bold red", + }, + } + scheme := ProduceColorSchemeFromConfig("editor", &defaultColors) + for k := range defaultColors { + g.Assert((*scheme)[k]).Equal((*scheme).GetColor(Config.Colors["editor"][k])) + } + }) + }) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 41be594..f133758 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -3,8 +3,9 @@ package config import ( "errors" "github.com/askovpen/gossiped/pkg/types" + "github.com/gdamore/tcell/v2" "gopkg.in/yaml.v3" - "io/ioutil" + "os" "runtime" "strings" ) @@ -25,28 +26,33 @@ type ( BaseType string Chrs string } - Colors map[string]ColorMap - Log string - Address *types.FidoAddr - Origin string - Tearline string - Template string - Chrs struct { + Colorscheme string + Log string + Address *types.FidoAddr + Origin string + Tearline string + Template string + Chrs struct { Default string IBMPC string } + Statusbar struct { + Clock bool + } Sorting SortTypeMap + Colors map[string]ColorMap } ) // vars var ( - Version string - PID string - LongPID string - Config configS - Template []string - city map[string]string + Version string + PID string + LongPID string + Config configS + Template []string + city map[string]string + StyleDefault tcell.Style ) // InitVars define version variables @@ -57,7 +63,7 @@ func InitVars() { // Read config func Read(fn string) error { - yamlFile, err := ioutil.ReadFile(fn) + yamlFile, err := os.ReadFile(fn) if err != nil { return err } @@ -71,37 +77,27 @@ func Read(fn string) error { if Config.Chrs.Default == "" { return errors.New("Config.Chrs.Default not defined") } - tpl, err := ioutil.ReadFile(Config.Template) + tpl, err := os.ReadFile(Config.Template) if err != nil { return err } - for _, l := range strings.Split(string(tpl), "\n") { - if len(l) > 0 && l[0] == ';' { - continue - } - Template = append(Template, l) - } + readTemplate(tpl) if len(Config.Tearline) == 0 { Config.Tearline = LongPID } + errColors := readColors() + if errColors != nil { + return errColors + } readCity() return nil } -func readCity() { - yamlFile, err := ioutil.ReadFile("city.yaml") - if err != nil { - return - } - err = yaml.Unmarshal(yamlFile, &city) - if err != nil { - return - } -} -// GetCity return city -func GetCity(sa string) string { - if val, ok := city[sa]; ok { - return val +func readTemplate(tpl []byte) { + for _, l := range strings.Split(string(tpl), "\n") { + if len(l) > 0 && l[0] == ';' { + continue + } + Template = append(Template, l) } - return "unknown" } diff --git a/pkg/ui/arealist.go b/pkg/ui/arealist.go index f1a8edc..83dffea 100644 --- a/pkg/ui/arealist.go +++ b/pkg/ui/arealist.go @@ -2,6 +2,7 @@ package ui import ( "fmt" + "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/msgapi" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" @@ -29,31 +30,30 @@ func (a *App) AreaListQuit() (string, tview.Primitive, bool, bool) { } func initAreaListHeader(a *App) { + borderStyle := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementBorder) + headerStyle := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementHeader) + fgHeader, bgHeader, attrHeader := headerStyle.Decompose() + selStyle := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementSelection) a.al.SetBorder(true). - SetBorderAttributes(tcell.AttrBold). - SetBorderColor(tcell.ColorBlue) - a.al.SetSelectedStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy).Bold(true)) + SetBorderStyle(borderStyle) + a.al.SetSelectedStyle(selStyle) a.al.SetCell( 0, 0, tview.NewTableCell(" Area"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false)) a.al.SetCell( 0, 1, tview.NewTableCell("EchoID"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetExpansion(1). SetSelectable(false)) a.al.SetCell( 0, 2, tview.NewTableCell("Msgs"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false). SetAlign(tview.AlignRight)) a.al.SetCell( 0, 3, tview.NewTableCell(" New"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false). SetAlign(tview.AlignRight)) } @@ -70,19 +70,29 @@ func refreshAreaList(a *App, currentArea string) { msgapi.SortAreas() a.al.Clear() initAreaListHeader(a) + styleItem := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementItem) + styleHighligt := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementHighlight) + fgItem, bgItem, attrItem := styleItem.Decompose() + fgHigh, bgHigh, attrHigh := styleHighligt.Decompose() var selectIndex = -1 for i, ar := range msgapi.Areas { - var areaStyle = " " + fg, bg, attr := fgItem, bgItem, attrItem + areaStyle := "" if msgapi.AreaHasUnreadMessages(&ar) { - areaStyle = "[::b]+" + areaStyle = "+" + fg, bg, attr = fgHigh, bgHigh, attrHigh } - a.al.SetCell(i+1, 0, tview.NewTableCell(strconv.FormatInt(int64(i), 10)+areaStyle). - SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) - a.al.SetCell(i+1, 1, tview.NewTableCell(ar.GetName()).SetTextColor(tcell.ColorSilver)) + a.al.SetCell(i+1, 0, tview.NewTableCell(areaStyle+strconv.FormatInt(int64(i), 10)). + SetAlign(tview.AlignRight). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) + a.al.SetCell(i+1, 1, tview.NewTableCell(ar.GetName()). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) a.al.SetCell(i+1, 2, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()), 10)). - SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr). + SetAlign(tview.AlignRight)) a.al.SetCell(i+1, 3, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()-ar.GetLast()), 10)). - SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr). + SetAlign(tview.AlignRight)) if currentArea != "" && currentArea == ar.GetName() { selectIndex = i + 1 } @@ -110,6 +120,8 @@ func (a *App) AreaList() (string, tview.Primitive, bool, bool) { area.GetCount()-area.GetLast(), )) }) + _, defBg, _ := config.StyleDefault.Decompose() + a.al.SetBackgroundColor(defBg) a.al.SetSelectedFunc(func(row int, column int) { a.onSelected(row, column) }) diff --git a/pkg/ui/editheader.go b/pkg/ui/editheader.go index 759b293..9c0bd09 100644 --- a/pkg/ui/editheader.go +++ b/pkg/ui/editheader.go @@ -1,6 +1,7 @@ package ui import ( + "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/msgapi" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" @@ -53,18 +54,27 @@ func NewEditHeader(msg *msgapi.Message) *EditHeader { // Draw header func (e *EditHeader) Draw(screen tcell.Screen) { e.Box.Draw(screen) + + boxFg, boxBg, _ := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementWindow).Decompose() + e.Box.SetBackgroundColor(boxBg) x, y, _, _ := e.GetInnerRect() - tview.Print(screen, "Msg :", x+1, y, 6, 0, tcell.ColorSilver) - tview.Print(screen, "From :", x+1, y+1, 6, 0, tcell.ColorSilver) - tview.Print(screen, "To :", x+1, y+2, 6, 0, tcell.ColorSilver) - tview.Print(screen, "Subj :", x+1, y+3, 6, 0, tcell.ColorSilver) + itemStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementItem) + itemStyle = itemStyle.Attributes(tcell.AttrNone) + headerStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementHeader) + selectionStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementSelection) + + tview.Print(screen, config.FormatTextWithStyle("Msg :", headerStyle), x+1, y, 6, 0, boxBg) + tview.Print(screen, config.FormatTextWithStyle("From :", headerStyle), x+1, y+1, 6, 0, boxBg) + tview.Print(screen, config.FormatTextWithStyle("To :", headerStyle), x+1, y+2, 6, 0, boxBg) + tview.Print(screen, config.FormatTextWithStyle("Subj :", headerStyle), x+1, y+3, 6, 0, boxBg) + if e.HasFocus() { for i := e.sCoords[e.sIndex].f; i < e.sCoords[e.sIndex].t; i++ { - screen.SetContent(x+i, y+e.sCoords[e.sIndex].y, ' ', nil, tcell.StyleDefault.Background(tcell.ColorNavy)) + screen.SetContent(x+i, y+e.sCoords[e.sIndex].y, ' ', nil, selectionStyle) } } for i := 0; i < 5; i++ { - tview.Print(screen, string(e.sInputs[i]), x+e.sCoords[i].f, y+e.sCoords[i].y, len(e.sInputs[i]), 0, tcell.ColorSilver) + tview.Print(screen, config.FormatTextWithStyle(string(e.sInputs[i]), itemStyle), x+e.sCoords[i].f, y+e.sCoords[i].y, len(e.sInputs[i]), 0, boxFg) } if e.HasFocus() { screen.ShowCursor(x+e.sCoords[e.sIndex].f+len(e.sInputs[e.sIndex][:e.sPosition[e.sIndex]]), y+e.sCoords[e.sIndex].y) diff --git a/pkg/ui/editor/cellview.go b/pkg/ui/editor/cellview.go index 5f66c52..e8b1e9a 100644 --- a/pkg/ui/editor/cellview.go +++ b/pkg/ui/editor/cellview.go @@ -1,6 +1,7 @@ package editor import ( + "github.com/askovpen/gossiped/pkg/config" "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" ) @@ -12,7 +13,12 @@ func min(a, b int) int { return b } -func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, colorscheme Colorscheme, tabsize int) (int, int, *tcell.Style) { +func visualToCharPos(visualIndex int, + lineN int, + str string, + buf *Buffer, + colorscheme *config.ColorScheme, + tabsize int) (int, int, *tcell.Style) { charPos := 0 var lineIdx int var lastWidth int @@ -23,7 +29,7 @@ func visualToCharPos(visualIndex int, lineN int, str string, buf *Buffer, colors // width := StringWidth(str[:i], tabsize) if group, ok := buf.Match(lineN)[charPos]; ok { - s := colorscheme.GetColor(group.String()) + s := (*colorscheme).GetColor(group.String()) style = &s } @@ -67,11 +73,10 @@ type CellView struct { } // Draw func -func (c *CellView) Draw(buf *Buffer, colorscheme Colorscheme, top, height, left, width int) { +func (c *CellView) Draw(buf *Buffer, colorscheme *config.ColorScheme, top, height, left, width int) { if width <= 0 { return } - tabsize := int(buf.Settings["tabsize"].(float64)) indentrunes := []rune(buf.Settings["indentchar"].(string)) // if empty indentchar settings, use space @@ -97,7 +102,7 @@ func (c *CellView) Draw(buf *Buffer, colorscheme Colorscheme, top, height, left, viewLine := 0 lineN := top - curStyle := defStyle + curStyle := config.StyleDefault for viewLine < height { if lineN >= len(buf.lines) { break @@ -133,7 +138,7 @@ func (c *CellView) Draw(buf *Buffer, colorscheme Colorscheme, top, height, left, break } if group, ok := buf.Match(lineN)[colN]; ok { - curStyle = colorscheme.GetColor(group.String()) + curStyle = (*colorscheme).GetColor(group.String()) } char := line[colN] @@ -152,7 +157,7 @@ func (c *CellView) Draw(buf *Buffer, colorscheme Colorscheme, top, height, left, indentStyle := curStyle ch := buf.Settings["indentchar"].(string) - if group, ok := colorscheme["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" { + if group, ok := (*colorscheme)["indent-char"]; ok && !IsStrWhitespace(ch) && ch != "" { indentStyle = group } @@ -199,7 +204,7 @@ func (c *CellView) Draw(buf *Buffer, colorscheme Colorscheme, top, height, left, } } if group, ok := buf.Match(lineN)[len(line)]; ok { - curStyle = colorscheme.GetColor(group.String()) + curStyle = (*colorscheme).GetColor(group.String()) } // newline diff --git a/pkg/ui/editor/colorscheme.go b/pkg/ui/editor/colorscheme.go deleted file mode 100644 index 21ad2d7..0000000 --- a/pkg/ui/editor/colorscheme.go +++ /dev/null @@ -1,238 +0,0 @@ -package editor - -import ( - "errors" - "fmt" - "github.com/askovpen/gossiped/pkg/config" - "github.com/gdamore/tcell/v2" - "log" - "regexp" - "strconv" - "strings" -) - -// Colorscheme is a map from string to style -- it represents a colorscheme -type Colorscheme map[string]tcell.Style - -const ConfigColorArea = "editor" - -const ( - StyleUnderline = "underline" - StyleBold = "bold" - StyleReverse = "reverse" -) - -var ( - // The current default colorscheme - colorscheme Colorscheme - // The default cell style - defStyle tcell.Style - // Default colors - defaultColors = config.ColorMap{ - "comment": "bold yellow", - "icomment": "bold white", - "origin": "bold white", - "tearline": "bold white", - "tagline": "bold white", - "kludge": "bold gray", - } -) - -// GetColor takes in a syntax group and returns the colorscheme's style for that group -func GetColor(color string) tcell.Style { - return colorscheme.GetColor(color) -} - -// GetColor takes in a syntax group and returns the colorscheme's style for that group -func (colorscheme Colorscheme) GetColor(color string) tcell.Style { - st := defStyle - if color == "" { - return st - } - groups := strings.Split(color, ".") - if len(groups) > 1 { - curGroup := "" - for i, g := range groups { - if i != 0 { - curGroup += "." - } - curGroup += g - if style, ok := colorscheme[curGroup]; ok { - st = style - } - } - } else if style, ok := colorscheme[color]; ok { - st = style - } else { - st, _ = StringToStyle(color) - } - - return st -} - -// init picks and initializes the colorscheme when micro starts -func init() { - colorscheme = make(Colorscheme) - defStyle = tcell.StyleDefault. - Foreground(tcell.ColorDefault). - Background(tcell.ColorDefault) -} - -// SetDefaultColorscheme sets the current default colorscheme for new Views. -func SetDefaultColorscheme(scheme Colorscheme) { - colorscheme = scheme -} - -// ParseColorscheme parses the text definition for a colorscheme and returns the corresponding object -// Colorschemes are made up of color-link statements linking a color group to a list of colors -// For example, color-link keyword (blue,red) makes all keywords have a blue foreground and -// red background -func ParseColorscheme(text string) Colorscheme { - parser := regexp.MustCompile(`color-link\s+(\S*)\s+"(.*)"`) - - lines := strings.Split(text, "\n") - - c := make(Colorscheme) - - for _, line := range lines { - if strings.TrimSpace(line) == "" || - strings.TrimSpace(line)[0] == '#' { - // Ignore this line - continue - } - - matches := parser.FindSubmatch([]byte(line)) - if len(matches) == 3 { - link := string(matches[1]) - colors := string(matches[2]) - - style, _ := StringToStyle(colors) - c[link] = style - - if link == "default" { - defStyle = style - } - } else { - log.Println("Color-link statement is not valid:", line) - } - } - - return c -} - -// StringToStyle returns a style from a string -// The strings must be in the format "extra foregroundcolor,backgroundcolor" -// The 'extra' can be bold, reverse, or underline -func StringToStyle(str string) (tcell.Style, error) { - var errStack error - str = strings.ToLower(strings.TrimSpace(str)) - - if len(str) == 0 { - errStack = errors.New("empty color value") - return tcell.StyleDefault, errStack - } - - var fg, bg string - var split = strings.Split(str, ",") - if len(split) > 1 { - fg, bg = split[0], split[1] - } else { - fg = split[0] - } - fg = strings.TrimSpace(fg) - bg = strings.TrimSpace(bg) - - var styles = "" - var splitFg = strings.Split(fg, " ") - if len(splitFg) > 1 { - styles = strings.TrimSpace(splitFg[0]) - fg = strings.TrimSpace(splitFg[1]) - } else { - fg = strings.TrimSpace(splitFg[0]) - } - - var fgColor, bgColor, _ = defStyle.Decompose() - - if fg != "" && fg != "default" { - if _, ok := tcell.ColorNames[fg]; !ok && fg != "default" { - errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown foreground color name \"%s\"", fg))) - } - fgColor = StringToColor(fg) - } - if bg != "" && bg != "default" { - if _, ok := tcell.ColorNames[bg]; !ok { - errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown background color name \"%s\"", bg))) - } - bgColor = StringToColor(bg) - } - - style := defStyle.Foreground(fgColor).Background(bgColor) - var splitStyles = strings.Split(styles, "|") - for _, v := range splitStyles { - v = strings.TrimSpace(v) - if v == StyleReverse { - style = style.Reverse(true) - } else if v == StyleUnderline { - style = style.Underline(true) - } else if v == StyleBold { - style = style.Bold(true) - } else if v != "" { - errStack = errors.Join(errStack, errors.New(fmt.Sprintf("unknown style \"%s\"", v))) - } - } - return style, errStack -} - -// StringToColor returns a tcell color from a string representation of a color -func StringToColor(str string) tcell.Color { - if num, err := strconv.Atoi(str); err == nil { - if num > 255 || num < 0 { - return tcell.ColorDefault - } - return tcell.PaletteColor(num) - } - return tcell.GetColor(str) -} - -func ProduceColorMapFromConfig(colorArea string, fallbackColors *config.ColorMap) (config.ColorMap, error) { - var out = make(config.ColorMap) - var validKeys = make(map[string]bool) - for k, v := range *fallbackColors { - validKeys[k] = true - out[k] = v - } - var fallback = out - if config.Config.Colors[colorArea] == nil || len(config.Config.Colors[colorArea]) == 0 { - return fallback, nil - } - var validation error = nil - for element, colorValue := range config.Config.Colors[colorArea] { - colorValue = strings.ToLower(strings.TrimSpace(colorValue)) - if !validKeys[element] { - validation = errors.Join( - validation, - errors.New("not valid element for area (element: "+element+", area: "+colorArea+")"), - ) - continue - } - - if _, err := StringToStyle(colorValue); err != nil { - validation = errors.Join( - validation, - errors.New(err.Error()+" (element: "+element+", area: "+colorArea+")"), - ) - continue - } - out[element] = colorValue - } - return out, validation -} - -func ProduceColorSchemeFromConfig() Colorscheme { - var scheme = Colorscheme{} - colors, _ := ProduceColorMapFromConfig(ConfigColorArea, &defaultColors) - for colorType, colorValue := range colors { - scheme[colorType], _ = StringToStyle(colorValue) - } - return scheme -} diff --git a/pkg/ui/editor/scrollbar.go b/pkg/ui/editor/scrollbar.go index 55c6c75..5849a85 100644 --- a/pkg/ui/editor/scrollbar.go +++ b/pkg/ui/editor/scrollbar.go @@ -1,6 +1,9 @@ package editor -import "github.com/gdamore/tcell/v2" +import ( + "github.com/askovpen/gossiped/pkg/config" + "github.com/gdamore/tcell/v2" +) // ScrollBar represents an optional scrollbar that can be used type ScrollBar struct { @@ -9,7 +12,7 @@ type ScrollBar struct { // Display shows the scrollbar func (sb *ScrollBar) Display(screen tcell.Screen) { - style := defStyle.Reverse(true) + style := config.StyleDefault.Reverse(true) screen.SetContent(sb.view.x+sb.view.width-1, sb.view.y+sb.pos(), ' ', nil, style) } diff --git a/pkg/ui/editor/view.go b/pkg/ui/editor/view.go index 80da580..d751272 100644 --- a/pkg/ui/editor/view.go +++ b/pkg/ui/editor/view.go @@ -1,6 +1,7 @@ package editor import ( + "github.com/askovpen/gossiped/pkg/config" "strings" "github.com/gdamore/tcell/v2" @@ -55,7 +56,7 @@ type View struct { bindings KeyBindings // The colorscheme - colorscheme Colorscheme + colorscheme *config.ColorScheme // The runtime files done func() @@ -104,7 +105,7 @@ func (v *View) SetKeybindings(bindings KeyBindings) { } // SetColorscheme sets the colorscheme for this view. -func (v *View) SetColorscheme(colorscheme Colorscheme) { +func (v *View) SetColorscheme(colorscheme *config.ColorScheme) { v.colorscheme = colorscheme v.Buf.updateRules() } @@ -145,7 +146,7 @@ func (v *View) OpenBuffer(buf *Buffer) { v.isOverwriteMode = false v.Buf.updateRules() // Prepare color scheme - v.SetColorscheme(ProduceColorSchemeFromConfig()) + v.SetColorscheme(config.GetColors(config.ColorAreaEditor)) } // Bottomline returns the line number of the lowest line in the view @@ -410,20 +411,19 @@ func ShowMultiCursor(screen tcell.Screen, x, y, i int) { screen.ShowCursor(x, y) } else { r, _, _, _ := screen.GetContent(x, y) - screen.SetContent(x, y, r, nil, defStyle.Reverse(true)) + screen.SetContent(x, y, r, nil, config.StyleDefault.Reverse(true)) } } // Draw renders the view and the cursor func (v *View) Draw(screen tcell.Screen) { v.Box.Draw(screen) - v.x, v.y, v.width, v.height = v.Box.GetInnerRect() // TODO(pdg): just clear from the last line down. for y := v.y; y < v.y+v.height; y++ { for x := v.x; x < v.x+v.width; x++ { - screen.SetContent(x, y, ' ', nil, defStyle) + screen.SetContent(x, y, ' ', nil, config.StyleDefault) } } diff --git a/pkg/ui/help.go b/pkg/ui/help.go index 7dcf1d1..c2329db 100644 --- a/pkg/ui/help.go +++ b/pkg/ui/help.go @@ -1,6 +1,8 @@ package ui import ( + "fmt" + "github.com/askovpen/gossiped/pkg/config" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -23,10 +25,17 @@ func NewModalHelp() *ModalHelp { } m.txt = tview.NewTextView() m.frame = tview.NewFrame(m.txt).SetBorders(0, 0, 1, 0, 0, 0) + fgBorder, bgBorder, styleBorder := config.GetElementStyle(config.ColorAreaHelp, "border").Decompose() + fgTitle, bgTitle, styleTitle := config.GetElementStyle(config.ColorAreaHelp, "title").Decompose() + title := fmt.Sprintf("[:%s:%s] Keys ", bgTitle.String(), config.MaskToStringStyle(styleTitle)) m.frame.SetBorder(true). - SetBackgroundColor(tcell.ColorBlack). - SetBorderPadding(0, 0, 1, 1).SetBorderColor(tcell.ColorYellow).SetBorderAttributes(tcell.AttrBold).SetTitleColor(tcell.ColorYellow).SetTitleAlign(tview.AlignLeft).SetTitle("Keys") - + SetBackgroundColor(bgBorder). + SetBorderPadding(0, 0, 1, 1). + SetBorderColor(fgBorder). + SetBorderAttributes(styleBorder). + SetTitleColor(fgTitle). + SetTitleAlign(tview.AlignLeft). + SetTitle(title) return m } @@ -41,6 +50,8 @@ func (m *ModalHelp) InputHandler() func(event *tcell.EventKey, setFocus func(p t // SetText Set Text func (m *ModalHelp) SetText(txt string) *ModalHelp { + style := config.GetElementStyle(config.ColorAreaHelp, "text") + m.txt.SetTextStyle(style) m.txt.SetText(txt) return m } diff --git a/pkg/ui/insertmsg.go b/pkg/ui/insertmsg.go index 949cd14..f30dcb9 100644 --- a/pkg/ui/insertmsg.go +++ b/pkg/ui/insertmsg.go @@ -6,7 +6,6 @@ import ( "github.com/askovpen/gossiped/pkg/msgapi" "github.com/askovpen/gossiped/pkg/types" "github.com/askovpen/gossiped/pkg/ui/editor" - "github.com/gdamore/tcell/v2" "github.com/rivo/tview" //"log" ) @@ -90,14 +89,17 @@ func (a *App) InsertMsg(area *msgapi.AreaPrimitive, msgType int) (string, tview. omsg, _ = (*area).GetMsg((*a.im.curArea).GetLast()) a.im.newMsg.Subject = omsg.Subject } + _, boxBg, _ := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementWindow).Decompose() + mhStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementTitle) a.im.eh = NewEditHeader(a.im.newMsg) + a.im.eh.SetBackgroundColor(boxBg) a.im.eh.SetBorder(true). - SetBorderAttributes(tcell.AttrBold). - SetBorderColor(tcell.ColorBlue). - SetTitle(" " + (*a.im.postArea).GetName() + " "). + SetTitle(config.FormatTextWithStyle(" "+(*a.im.postArea).GetName()+" ", mhStyle)). SetTitleAlign(tview.AlignLeft). - SetTitleColor(tcell.ColorYellow) + SetBorderStyle(config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementBorder)) + a.im.eb = editor.NewView(editor.NewBufferFromString("")) + //a.im.eb.SetBackgroundColor() // a.im.eb = NewEditBody(). a.im.eb.SetDoneFunc(func() { a.Pages.ShowPage("InsertMsgMenu") diff --git a/pkg/ui/modalarealist.go b/pkg/ui/modalarealist.go index 168be37..bf8ad09 100644 --- a/pkg/ui/modalarealist.go +++ b/pkg/ui/modalarealist.go @@ -1,6 +1,7 @@ package ui import ( + "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/msgapi" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" @@ -19,49 +20,63 @@ type ModalAreaList struct { // NewModalAreaList returns a new modal message window. func NewModalAreaList() *ModalAreaList { + defFg, defBg, _ := config.StyleDefault.Decompose() m := &ModalAreaList{ - Box: tview.NewBox(), - textColor: tview.Styles.PrimaryTextColor, + Box: tview.NewBox().SetBackgroundColor(defBg), + textColor: defFg, } + borderFg, _, borderAttr := config.GetElementStyle(config.ColorAreaAreaListModal, config.ColorElementBorder).Decompose() + headerStyle := config.GetElementStyle(config.ColorAreaAreaListModal, config.ColorElementHeader) + selectionStyle := config.GetElementStyle(config.ColorAreaAreaListModal, config.ColorElementSelection) + itemStyle := config.GetElementStyle(config.ColorAreaAreaListModal, config.ColorElementItem) + fgItem, bgItem, attrItem := itemStyle.Decompose() + fgHeader, bgHeader, attrHeader := headerStyle.Decompose() m.table = tview.NewTable(). SetFixed(1, 0). + SetBordersColor(borderFg). SetSelectable(true, false). - SetSelectedStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy).Bold(true)). + SetSelectedStyle(selectionStyle). SetSelectedFunc(func(row int, column int) { m.done(row) }) m.frame = tview.NewFrame(m.table).SetBorders(0, 0, 1, 0, 0, 0) + m.frame.SetBackgroundColor(defBg) + m.table.SetBackgroundColor(defBg) m.frame.SetBorder(true). - SetBackgroundColor(tcell.ColorBlack). - SetBorderPadding(0, 0, 1, 1).SetBorderColor(tcell.ColorBlue).SetBorderAttributes(tcell.AttrBold).SetTitleColor(tcell.ColorYellow).SetTitleAlign(tview.AlignLeft) + SetTitleAlign(tview.AlignLeft). + SetBorderAttributes(borderAttr). + SetBorderColor(borderFg). + SetBorderPadding(0, 0, 1, 1) m.table.SetCell( 0, 0, tview.NewTableCell(" Area"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false)) m.table.SetCell( 0, 1, tview.NewTableCell("EchoID"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetExpansion(1). SetSelectable(false)) m.table.SetCell( 0, 2, tview.NewTableCell("Msgs"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false). SetAlign(tview.AlignRight)) m.table.SetCell( 0, 3, tview.NewTableCell(" New"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false). SetAlign(tview.AlignRight)) for i, ar := range msgapi.Areas { - m.table.SetCell(i+1, 0, tview.NewTableCell(strconv.FormatInt(int64(i), 10)+" ").SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) - m.table.SetCell(i+1, 1, tview.NewTableCell(ar.GetName()).SetTextColor(tcell.ColorSilver)) - m.table.SetCell(i+1, 2, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()), 10)).SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) - m.table.SetCell(i+1, 3, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()-ar.GetLast()), 10)).SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) + m.table.SetCell(i+1, 0, tview.NewTableCell(strconv.FormatInt(int64(i), 10)+" "). + SetAlign(tview.AlignRight).SetTextColor(fgItem).SetBackgroundColor(bgItem).SetAttributes(attrItem)) + m.table.SetCell(i+1, 1, tview.NewTableCell(ar.GetName()). + SetTextColor(fgItem).SetBackgroundColor(bgItem).SetAttributes(attrItem)) + m.table.SetCell(i+1, 2, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()), 10)). + SetAlign(tview.AlignRight). + SetTextColor(fgItem).SetBackgroundColor(bgItem).SetAttributes(attrItem)) + m.table.SetCell(i+1, 3, tview.NewTableCell(strconv.FormatInt(int64(ar.GetCount()-ar.GetLast()), 10)). + SetAlign(tview.AlignRight). + SetTextColor(fgItem).SetBackgroundColor(bgItem).SetAttributes(attrItem)) } return m } @@ -86,7 +101,8 @@ func (m *ModalAreaList) SetDoneFunc(handler func(buttonIndex int)) *ModalAreaLis // window. func (m *ModalAreaList) SetText(text string) *ModalAreaList { m.title = text - m.frame.SetTitle(text) + style := config.GetElementStyle(config.ColorAreaAreaListModal, config.ColorElementTitle) + m.frame.SetTitle(config.FormatTextWithStyle(text, style)) return m } diff --git a/pkg/ui/modalmenu.go b/pkg/ui/modalmenu.go index 33ab464..0085e1a 100644 --- a/pkg/ui/modalmenu.go +++ b/pkg/ui/modalmenu.go @@ -1,6 +1,7 @@ package ui import ( + "github.com/askovpen/gossiped/pkg/config" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -19,22 +20,30 @@ type ModalMenu struct { // NewModalMenu returns a new modal message window. func NewModalMenu() *ModalMenu { + itemStyle := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementItem) + defFg, defBg, _ := itemStyle.Decompose() m := &ModalMenu{ - Box: tview.NewBox(), - textColor: tview.Styles.PrimaryTextColor, + Box: tview.NewBox().SetBackgroundColor(defBg), + textColor: defFg, y: 1, width: 0, } + selStyle := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementSelection) + borderStyle := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementBorder) + fgTitle, _, _ := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementTitle).Decompose() m.table = tview.NewTable(). SetSelectable(true, false). - SetSelectedStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy).Bold(true)). + SetSelectedStyle(selStyle). SetSelectedFunc(func(row int, column int) { m.done(row) }) + m.table.SetBackgroundColor(defBg) m.frame = tview.NewFrame(m.table).SetBorders(0, 0, 1, 0, 0, 0) m.frame.SetBorder(true). - SetBackgroundColor(tcell.ColorBlack). - SetBorderPadding(0, 0, 1, 1).SetBorderColor(tcell.ColorRed).SetBorderAttributes(tcell.AttrBold).SetTitleColor(tcell.ColorYellow) + SetTitleColor(fgTitle). + SetBackgroundColor(defBg). + SetBorderPadding(0, 0, 1, 1). + SetBorderStyle(borderStyle) return m } @@ -57,8 +66,9 @@ func (m *ModalMenu) SetDoneFunc(handler func(buttonIndex int)) *ModalMenu { // breaks. Note that words are wrapped, too, based on the final size of the // window. func (m *ModalMenu) SetText(text string) *ModalMenu { + style := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementTitle) m.title = text - m.frame.SetTitle(text) + m.frame.SetTitle(config.FormatTextWithStyle(text, style)) return m } @@ -71,10 +81,14 @@ func (m *ModalMenu) SetY(y int) *ModalMenu { // AddButtons adds buttons to the window. There must be at least one button and // a "done" handler so the window can be closed again. func (m *ModalMenu) AddButtons(labels []string) *ModalMenu { + style := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementItem) + selStyle := config.GetElementStyle(config.ColorAreaDialog, config.ColorElementSelection) + fg, bg, attr := style.Decompose() for index, label := range labels { func(i int, l string) { //m.list.AddItem(label,"",0,func() {m.done(i,l)}) - m.table.SetCell(i, 0, tview.NewTableCell(label).SetTextColor(tcell.ColorSilver)) + m.table.SetCell(i, 0, tview.NewTableCell(config.FormatTextWithStyle(label, style)). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr).SetSelectedStyle(selStyle)) if m.width < len(label) { m.width = len(label) } diff --git a/pkg/ui/modalmlist.go b/pkg/ui/modalmlist.go index edb13d6..f9a47f5 100644 --- a/pkg/ui/modalmlist.go +++ b/pkg/ui/modalmlist.go @@ -1,6 +1,7 @@ package ui import ( + "fmt" "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/msgapi" "github.com/askovpen/gossiped/pkg/utils" @@ -24,69 +25,87 @@ type ModalMessageList struct { // NewModalMessageList returns a new modal message window. func NewModalMessageList(area *msgapi.AreaPrimitive) *ModalMessageList { + _, defBg, _ := config.StyleDefault.Decompose() m := &ModalMessageList{ - Box: tview.NewBox(), + Box: tview.NewBox().SetBackgroundColor(defBg), textColor: tview.Styles.PrimaryTextColor, } + styleBorder := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementBorder) + styleSelection := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementSelection) + fgHeader, bgHeader, attrHeader := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementHeader).Decompose() + fgItem, bgItem, attrItem := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementItem).Decompose() + fgTitle, bgTitle, attrTitle := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementTitle).Decompose() + fgHigh, bgHigh, attrHigh := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementHighlight).Decompose() + //fgCur, bgCur, attrCur := config.GetElementStyle(config.ColorAreaMessageList, config.ColorElementCurrent).Decompose() m.table = tview.NewTable(). SetFixed(1, 0). SetSelectable(true, false). - SetSelectedStyle(tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorNavy).Bold(true)). + SetSelectedStyle(styleSelection). SetSelectedFunc(func(row int, column int) { m.done(uint32(row)) }) m.frame = tview.NewFrame(m.table).SetBorders(0, 0, 1, 0, 0, 0) - m.frame.SetTitle("List Messages") + m.frame.SetBackgroundColor(defBg) + m.table.SetBackgroundColor(defBg) + m.frame.SetTitle(fmt.Sprintf("[%s:%s:%s] List Messages ", fgTitle.String(), bgTitle.String(), config.MaskToStringStyle(attrTitle))) m.frame.SetBorder(true). - SetBackgroundColor(tcell.ColorBlack). - SetBorderPadding(0, 0, 1, 1).SetBorderColor(tcell.ColorRed).SetBorderAttributes(tcell.AttrBold).SetTitleColor(tcell.ColorYellow).SetTitleAlign(tview.AlignLeft) - m.table.SetCell( - 0, 0, tview.NewTableCell(" Msg "). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). - SetSelectable(false). - SetAlign(tview.AlignRight)) + SetBorderStyle(styleBorder). + SetBorderPadding(0, 0, 1, 1). + SetTitleAlign(tview.AlignLeft) + m.table.SetCell(0, 0, tview.NewTableCell(" Msg "). + SetSelectable(false). + SetAlign(tview.AlignRight). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader)) m.table.SetCell( 0, 1, tview.NewTableCell("From"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false)) m.table.SetCell( 0, 2, tview.NewTableCell("To"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false)) m.table.SetCell( 0, 3, tview.NewTableCell("Subj"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetExpansion(1). SetSelectable(false)) m.table.SetCell( 0, 4, tview.NewTableCell("Written"). - SetTextColor(tcell.ColorYellow). - SetAttributes(tcell.AttrBold). + SetTextColor(fgHeader).SetBackgroundColor(bgHeader).SetAttributes(attrHeader). SetSelectable(false). SetAlign(tview.AlignRight)) for i, mh := range *(*area).GetMessages() { ch := " " + fg, bg, attr := fgItem, bgItem, attrItem if i == int((*area).GetLast()-1) { - ch = "[::b]," + //fg, bg, attr = fgCur, bgCur, attrCur + fg, bg, attr = fgHigh, bgHigh, attrHigh + ch = "*" } + fromCondition := utils.NamesEqual(mh.From, config.Config.Username) + toCondition := utils.NamesEqual(mh.To, config.Config.Username) + m.table.SetCell(i+1, 0, tview.NewTableCell(strconv.FormatInt(int64(mh.MsgNum), 10)+ch). + SetAlign(tview.AlignRight). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) //mh.From, mh.To, mh.Subject, mh.DateWritten.Format("02 Jan 06")) - m.table.SetCell(i+1, 0, tview.NewTableCell(strconv.FormatInt(int64(mh.MsgNum), 10)+ch).SetAlign(tview.AlignRight).SetTextColor(tcell.ColorSilver)) - if utils.NamesEqual(mh.From, config.Config.Username) { - m.table.SetCell(i+1, 1, tview.NewTableCell(mh.From).SetTextColor(tcell.ColorSilver).SetAttributes(tcell.AttrBold)) + if fromCondition { + m.table.SetCell(i+1, 1, tview.NewTableCell(mh.From). + SetTextColor(fgHigh).SetBackgroundColor(bgHigh).SetAttributes(attrHigh)) } else { - m.table.SetCell(i+1, 1, tview.NewTableCell(mh.From).SetTextColor(tcell.ColorSilver)) + m.table.SetCell(i+1, 1, tview.NewTableCell(mh.From). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) } - if utils.NamesEqual(mh.To, config.Config.Username) { - m.table.SetCell(i+1, 2, tview.NewTableCell(mh.To).SetTextColor(tcell.ColorSilver).SetAttributes(tcell.AttrBold)) + if toCondition { + m.table.SetCell(i+1, 2, tview.NewTableCell(mh.To). + SetTextColor(fgHigh).SetBackgroundColor(bgHigh).SetAttributes(attrHigh)) } else { - m.table.SetCell(i+1, 2, tview.NewTableCell(mh.To).SetTextColor(tcell.ColorSilver)) + m.table.SetCell(i+1, 2, tview.NewTableCell(mh.To). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) } - m.table.SetCell(i+1, 3, tview.NewTableCell(mh.Subject).SetTextColor(tcell.ColorSilver)) - m.table.SetCell(i+1, 4, tview.NewTableCell(mh.DateWritten.Format("02 Jan 06")).SetTextColor(tcell.ColorSilver)) + m.table.SetCell(i+1, 3, tview.NewTableCell(mh.Subject). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) + m.table.SetCell(i+1, 4, tview.NewTableCell(mh.DateWritten.Format("02 Jan 06")). + SetTextColor(fg).SetBackgroundColor(bg).SetAttributes(attr)) } m.table.Select(int((*area).GetLast()), 0) return m diff --git a/pkg/ui/searchString.go b/pkg/ui/searchString.go index 8dd5866..288e414 100644 --- a/pkg/ui/searchString.go +++ b/pkg/ui/searchString.go @@ -1,6 +1,7 @@ package ui import ( + "github.com/askovpen/gossiped/pkg/config" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -20,10 +21,15 @@ func NewSearchString() *SearchString { // Draw searchString func (e *SearchString) Draw(screen tcell.Screen) { + stylePrompt := config.GetElementStyle(config.ColorAreaAreaList, config.ColorElementPrompt) + //styleBorder := styles.GetElementStyle(styles.ColorAreaAreaList, styles.ColorElementBorder) + fg, bg, _ := stylePrompt.Decompose() e.Box.Draw(screen) + e.Box.SetBackgroundColor(bg) + //e.Box.SetBorderStyle(styleBorder) x, y, _, _ := e.GetInnerRect() - tview.Print(screen, ">>Pick New Area: ", x, y, 17, 0, tcell.ColorSilver) - tview.Print(screen, e.txt, x+17, y, len(e.txt), 0, tcell.ColorSilver) + tview.Print(screen, config.FormatTextWithStyle(">> Pick New Area: ", stylePrompt), x, y, 18, 0, fg) + tview.Print(screen, config.FormatTextWithStyle(e.txt, stylePrompt), x+18, y, len(e.txt), 0, fg) } // AddChar to searchString diff --git a/pkg/ui/statusbar.go b/pkg/ui/statusbar.go index d55c01b..2cbe81f 100644 --- a/pkg/ui/statusbar.go +++ b/pkg/ui/statusbar.go @@ -1,7 +1,7 @@ package ui import ( - "github.com/gdamore/tcell/v2" + "github.com/askovpen/gossiped/pkg/config" "github.com/rivo/tview" "time" ) @@ -19,18 +19,16 @@ func NewStatusBar(app *App) *StatusBar { sb := &StatusBar{} sb.app = app - + styleText := config.GetElementStyle(config.ColorAreaStatusBar, config.ColorElementText) sb.status = tview.NewTextView().SetWrap(false) - sb.status.SetBackgroundColor(tcell.ColorNavy) - sb.status.SetTextColor(tcell.ColorYellow) sb.status.SetDynamicColors(true) + sb.status.SetTextStyle(styleText) sb.status.SetChangedFunc(func() { sb.app.App.Draw() }) sb.statusTime = tview.NewTextView().SetWrap(false) - sb.statusTime.SetBackgroundColor(tcell.ColorNavy) - sb.statusTime.SetTextColor(tcell.ColorYellow) + sb.statusTime.SetTextStyle(styleText) sb.statusTime.SetDynamicColors(true) sb.statusTime.SetChangedFunc(func() { sb.app.App.Draw() @@ -44,17 +42,25 @@ func NewStatusBar(app *App) *StatusBar { // SetStatus set status func (sb StatusBar) SetStatus(s string) { - sb.status.SetText(" [::b][white]" + s) + styleText := config.GetElementStyle(config.ColorAreaStatusBar, config.ColorElementText) + + //sb.status.SetText(" [::b][white]" + s) + sb.status.SetTextStyle(styleText) + sb.status.SetText(" " + s) } // Run update timers func (sb StatusBar) Run() { - sb.statusTime.SetText("[::b][white]" + time.Now().Format("15:04:05")) - clock := time.NewTicker(1 * time.Second) - go func() { - for t := range clock.C { - //fmt.Fprintf(ui.StatusTime,"%s",t.Format("15:04:05")) - sb.statusTime.SetText("[::b][white]" + t.Format("15:04:05")) - } - }() + if config.Config.Statusbar.Clock { + styleText := config.GetElementStyle(config.ColorAreaStatusBar, config.ColorElementText) + sb.statusTime.SetTextStyle(styleText) + sb.statusTime.SetText(time.Now().Format("15:04:05")) + clock := time.NewTicker(1 * time.Second) + go func() { + for t := range clock.C { + //fmt.Fprintf(ui.StatusTime,"%s",t.Format("15:04:05")) + sb.statusTime.SetText(t.Format("15:04:05")) + } + }() + } } diff --git a/pkg/ui/viewheader.go b/pkg/ui/viewheader.go index 2b13d24..b89f0c3 100644 --- a/pkg/ui/viewheader.go +++ b/pkg/ui/viewheader.go @@ -77,23 +77,35 @@ func NewViewHeader(msg *msgapi.Message) *ViewHeader { // Draw header func (e *ViewHeader) Draw(screen tcell.Screen) { e.Box.Draw(screen) + defStyle := config.StyleDefault + boxFg, boxBg, _ := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementWindow).Decompose() + borderStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementBorder) + e.Box.SetBackgroundColor(boxBg) + e.Box.SetBorderStyle(borderStyle) x, y, _, _ := e.GetInnerRect() - tview.Print(screen, "of", x+14, y, 2, 0, tcell.ColorSilver) - tview.Print(screen, "Msg :", x+1, y, 6, 0, tcell.ColorSilver) - tview.Print(screen, "From :", x+1, y+1, 6, 0, tcell.ColorSilver) - tview.Print(screen, "To :", x+1, y+2, 6, 0, tcell.ColorSilver) - tview.Print(screen, "Subj :", x+1, y+3, 6, 0, tcell.ColorSilver) + itemStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementItem) + highlightStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementHighlight) + headerStyle := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementHeader) + _, bgSel, _ := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementSelection).Decompose() + tview.Print(screen, config.FormatTextWithStyle("of", itemStyle), x+14, y, 2, 0, boxFg) + tview.Print(screen, config.FormatTextWithStyle("Msg :", headerStyle), x+1, y, 6, 0, boxFg) + tview.Print(screen, config.FormatTextWithStyle("From :", headerStyle), x+1, y+1, 6, 0, boxFg) + tview.Print(screen, config.FormatTextWithStyle("To :", headerStyle), x+1, y+2, 6, 0, boxFg) + tview.Print(screen, config.FormatTextWithStyle("Subj :", headerStyle), x+1, y+3, 6, 0, boxFg) if e.HasFocus() { for i := e.sCoords[0].f; i < e.sCoords[0].t; i++ { - screen.SetContent(x+i, y+e.sCoords[0].y, ' ', nil, tcell.StyleDefault.Background(tcell.ColorNavy)) + screen.SetContent(x+i, y+e.sCoords[0].y, ' ', nil, defStyle.Background(bgSel)) } } for i := 0; i < len(e.sCoords); i++ { str := string(e.sInputs[i]) + style := itemStyle if utils.NamesEqual(config.Config.Username, str) { - str = "[::b]" + str + style = highlightStyle + } else { + style = itemStyle } - tview.Print(screen, str, x+e.sCoords[i].f, y+e.sCoords[i].y, len(e.sInputs[i]), 0, tcell.ColorSilver) + tview.Print(screen, config.FormatTextWithStyle(str, style), x+e.sCoords[i].f, y+e.sCoords[i].y, len(e.sInputs[i]), 0, boxFg) } if e.HasFocus() { screen.ShowCursor(x+e.sCoords[0].f+len(e.sInputs[0][:e.sPosition]), y+e.sCoords[0].y) diff --git a/pkg/ui/viewmsg.go b/pkg/ui/viewmsg.go index 06f80c6..3ce757b 100644 --- a/pkg/ui/viewmsg.go +++ b/pkg/ui/viewmsg.go @@ -3,6 +3,7 @@ package ui import ( //"errors" "fmt" + "github.com/askovpen/gossiped/pkg/config" "github.com/askovpen/gossiped/pkg/msgapi" "github.com/askovpen/gossiped/pkg/ui/editor" "github.com/gdamore/tcell/v2" @@ -40,13 +41,18 @@ func (a *App) ViewMsg(area *msgapi.AreaPrimitive, msgNum uint32) (string, tview. (*area).GetCount(), (*area).GetCount()-msgNum, )) + styleBorder := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementBorder) + fgTitle, bgTitle, titleAttrs := config.GetElementStyle(config.ColorAreaMessageHeader, config.ColorElementTitle).Decompose() header := NewViewHeader(msg) header.SetBorder(true). - SetBorderAttributes(tcell.AttrBold). - SetBorderColor(tcell.ColorBlue). - SetTitle(" " + (*area).GetName() + " "). - SetTitleAlign(tview.AlignLeft). - SetTitleColor(tcell.ColorYellow) + SetBorderStyle(styleBorder). + SetTitle(fmt.Sprintf("[%s:%s:%s] %s ", + fgTitle.String(), + bgTitle.String(), + config.MaskToStringStyle(titleAttrs), + (*area).GetName(), + )). + SetTitleAlign(tview.AlignLeft) var body *editor.View if msg != nil { body = editor.NewView(editor.NewBufferFromString(msg.ToView(a.showKludges))) @@ -190,6 +196,7 @@ func (a *App) showMessageList(area *msgapi.AreaPrimitive) (string, tview.Primiti }) return "MessageListModal", modal, true, true } + func (a *App) showAreaList(area *msgapi.AreaPrimitive, newMsgType int) (string, tview.Primitive, bool, bool) { modal := NewModalAreaList(). SetDoneFunc(func(buttonIndex int) {