diff --git a/README.md b/README.md index e96ce17..a20f81b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:compose` compose a new toot * `:favorited` lists toots you've favorited * `:favorites` lists users that favorited the toot +* `:h` `:help` view help * `:lists` show a list of your lists * `:muting` lists users that you have muted * `:profile` go to your profile @@ -50,6 +51,7 @@ Keys without description in tut * `arrow keys` = navigation. Same as `jk` and `hl` * `g` or `Home` = go to top * `G` or `End` = go to bottom +* `?` = view help * `q` = go back and quit * `ESC` = go back diff --git a/cmdbar.go b/cmdbar.go index 4863a8c..f085645 100644 --- a/cmdbar.go +++ b/cmdbar.go @@ -180,5 +180,9 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { c.app.UI.StatusView.AddFeed(NewListFeed(c.app)) c.app.UI.SetFocus(LeftPaneFocus) c.app.UI.CmdBar.ClearInput() + case ":help", ":h": + c.app.UI.SetFocus(HelpOverlayFocus) + c.app.UI.CmdBar.ClearInput() } + } diff --git a/config.example.ini b/config.example.ini index d82def5..8a19f21 100644 --- a/config.example.ini +++ b/config.example.ini @@ -92,6 +92,10 @@ char-limit=500 # default=true show-icons=true +# If you want to show a message in the cmdbar how you can view the help message +# default=true +show-help=true + # If you've learnt all the shortcut keys you can remove the help text and # only show the key in tui. So it gets less cluttered. # default=false diff --git a/config.go b/config.go index ef57771..ffd352c 100644 --- a/config.go +++ b/config.go @@ -19,6 +19,9 @@ var tootTemplate string //go:embed user.tmpl var userTemplate string +//go:embed help.tmpl +var helpTemplate string + type Config struct { General GeneralConfig Style StyleConfig @@ -47,6 +50,7 @@ type GeneralConfig struct { ListProportion int ContentProportion int ShowIcons bool + ShowHelp bool } type StyleConfig struct { @@ -156,6 +160,7 @@ type NotificationConfig struct { type TemplatesConfig struct { TootTemplate *template.Template UserTemplate *template.Template + HelpTemplate *template.Template } func parseColor(input string, def string, xrdb map[string]string) tcell.Color { @@ -290,6 +295,7 @@ func parseGeneral(cfg *ini.File) GeneralConfig { general.ShortHints = cfg.Section("general").Key("short-hints").MustBool(false) general.HideNotificationText = cfg.Section("general").Key("hide-notification-text").MustBool(false) general.ShowIcons = cfg.Section("general").Key("show-icons").MustBool(true) + general.ShowHelp = cfg.Section("general").Key("show-help").MustBool(true) lp := cfg.Section("general").Key("list-placement").In("left", []string{"left", "right", "top", "bottom"}) switch lp { @@ -512,9 +518,18 @@ func ParseTemplates(cfg *ini.File) TemplatesConfig { if err != nil { log.Fatalf("Couldn't parse user.tmpl. Error: %v", err) } + var helpTmpl *template.Template + helpTmpl, err = template.New("help.tmpl").Funcs(template.FuncMap{ + "Color": ColorMark, + "Flags": TextFlags, + }).Parse(helpTemplate) + if err != nil { + log.Fatalf("Couldn't parse help.tmpl. Error: %v", err) + } return TemplatesConfig{ TootTemplate: tootTmpl, UserTemplate: userTmpl, + HelpTemplate: helpTmpl, } } @@ -657,6 +672,10 @@ char-limit=500 # default=true show-icons=true +# If you want to show a message in the cmdbar how you can view the help message +# default=true +show-help=true + # If you've learnt all the shortcut keys you can remove the help text and # only show the key in tui. So it gets less cluttered. # default=false diff --git a/help.tmpl b/help.tmpl new file mode 100644 index 0000000..5c50a6f --- /dev/null +++ b/help.tmpl @@ -0,0 +1,87 @@ +{{ Color .Style.Text }}{{ Flags "b" }}Keys and navigation{{ Flags "-" }} + +Below are the keys that doesn't have hints in the TUI. All other keys have hints in the TUI. E.g. you're viewing a toot, down at the bottom you'll see {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}F{{ Flags "-" }}{{ Color .Style.Text }}avorite. +That means that you'll have to press {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}F{{ Flags "-" }}{{ Color .Style.Text }} to favorite a toot. + + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}c{{ Flags "-" }}{{ Color .Style.Text }} - Compose a new toot + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}j{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} Down arrow{{ Flags "-" }}{{ Color .Style.Text }} - Navigate down in feed list or toot + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}k{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} Up arrow{{ Flags "-" }}{{ Color .Style.Text }} - Navigate up in feed list or toot + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}h{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} Left arrow{{ Flags "-" }}{{ Color .Style.Text }} - Cycle back between feeds + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}l{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} Right arrow{{ Flags "-" }}{{ Color .Style.Text }} - Cycle forward between feeds + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}g{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} Home{{ Flags "-" }}{{ Color .Style.Text }} - Go to top in feed list or toot + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}G{{ Flags "-" }}{{ Color .Style.Text }} or + {{- Color .Style.TextSpecial2 }}{{ Flags "b" }} End{{ Flags "-" }}{{ Color .Style.Text }} - Go to bottom in feed list or toot + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}?{{ Flags "-" }}{{ Color .Style.Text }} - View help + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}q{{ Flags "-" }}{{ Color .Style.Text }} - Go back or quit + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}ESC{{ Flags "-" }}{{ Color .Style.Text }} - Go back + +{{ Color .Style.Text }}{{ Flags "b" }}Commands{{ Flags "-" }} + +All commands start with {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:{{ Flags "-" }}{{ Color .Style.Text }}. And you run the command by hitting {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}Enter{{ Flags "-" }}{{ Color .Style.Text }}. + +Here's a list of supported commands. + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:q{{ Flags "-" }}{{ Color .Style.Text }} or +{{- Color .Style.TextSpecial2 }}{{ Flags "b" }} :quit{{ Flags "-" }}{{ Color .Style.Text }} + Exit the program + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:timeline{{ Flags "-" }}{{ Color .Style.Text }} home|local|federated|direct|notifications|favorited + Open selected timeline + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:tl{{ Flags "-" }}{{ Color .Style.Text }} h|l|f|d|n|fav + Shorter form of the former command *:timeline* + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:blocking{{ Flags "-" }}{{ Color .Style.Text }} + Lists users that you have blocked + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:boosts{{ Flags "-" }}{{ Color .Style.Text }} + Lists users that boosted the toot + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:bookmarks{{ Flags "-" }}{{ Color .Style.Text }} + Lists all your bookmarks + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:compose{{ Flags "-" }}{{ Color .Style.Text }} + Compose a new toot + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:favorited{{ Flags "-" }}{{ Color .Style.Text }} + Lists toots you've favorited + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:favorites{{ Flags "-" }}{{ Color .Style.Text }} + Lists users that favorited the toot + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:h{{ Flags "-" }}{{ Color .Style.Text }} or +{{- Color .Style.TextSpecial2 }}{{ Flags "b" }} :help{{ Flags "-" }}{{ Color .Style.Text }} + View this help message + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:lists{{ Flags "-" }}{{ Color .Style.Text }} + Show a list of your lists + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:muting{{ Flags "-" }}{{ Color .Style.Text }} + lists users that you have muted + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:profile{{ Flags "-" }}{{ Color .Style.Text }} + Go to your own profile + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:saved{{ Flags "-" }}{{ Color .Style.Text }} + Alias for :bookmarks + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:tag{{ Flags "-" }}{{ Color .Style.Text }} tagname + See toots for a tag. E.g. :tag linux + +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:user{{ Flags "-" }}{{ Color .Style.Text }} username + Go to profile for . E.g. :user rasmus + To narrow a search include the instance like this :user rasmus@mastodon.acc.sunet.se + +{{ Color .Style.Text }}{{ Flags "b" }}Configuration{{ Flags "-" }} + +tut searches for a config file in the following locations: + + 1. $XDG_CONFIG_HOME/tut/config.ini + 2. $HOME/.config/tut/config.ini + +For information on the config file format go to . diff --git a/helpoverlay.go b/helpoverlay.go new file mode 100644 index 0000000..5d446d3 --- /dev/null +++ b/helpoverlay.go @@ -0,0 +1,64 @@ +package main + +import ( + "bytes" + + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +func NewHelpOverlay(app *App) *HelpOverlay { + h := &HelpOverlay{ + app: app, + Flex: tview.NewFlex(), + TextMain: tview.NewTextView(), + TextBottom: tview.NewTextView(), + } + + h.TextMain.SetBackgroundColor(app.Config.Style.Background) + h.TextMain.SetDynamicColors(true) + h.TextBottom.SetBackgroundColor(app.Config.Style.Background) + h.TextBottom.SetDynamicColors(true) + h.TextBottom.SetText(ColorKey(app.Config, "", "Q", "uit")) + h.Flex.SetDrawFunc(app.Config.ClearContent) + + hd := HelpData{ + Style: app.Config.Style, + } + var output bytes.Buffer + err := app.Config.Templates.HelpTemplate.ExecuteTemplate(&output, "help.tmpl", hd) + if err != nil { + panic(err) + } + h.TextMain.SetText(output.String()) + + return h +} + +type HelpData struct { + Style StyleConfig +} + +type HelpOverlay struct { + app *App + Flex *tview.Flex + TextMain *tview.TextView + TextBottom *tview.TextView +} + +func (h *HelpOverlay) InputHandler(event *tcell.EventKey) *tcell.EventKey { + if event.Key() == tcell.KeyRune { + switch event.Rune() { + case 'q', 'Q': + h.app.UI.StatusView.giveBackFocus() + return nil + } + } else { + switch event.Key() { + case tcell.KeyEsc: + h.app.UI.StatusView.giveBackFocus() + return nil + } + } + return event +} diff --git a/linkoverlay.go b/linkoverlay.go index 8005970..324d1f6 100644 --- a/linkoverlay.go +++ b/linkoverlay.go @@ -194,11 +194,7 @@ func (l *LinkOverlay) InputHandler(event *tcell.EventKey) { i, _ := strconv.Atoi(s) l.OpenCustom(i) case 'q', 'Q': - if l.app.UI.StatusView.lastList == NotificationPaneFocus { - l.app.UI.SetFocus(NotificationPaneFocus) - } else { - l.app.UI.SetFocus(LeftPaneFocus) - } + l.app.UI.StatusView.giveBackFocus() } } else { switch event.Key() { @@ -209,11 +205,7 @@ func (l *LinkOverlay) InputHandler(event *tcell.EventKey) { case tcell.KeyDown: l.Next() case tcell.KeyEsc: - if l.app.UI.StatusView.lastList == NotificationPaneFocus { - l.app.UI.SetFocus(NotificationPaneFocus) - } else { - l.app.UI.SetFocus(LeftPaneFocus) - } + l.app.UI.StatusView.giveBackFocus() } } } diff --git a/main.go b/main.go index 3103d2e..bfd106c 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( "github.com/gdamore/tcell/v2" ) -const version string = "0.0.40" +const version string = "0.0.41" func main() { newUser := false @@ -165,6 +165,11 @@ func main() { return nil } + if app.UI.Focus == HelpOverlayFocus { + ev := app.UI.HelpOverlay.InputHandler(event) + return ev + } + if app.UI.Focus == VoteOverlayFocus { app.UI.VoteOverlay.InputHandler(event) return nil @@ -207,21 +212,13 @@ func main() { app.UI.SetFocus(VisibilityOverlayFocus) return nil case 'q', 'Q': - if app.UI.StatusView.lastList == NotificationPaneFocus { - app.UI.SetFocus(NotificationPaneFocus) - } else { - app.UI.SetFocus(LeftPaneFocus) - } + app.UI.StatusView.giveBackFocus() return nil } } else { switch event.Key() { case tcell.KeyEsc: - if app.UI.StatusView.lastList == NotificationPaneFocus { - app.UI.SetFocus(NotificationPaneFocus) - } else { - app.UI.SetFocus(LeftPaneFocus) - } + app.UI.StatusView.giveBackFocus() return nil } } @@ -304,7 +301,7 @@ func main() { ) app.UI.CmdBar.Input.SetAutocompleteFunc(func(currentText string) (entries []string) { - words := strings.Split(":blocking,:boosts,:bookmarks,:compose,:favorites,:favorited,:lists,:muting,:profile,:saved,:tag,:timeline,:tl,:user,:quit,:q", ",") + words := strings.Split(":blocking,:boosts,:bookmarks,:compose,:favorites,:favorited,:help,:h,:lists,:muting,:profile,:saved,:tag,:timeline,:tl,:user,:quit,:q", ",") if currentText == "" { return } diff --git a/messagebox.go b/messagebox.go index 81fa4a4..7f7581c 100644 --- a/messagebox.go +++ b/messagebox.go @@ -159,12 +159,7 @@ func (m *MessageBox) Post() { m.app.UI.CmdBar.ShowError(fmt.Sprintf("Couldn't post toot. Error: %v\n", err)) return } - if m.app.UI.StatusView.lastList == NotificationPaneFocus { - m.app.UI.SetFocus(NotificationPaneFocus) - } else { - m.app.UI.SetFocus(LeftPaneFocus) - } - m.app.UI.SetFocus(LeftPaneFocus) + m.app.UI.StatusView.giveBackFocus() } func (m *MessageBox) TootLength() int { diff --git a/statusview.go b/statusview.go index 870f2e6..49627f6 100644 --- a/statusview.go +++ b/statusview.go @@ -263,6 +263,8 @@ func (t *StatusView) inputBoth(event *tcell.EventKey) { t.home() case 'G': t.end() + case '?': + t.app.UI.SetFocus(HelpOverlayFocus) } } else { switch event.Key() { @@ -675,3 +677,29 @@ func (t *StatusView) loadOlder() { }) }() } + +func (t *StatusView) giveBackFocus() { + if t.focus == RightPaneFocus { + t.app.UI.SetFocus(RightPaneFocus) + t.focus = RightPaneFocus + t.app.UI.StatusBar.Text.SetBackgroundColor( + t.app.Config.Style.StatusBarViewBackground, + ) + t.app.UI.StatusBar.Text.SetTextColor( + t.app.Config.Style.StatusBarViewText, + ) + return + } else if t.lastList == LeftPaneFocus { + t.app.UI.SetFocus(LeftPaneFocus) + t.focus = LeftPaneFocus + } else if t.lastList == NotificationPaneFocus { + t.app.UI.SetFocus(NotificationPaneFocus) + t.focus = NotificationPaneFocus + } + t.app.UI.StatusBar.Text.SetBackgroundColor( + t.app.Config.Style.StatusBarBackground, + ) + t.app.UI.StatusBar.Text.SetTextColor( + t.app.Config.Style.StatusBarText, + ) +} diff --git a/ui.go b/ui.go index c49e3e1..ea5265a 100644 --- a/ui.go +++ b/ui.go @@ -24,6 +24,7 @@ const ( VoteOverlayFocus AuthOverlayFocus UserSelectFocus + HelpOverlayFocus ) func NewUI(app *App) *UI { @@ -61,6 +62,7 @@ func (ui *UI) Init() { ui.AuthOverlay = NewAuthOverlay(ui.app) ui.UserSelectOverlay = NewUserSelectOverlay(ui.app) ui.MediaOverlay = NewMediaOverlay(ui.app) + ui.HelpOverlay = NewHelpOverlay(ui.app) ui.Pages.SetBackgroundColor(ui.app.Config.Style.Background) @@ -155,6 +157,14 @@ func (ui *UI) Init() { AddItem(ui.MediaOverlay.InputField.View, 2, 1, false), 0, 8, false). AddItem(nil, 0, 1, false), 0, 8, true). AddItem(nil, 0, 1, false), true, false) + ui.Pages.AddPage("help", tview.NewFlex().AddItem(nil, 0, 1, false). + AddItem(tview.NewFlex().SetDirection(tview.FlexRow). + AddItem(nil, 0, 1, false). + AddItem(ui.HelpOverlay.Flex.SetDirection(tview.FlexRow). + AddItem(ui.HelpOverlay.TextMain, 0, 10, true). + AddItem(ui.HelpOverlay.TextBottom, 1, 1, true), 0, 8, false). + AddItem(nil, 0, 1, false), 0, 8, true). + AddItem(nil, 0, 1, false), true, false) ui.Root.SetBeforeDrawFunc(func(screen tcell.Screen) bool { screen.Clear() @@ -179,6 +189,7 @@ type UI struct { MediaOverlay *MediaView Timeline TimelineType StatusView *StatusView + HelpOverlay *HelpOverlay } func (ui *UI) FocusAt(p tview.Primitive, s string) { @@ -196,6 +207,7 @@ func (ui *UI) SetFocus(f FocusAt) { ui.Focus = f switch f { case RightPaneFocus: + ui.Pages.SwitchToPage("main") ui.FocusAt(ui.StatusView.text, "-- VIEW --") case CmdBarFocus: ui.FocusAt(ui.CmdBar.Input, "-- CMD --") @@ -212,6 +224,13 @@ func (ui *UI) SetFocus(f FocusAt) { ui.Pages.ShowPage("links") ui.Root.SetFocus(ui.LinkOverlay.List) ui.FocusAt(ui.LinkOverlay.List, "-- LINK --") + case HelpOverlayFocus: + ui.Pages.ShowPage("help") + if ui.app.Config.General.ShowHelp { + ui.CmdBar.ClearInput() + } + ui.Root.SetFocus(ui.HelpOverlay.TextMain) + ui.FocusAt(ui.HelpOverlay.TextMain, "-- HELP --") case VoteOverlayFocus: ui.Pages.ShowPage("vote") ui.Root.SetFocus(ui.VoteOverlay.List) @@ -362,6 +381,9 @@ func (ui *UI) SetTopText(s string) { } func (ui *UI) LoggedIn() { + if ui.app.Config.General.ShowHelp { + ui.CmdBar.ShowMsg("Press ? or :help to learn how tut functions") + } ui.StatusView = NewStatusView(ui.app, ui.Timeline) verticalLine := tview.NewBox() diff --git a/voteoverlay.go b/voteoverlay.go index b2571c6..7c32b6d 100644 --- a/voteoverlay.go +++ b/voteoverlay.go @@ -141,11 +141,7 @@ func (v *VoteOverlay) Vote() { return } v.app.UI.StatusView.RedrawPoll(p) - if v.app.UI.StatusView.lastList == NotificationPaneFocus { - v.app.UI.SetFocus(NotificationPaneFocus) - } else { - v.app.UI.SetFocus(LeftPaneFocus) - } + v.app.UI.StatusView.giveBackFocus() } func (v *VoteOverlay) InputHandler(event *tcell.EventKey) { @@ -160,11 +156,7 @@ func (v *VoteOverlay) InputHandler(event *tcell.EventKey) { case ' ': v.ToggleSelect() case 'q', 'Q': - if v.app.UI.StatusView.lastList == NotificationPaneFocus { - v.app.UI.SetFocus(NotificationPaneFocus) - } else { - v.app.UI.SetFocus(LeftPaneFocus) - } + v.app.UI.StatusView.giveBackFocus() } } else { switch event.Key() { @@ -175,11 +167,7 @@ func (v *VoteOverlay) InputHandler(event *tcell.EventKey) { case tcell.KeyDown: v.Next() case tcell.KeyEsc: - if v.app.UI.StatusView.lastList == NotificationPaneFocus { - v.app.UI.SetFocus(NotificationPaneFocus) - } else { - v.app.UI.SetFocus(LeftPaneFocus) - } + v.app.UI.StatusView.giveBackFocus() } } }