diff --git a/color.go b/color.go deleted file mode 100644 index aa0de37..0000000 --- a/color.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "github.com/gdamore/tcell/v2" -) - -var ( - AllTheme = []string{"Gruvbox", "Nord"} - Palette = []string{"red", "green", "yellow", "blue", "purple", "cyan", "orange"} - Themes = map[string]map[string]tcell.Color{ - "Gruvbox": { - "bg": tcell.NewHexColor(0x282828), - "fg": tcell.NewHexColor(0xebdbb2), - "gray": tcell.NewHexColor(0x665c54), - "red": tcell.NewHexColor(0xfb4934), - "green": tcell.NewHexColor(0xb8bb26), - "yellow": tcell.NewHexColor(0xfabd2f), - "blue": tcell.NewHexColor(0x83a598), - "purple": tcell.NewHexColor(0xd3869b), - "cyan": tcell.NewHexColor(0x8ec07c), - "orange": tcell.NewHexColor(0xfe8019), - }, - "Nord": { - "bg": tcell.NewHexColor(0x3b4252), - "fg": tcell.NewHexColor(0xeceff4), - "gray": tcell.NewHexColor(0x4c566a), - "red": tcell.NewHexColor(0xbf616a), - "green": tcell.NewHexColor(0xa3be8c), - "yellow": tcell.NewHexColor(0xebcb8b), - "blue": tcell.NewHexColor(0x81a1c1), - "purple": tcell.NewHexColor(0xb48ead), - "cyan": tcell.NewHexColor(0x8fbcbb), - "orange": tcell.NewHexColor(0xd08770), - }, - } -) - -type WindowStyle struct { - borderColor string -} - -type Style struct { - src WindowStyle - dst WindowStyle - backgroundColor string - foregroundColor string - selectedColor string - prefixColor string - labelColor string - pressColor string - highLightColor string - Theme string - Transparent bool -} - -func NewStyle() *Style { - return &Style{ - backgroundColor: "bg", - foregroundColor: "fg", - selectedColor: "gray", - prefixColor: "cyan", - labelColor: "yellow", - pressColor: "purple", - highLightColor: "orange", - } -} - -func (s Style) BackgroundColor() tcell.Color { - if s.Transparent { - return tcell.ColorDefault - } - return Themes[s.Theme][s.backgroundColor] -} - -func (s Style) ForegroundColor() tcell.Color { - return Themes[s.Theme][s.foregroundColor] -} - -func (s Style) SelectedColor() tcell.Color { - return Themes[s.Theme][s.selectedColor] -} - -func (s Style) PrefixColor() tcell.Color { - return Themes[s.Theme][s.prefixColor] -} - -func (s Style) LabelColor() tcell.Color { - return Themes[s.Theme][s.labelColor] -} - -func (s Style) PressColor() tcell.Color { - return Themes[s.Theme][s.pressColor] -} - -func (s Style) HighLightColor() tcell.Color { - return Themes[s.Theme][s.highLightColor] -} - -func (s Style) SrcBorderColor() tcell.Color { - return Themes[s.Theme][s.src.borderColor] -} - -func (s Style) DstBorderColor() tcell.Color { - return Themes[s.Theme][s.dst.borderColor] -} - -func (s Style) SrcBorderStr() string { - return s.src.borderColor -} - -func (s Style) DstBorderStr() string { - return s.dst.borderColor -} - -func (s *Style) SetSrcBorderColor(color string) *Style { - s.src.borderColor = color - return s -} - -func (s *Style) SetDstBorderColor(color string) *Style { - s.dst.borderColor = color - return s -} diff --git a/config.go b/config.go index 66129f4..351f65b 100644 --- a/config.go +++ b/config.go @@ -33,8 +33,8 @@ func configInit() { } // setup - translator.srcLang = config.GetString("source.language") - translator.dstLang = config.GetString("destination.language") + translator.SrcLang = config.GetString("source.language") + translator.DstLang = config.GetString("destination.language") style.Theme = config.GetString("theme") style.Transparent = config.GetBool("transparent") style.SetSrcBorderColor(config.GetString("source.borderColor")). @@ -53,17 +53,17 @@ func updateConfig() { changed = true config.Set("transparent", style.Transparent) } - if config.GetString("source.language") != translator.srcLang { + if config.GetString("source.language") != translator.SrcLang { changed = true - config.Set("source.language", translator.srcLang) + config.Set("source.language", translator.SrcLang) } if config.GetString("source.borderColor") != style.SrcBorderStr() { changed = true config.Set("source.borderColor", style.SrcBorderStr()) } - if config.GetString("destination.language") != translator.dstLang { + if config.GetString("destination.language") != translator.DstLang { changed = true - config.Set("destination.language", translator.dstLang) + config.Set("destination.language", translator.DstLang) } if config.GetString("destination.borderColor") != style.DstBorderStr() { changed = true diff --git a/internal/color/color.go b/internal/color/color.go new file mode 100644 index 0000000..aeee67b --- /dev/null +++ b/internal/color/color.go @@ -0,0 +1,36 @@ +package color + +import ( + "github.com/gdamore/tcell/v2" +) + +var ( + AllTheme = []string{"Gruvbox", "Nord"} + Palette = []string{"red", "green", "yellow", "blue", "purple", "cyan", "orange"} + themes = map[string]map[string]tcell.Color{ + "Gruvbox": { + "bg": tcell.NewHexColor(0x282828), + "fg": tcell.NewHexColor(0xebdbb2), + "gray": tcell.NewHexColor(0x665c54), + "red": tcell.NewHexColor(0xfb4934), + "green": tcell.NewHexColor(0xb8bb26), + "yellow": tcell.NewHexColor(0xfabd2f), + "blue": tcell.NewHexColor(0x83a598), + "purple": tcell.NewHexColor(0xd3869b), + "cyan": tcell.NewHexColor(0x8ec07c), + "orange": tcell.NewHexColor(0xfe8019), + }, + "Nord": { + "bg": tcell.NewHexColor(0x3b4252), + "fg": tcell.NewHexColor(0xeceff4), + "gray": tcell.NewHexColor(0x4c566a), + "red": tcell.NewHexColor(0xbf616a), + "green": tcell.NewHexColor(0xa3be8c), + "yellow": tcell.NewHexColor(0xebcb8b), + "blue": tcell.NewHexColor(0x81a1c1), + "purple": tcell.NewHexColor(0xb48ead), + "cyan": tcell.NewHexColor(0x8fbcbb), + "orange": tcell.NewHexColor(0xd08770), + }, + } +) diff --git a/internal/color/control.go b/internal/color/control.go new file mode 100644 index 0000000..b793d05 --- /dev/null +++ b/internal/color/control.go @@ -0,0 +1,92 @@ +package color + +import ( + "github.com/gdamore/tcell/v2" +) + +type windowStyle struct { + borderColor string +} + +type Style struct { + src windowStyle + dst windowStyle + backgroundColor string + foregroundColor string + selectedColor string + prefixColor string + labelColor string + pressColor string + highLightColor string + Theme string + Transparent bool +} + +func NewStyle() *Style { + return &Style{ + backgroundColor: "bg", + foregroundColor: "fg", + selectedColor: "gray", + prefixColor: "cyan", + labelColor: "yellow", + pressColor: "purple", + highLightColor: "orange", + } +} + +func (s Style) BackgroundColor() tcell.Color { + if s.Transparent { + return tcell.ColorDefault + } + return themes[s.Theme][s.backgroundColor] +} + +func (s Style) ForegroundColor() tcell.Color { + return themes[s.Theme][s.foregroundColor] +} + +func (s Style) SelectedColor() tcell.Color { + return themes[s.Theme][s.selectedColor] +} + +func (s Style) PrefixColor() tcell.Color { + return themes[s.Theme][s.prefixColor] +} + +func (s Style) LabelColor() tcell.Color { + return themes[s.Theme][s.labelColor] +} + +func (s Style) PressColor() tcell.Color { + return themes[s.Theme][s.pressColor] +} + +func (s Style) HighLightColor() tcell.Color { + return themes[s.Theme][s.highLightColor] +} + +func (s Style) SrcBorderColor() tcell.Color { + return themes[s.Theme][s.src.borderColor] +} + +func (s Style) DstBorderColor() tcell.Color { + return themes[s.Theme][s.dst.borderColor] +} + +func (s Style) SrcBorderStr() string { + return s.src.borderColor +} + +func (s Style) DstBorderStr() string { + return s.dst.borderColor +} + +func (s *Style) SetSrcBorderColor(color string) *Style { + s.src.borderColor = color + return s +} + +func (s *Style) SetDstBorderColor(color string) *Style { + s.dst.borderColor = color + return s +} diff --git a/language.go b/internal/translate/language.go similarity index 99% rename from language.go rename to internal/translate/language.go index 69b6ae8..94cb10f 100644 --- a/language.go +++ b/internal/translate/language.go @@ -1,4 +1,4 @@ -package main +package translate // https://cloud.google.com/translate/docs/languages var ( diff --git a/internal/translate/lock.go b/internal/translate/lock.go new file mode 100644 index 0000000..3f5973d --- /dev/null +++ b/internal/translate/lock.go @@ -0,0 +1,27 @@ +package translate + +type Lock struct { + Stop bool + threadCount int8 +} + +func NewLock() *Lock { + return &Lock{ + Stop: true, + threadCount: 0, + } +} + +func (l *Lock) Available() bool { + return l.Stop && l.threadCount == 0 +} + +func (l *Lock) Acquire() { + l.Stop = false + l.threadCount++ +} + +func (l *Lock) Release() { + l.Stop = true + l.threadCount-- +} diff --git a/translator.go b/internal/translate/translator.go similarity index 72% rename from translator.go rename to internal/translate/translator.go index 03eb863..50aef58 100644 --- a/translator.go +++ b/internal/translate/translator.go @@ -1,4 +1,4 @@ -package main +package translate import ( "encoding/json" @@ -17,41 +17,15 @@ const ( soundURL = "https://translate.google.com.vn/translate_tts?ie=UTF-8&q=%s&tl=%s&client=tw-ob" ) -type Lock struct { - stop bool - threadCount int8 -} - -func NewLock() *Lock { - return &Lock{ - stop: true, - threadCount: 0, - } -} - -func (l *Lock) Available() bool { - return l.stop && l.threadCount == 0 -} - -func (l *Lock) Acquire() { - l.stop = false - l.threadCount++ -} - -func (l *Lock) Release() { - l.stop = true - l.threadCount-- -} - type Translator struct { - srcLang string - dstLang string - soundLock *Lock + SrcLang string + DstLang string + SoundLock *Lock } func NewTranslator() *Translator { return &Translator{ - soundLock: NewLock(), + SoundLock: NewLock(), } } @@ -61,8 +35,8 @@ func (t *Translator) Translate(message string) (string, error) { urlStr := fmt.Sprintf( textURL, - LangCode[t.srcLang], - LangCode[t.dstLang], + LangCode[t.SrcLang], + LangCode[t.DstLang], url.QueryEscape(message), ) res, err := http.Get(urlStr) @@ -98,35 +72,35 @@ func (t *Translator) PlaySound(lang string, message string) error { ) res, err := http.Get(urlStr) if err != nil { - t.soundLock.Release() + t.SoundLock.Release() return err } decoder, err := mp3.NewDecoder(res.Body) if err != nil { - t.soundLock.Release() + t.SoundLock.Release() return err } otoCtx, readyChan, err := oto.NewContext(decoder.SampleRate(), 2, 2) if err != nil { - t.soundLock.Release() + t.SoundLock.Release() return err } <-readyChan player := otoCtx.NewPlayer(decoder) player.Play() for player.IsPlaying() { - if t.soundLock.stop { - t.soundLock.Release() + if t.SoundLock.Stop { + t.SoundLock.Release() return nil } else { time.Sleep(time.Millisecond) } } if err = player.Close(); err != nil { - t.soundLock.Release() + t.SoundLock.Release() return err } - t.soundLock.Release() + t.SoundLock.Release() return nil } diff --git a/internal/ui/cycle.go b/internal/ui/cycle.go new file mode 100644 index 0000000..f0c9b2b --- /dev/null +++ b/internal/ui/cycle.go @@ -0,0 +1,37 @@ +package ui + +import ( + "github.com/rivo/tview" +) + +type UICycle struct { + widget []tview.Primitive + index int + len int +} + +func NewUICycle(widgets ...tview.Primitive) *UICycle { + var w []tview.Primitive + + for _, widget := range widgets { + w = append(w, widget) + } + + return &UICycle{ + widget: w, + index: 0, + len: len(w), + } +} + +func (ui *UICycle) Increase() { + ui.index = (ui.index + 1) % ui.len +} + +func (ui *UICycle) Decrease() { + ui.index = ((ui.index-1)%ui.len + ui.len) % ui.len +} + +func (ui *UICycle) GetCurrentUI() tview.Primitive { + return ui.widget[ui.index] +} diff --git a/main.go b/main.go index ab2c5af..96ff902 100644 --- a/main.go +++ b/main.go @@ -3,27 +3,30 @@ package main import ( "github.com/rivo/tview" "github.com/spf13/viper" + "gtt/internal/color" + "gtt/internal/translate" + "gtt/internal/ui" ) var ( // Translate - translator = NewTranslator() + translator = translate.NewTranslator() // UI app = tview.NewApplication() srcInput = tview.NewTextArea() dstOutput = tview.NewTextView() srcLangDropDown = tview.NewDropDown() dstLangDropDown = tview.NewDropDown() - langCycle = NewUICycle(srcLangDropDown, dstLangDropDown) + langCycle = ui.NewUICycle(srcLangDropDown, dstLangDropDown) themeDropDown = tview.NewDropDown() transparentDropDown = tview.NewDropDown() srcBorderDropDown = tview.NewDropDown() dstBorderDropDown = tview.NewDropDown() - styleCycle = NewUICycle(themeDropDown, transparentDropDown, srcBorderDropDown, dstBorderDropDown) + styleCycle = ui.NewUICycle(themeDropDown, transparentDropDown, srcBorderDropDown, dstBorderDropDown) keyMapMenu = tview.NewTextView() langButton = tview.NewButton("(1)Language") styleButton = tview.NewButton("(2)Style") - keyMapButton = tview.NewButton("(3)KeyMap") + keyMapButton = tview.NewButton("(3)KeyMap") translateWindow = tview.NewFlex() langWindow = tview.NewFlex() styleWindow = tview.NewFlex() @@ -31,7 +34,7 @@ var ( mainPage = tview.NewPages() // settings config = viper.New() - style = NewStyle() + style = color.NewStyle() ) func main() { @@ -49,5 +52,6 @@ func main() { panic(err) } + // Check if config need to update defer updateConfig() } diff --git a/ui.go b/ui.go index 54f4993..ebd33bf 100644 --- a/ui.go +++ b/ui.go @@ -4,41 +4,11 @@ import ( "fmt" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" + "gtt/internal/color" + "gtt/internal/translate" "strconv" ) -type UICycle struct { - widget []tview.Primitive - index int - len int -} - -func NewUICycle(widgets ...tview.Primitive) *UICycle { - var w []tview.Primitive - - for _, widget := range widgets { - w = append(w, widget) - } - - return &UICycle{ - widget: w, - index: 0, - len: len(w), - } -} - -func (ui *UICycle) Increase() { - ui.index = (ui.index + 1) % ui.len -} - -func (ui *UICycle) Decrease() { - ui.index = ((ui.index-1)%ui.len + ui.len) % ui.len -} - -func (ui *UICycle) GetCurrentUI() tview.Primitive { - return ui.widget[ui.index] -} - const ( keyMapText string = `[#%[1]s][-] Exit program. @@ -197,12 +167,12 @@ func updateAllColor() { // Update title and option func updateTitle() { - srcInput.SetTitle(translator.srcLang) - dstOutput.SetTitle(translator.dstLang) - srcLangDropDown.SetCurrentOption(IndexOf(translator.srcLang, Lang)) - srcLangDropDown.SetTitle(translator.srcLang) - dstLangDropDown.SetCurrentOption(IndexOf(translator.dstLang, Lang)) - dstLangDropDown.SetTitle(translator.dstLang) + srcInput.SetTitle(translator.SrcLang) + dstOutput.SetTitle(translator.DstLang) + srcLangDropDown.SetCurrentOption(IndexOf(translator.SrcLang, translate.Lang)) + srcLangDropDown.SetTitle(translator.SrcLang) + dstLangDropDown.SetCurrentOption(IndexOf(translator.DstLang, translate.Lang)) + dstLangDropDown.SetTitle(translator.DstLang) } func attachButton() *tview.Flex { @@ -223,25 +193,25 @@ func uiInit() { // dropdown srcLangDropDown.SetBorder(true) - srcLangDropDown.SetOptions(Lang, nil) + srcLangDropDown.SetOptions(translate.Lang, nil) dstLangDropDown.SetBorder(true) - dstLangDropDown.SetOptions(Lang, nil) + dstLangDropDown.SetOptions(translate.Lang, nil) themeDropDown.SetLabel("Theme: "). - SetOptions(AllTheme, nil). - SetCurrentOption(IndexOf(style.Theme, AllTheme)) + SetOptions(color.AllTheme, nil). + SetCurrentOption(IndexOf(style.Theme, color.AllTheme)) transparentDropDown.SetLabel("Transparent: "). SetOptions([]string{"true", "false"}, nil). SetCurrentOption( IndexOf(strconv.FormatBool(style.Transparent), []string{"true", "false"})) srcBorderDropDown.SetLabel("Border Color: "). - SetOptions(Palette, nil). - SetCurrentOption(IndexOf(style.SrcBorderStr(), Palette)) + SetOptions(color.Palette, nil). + SetCurrentOption(IndexOf(style.SrcBorderStr(), color.Palette)) srcBorderDropDown.SetBorder(true). SetTitle("Source") dstBorderDropDown.SetLabel("Border Color: "). - SetOptions(Palette, nil). - SetCurrentOption(IndexOf(style.DstBorderStr(), Palette)) + SetOptions(color.Palette, nil). + SetCurrentOption(IndexOf(style.DstBorderStr(), color.Palette)) dstBorderDropDown.SetBorder(true). SetTitle("Destination") @@ -303,19 +273,19 @@ func uiInit() { // handler mainPage.SetInputCapture(mainPageHandler) + translateWindow.SetInputCapture(translatePageHandler) langWindow.SetInputCapture(popOutWindowHandler) styleWindow.SetInputCapture(popOutWindowHandler) keyMapWindow.SetInputCapture(popOutWindowHandler) - translateWindow.SetInputCapture(translatePageHandler) srcLangDropDown.SetDoneFunc(langDropDownHandler). SetSelectedFunc(func(text string, index int) { - translator.srcLang = text + translator.SrcLang = text srcInput.SetTitle(text) srcLangDropDown.SetTitle(text) }) dstLangDropDown.SetDoneFunc(langDropDownHandler). SetSelectedFunc(func(text string, index int) { - translator.dstLang = text + translator.DstLang = text dstOutput.SetTitle(text) dstLangDropDown.SetTitle(text) }) @@ -380,29 +350,6 @@ func mainPageHandler(event *tcell.EventKey) *tcell.EventKey { return event } -func popOutWindowHandler(event *tcell.EventKey) *tcell.EventKey { - ch := event.Rune() - - switch ch { - case '1': - mainPage.HidePage("stylePage") - mainPage.HidePage("keyMapPage") - mainPage.ShowPage("langPage") - app.SetFocus(langCycle.GetCurrentUI()) - case '2': - mainPage.HidePage("langPage") - mainPage.HidePage("keyMapPage") - mainPage.ShowPage("stylePage") - app.SetFocus(styleCycle.GetCurrentUI()) - case '3': - mainPage.HidePage("langPage") - mainPage.HidePage("stylePage") - mainPage.ShowPage("keyMapPage") - } - - return event -} - func translatePageHandler(event *tcell.EventKey) *tcell.EventKey { key := event.Key() @@ -424,7 +371,7 @@ func translatePageHandler(event *tcell.EventKey) *tcell.EventKey { case tcell.KeyCtrlQ: srcInput.SetText("", true) case tcell.KeyCtrlS: - translator.srcLang, translator.dstLang = translator.dstLang, translator.srcLang + translator.SrcLang, translator.DstLang = translator.DstLang, translator.SrcLang updateTitle() srcText := srcInput.GetText() dstText := dstOutput.GetText(false) @@ -437,13 +384,13 @@ func translatePageHandler(event *tcell.EventKey) *tcell.EventKey { dstOutput.SetText(srcText) case tcell.KeyCtrlO: // Play source sound - if translator.soundLock.Available() { + if translator.SoundLock.Available() { message := srcInput.GetText() if len(message) > 0 { // Only play when message exist - translator.soundLock.Acquire() + translator.SoundLock.Acquire() go func() { - err := translator.PlaySound(translator.srcLang, message) + err := translator.PlaySound(translator.SrcLang, message) if err != nil { srcInput.SetText(err.Error(), true) } @@ -453,13 +400,13 @@ func translatePageHandler(event *tcell.EventKey) *tcell.EventKey { } case tcell.KeyCtrlP: // Play destination sound - if translator.soundLock.Available() { + if translator.SoundLock.Available() { message := dstOutput.GetText(false) if len(message) > 0 { // Only play when message exist - translator.soundLock.Acquire() + translator.SoundLock.Acquire() go func() { - err := translator.PlaySound(translator.dstLang, message) + err := translator.PlaySound(translator.DstLang, message) if err != nil { dstOutput.SetText(err.Error()) } @@ -468,7 +415,30 @@ func translatePageHandler(event *tcell.EventKey) *tcell.EventKey { } case tcell.KeyCtrlX: // Stop play sound - translator.soundLock.stop = true + translator.SoundLock.Stop = true + } + + return event +} + +func popOutWindowHandler(event *tcell.EventKey) *tcell.EventKey { + ch := event.Rune() + + switch ch { + case '1': + mainPage.HidePage("stylePage") + mainPage.HidePage("keyMapPage") + mainPage.ShowPage("langPage") + app.SetFocus(langCycle.GetCurrentUI()) + case '2': + mainPage.HidePage("langPage") + mainPage.HidePage("keyMapPage") + mainPage.ShowPage("stylePage") + app.SetFocus(styleCycle.GetCurrentUI()) + case '3': + mainPage.HidePage("langPage") + mainPage.HidePage("stylePage") + mainPage.ShowPage("keyMapPage") } return event