diff --git a/README.md b/README.md index 4e34144..8496e08 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ features you can find in the web client. Press `C` to create a new toot and `N` to focus on your notifications. +You can also enable [mouse support](#mouse-support). + You can find Linux binaries under [releases](https://github.com/RasmusLindroth/tut/releases). ![Preview](./images/preview.png "Preview") @@ -28,6 +30,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * [Build it yourself](#build-it-yourself) * [Flags and commands](#flags-and-commands) * [Templates](#templates) +* [Mouse support](#mouse-support) * [Password manager for secrets](#password-manager-for-secrets) * [Thanks to](#thanks-to) @@ -38,6 +41,7 @@ You can find Linux binaries under [releases](https://github.com/RasmusLindroth/t * `:blocking` lists users that you have blocked * `:boosts` lists users that boosted the toot * `:bookmarks` lists all your bookmarks +* `:clear-notifications` clear all notifications * `:compose` compose a new toot * `:favorited` lists toots you've favorited * `:favorites` lists users that favorited the toot @@ -191,6 +195,10 @@ The data available in `user.tmpl` is almost the same. You still have the `Style` but instead of `Toot` you have a struct named `User`. You can see all fields in [./ui/item_user.go](./ui/item_user.go). +## Mouse support +To enable mouse support you'll have to set `mouse-support=true` under `[general]` +in your [config](#configuration). + ## Password manager for secrets If you run `pass`, `gopass` or something similar you can protect your secrets. You'll have to manually update your `accounts.toml`. It should be located at diff --git a/api/notifications.go b/api/notifications.go new file mode 100644 index 0000000..deb665b --- /dev/null +++ b/api/notifications.go @@ -0,0 +1,9 @@ +package api + +import ( + "context" +) + +func (ac *AccountClient) ClearNotifications() error { + return ac.Client.ClearNotifications(context.Background()) +} diff --git a/auth/file.go b/auth/file.go index f526795..dae60be 100644 --- a/auth/file.go +++ b/auth/file.go @@ -1,7 +1,7 @@ package auth import ( - "io/ioutil" + "io" "log" "os" "strings" @@ -27,7 +27,7 @@ func GetAccounts(filepath string) (*AccountData, error) { return &AccountData{}, err } defer f.Close() - data, err := ioutil.ReadAll(f) + data, err := io.ReadAll(f) if err != nil { return &AccountData{}, err } diff --git a/config.example.ini b/config.example.ini index 61481e0..260c1d8 100644 --- a/config.example.ini +++ b/config.example.ini @@ -149,10 +149,10 @@ leader-timeout=1000 # of two parts first the action then the shortcut. And they're separated by a # comma. # -# Available commands: home, direct, local, federated, compose, blocking, -# bookmarks, saved, favorited, boosts, favorites, following, followers, muting, -# newer, preferences, profile, notifications, lists, tag, window, -# list-placement, list-split, proportions +# Available commands: home, direct, local, federated, clear-notifications, +# compose, blocking, bookmarks, saved, favorited, boosts, favorites, following, +# followers, muting, newer, preferences, profile, notifications, lists, tag, +# window, list-placement, list-split, proportions # # The shortcuts are up to you, but keep them quite short and make sure they # don't collide. If you have one shortcut that is "f" and an other one that is diff --git a/config/config.go b/config/config.go index a46e162..7395677 100644 --- a/config/config.go +++ b/config/config.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -56,6 +55,7 @@ const ( LeaderDirect LeaderLocal LeaderFederated + LeaderClearNotifications LeaderCompose LeaderBlocking LeaderBookmarks @@ -849,6 +849,8 @@ func parseGeneral(cfg *ini.File) General { la.Command = LeaderLocal case "federated": la.Command = LeaderFederated + case "clear-notifications": + la.Command = LeaderClearNotifications case "compose": la.Command = LeaderCompose case "blocking": @@ -1481,7 +1483,7 @@ func getTheme(fname string, isLocal bool) (*ini.File, error) { if err != nil { return nil, err } - content, err := ioutil.ReadAll(f) + content, err := io.ReadAll(f) if err != nil { return nil, err } diff --git a/config/default_config.go b/config/default_config.go index 6cb16c8..3a5779f 100644 --- a/config/default_config.go +++ b/config/default_config.go @@ -151,10 +151,10 @@ leader-timeout=1000 # of two parts first the action then the shortcut. And they're separated by a # comma. # -# Available commands: home, direct, local, federated, compose, blocking, -# bookmarks, saved, favorited, boosts, favorites, following, followers, muting, -# newer, preferences, profile, notifications, lists, tag, window, -# list-placement, list-split, proportions +# Available commands: home, direct, local, federated, clear-notifications, +# compose, blocking, bookmarks, saved, favorited, boosts, favorites, following, +# followers, muting, newer, preferences, profile, notifications, lists, tag, +# window, list-placement, list-split, proportions # # The shortcuts are up to you, but keep them quite short and make sure they # don't collide. If you have one shortcut that is "f" and an other one that is diff --git a/config/help.tmpl b/config/help.tmpl index a87111f..a83efb2 100644 --- a/config/help.tmpl +++ b/config/help.tmpl @@ -45,6 +45,9 @@ Here's a list of supported commands. {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:bookmarks{{ Flags "-" }}{{ Color .Style.Text }} Lists all your bookmarks +{{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:clear-notifications{{ Flags "-" }}{{ Color .Style.Text }} + Clear all notifications + {{ Color .Style.TextSpecial2 }}{{ Flags "b" }}:compose{{ Flags "-" }}{{ Color .Style.Text }} Compose a new toot diff --git a/config/keys.go b/config/keys.go index 5f41e7c..860e6d2 100644 --- a/config/keys.go +++ b/config/keys.go @@ -6,18 +6,18 @@ import ( "github.com/gdamore/tcell/v2" ) -func ColorFromKey(c *Config, k Key, first bool) string { +func ColorFromKey(c *Config, k Key, first bool) (string, int) { if len(k.Hint) == 0 { - return "" + return "", 0 } parts := k.Hint[0] if !first && len(k.Hint) > 1 { parts = k.Hint[1] } if len(parts) != 3 { - return "" + return "", 0 } - return ColorKey(c, parts[0], parts[1], parts[2]) + return ColorKey(c, parts[0], parts[1], parts[2]), len(fmt.Sprintf("%s%s%s", parts[0], parts[1], parts[2])) } func ColorKey(c *Config, pre, key, end string) string { diff --git a/feed/feed.go b/feed/feed.go index 2528776..7702df1 100644 --- a/feed/feed.go +++ b/feed/feed.go @@ -103,6 +103,13 @@ func (f *Feed) Delete(id uint) { f.Updated(DekstopNotificationNone) } +func (f *Feed) Clear() { + f.itemsMux.Lock() + defer f.itemsMux.Unlock() + f.items = []api.Item{} + f.Updated(DekstopNotificationNone) +} + func (f *Feed) Item(index int) (api.Item, error) { f.itemsMux.RLock() defer f.itemsMux.RUnlock() diff --git a/go.mod b/go.mod index 10b9f9e..3c047f1 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,13 @@ require ( github.com/gdamore/tcell/v2 v2.5.2 github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc github.com/gobwas/glob v0.2.3 - github.com/icza/gox v0.0.0-20220321141217-e2d488ab2fbc + github.com/icza/gox v0.0.0-20220812133721-0fbf7a534d8e github.com/microcosm-cc/bluemonday v1.0.19 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/rivo/tview v0.0.0-20220801133142-711ef394f9b3 - github.com/rivo/uniseg v0.3.1 - golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 - gopkg.in/ini.v1 v1.66.6 + github.com/rivo/tview v0.0.0-20220812085834-0e6b21a48e96 + github.com/rivo/uniseg v0.3.4 + golang.org/x/net v0.0.0-20220812174116-3211cb980234 + gopkg.in/ini.v1 v1.67.0 ) require ( @@ -29,7 +29,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af // indirect github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect - golang.org/x/sys v0.0.0-20220731174439-a90be440212d // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index 557dce2..eabf057 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/icza/gox v0.0.0-20220321141217-e2d488ab2fbc h1:/vPVDa098f2SM/Ef3NtrWiyo4UBWL+QkD4hodlNpha8= -github.com/icza/gox v0.0.0-20220321141217-e2d488ab2fbc/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= +github.com/icza/gox v0.0.0-20220812133721-0fbf7a534d8e h1:vD4inAaWEbFk4Dpu1Y+m9URk8s6dOif7pBW5pW7fAak= +github.com/icza/gox v0.0.0-20220812133721-0fbf7a534d8e/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -36,11 +36,11 @@ github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/tview v0.0.0-20220801133142-711ef394f9b3 h1:gAT0XOEOwYJboaU9CIjJ/2v+/RX6ls7isQhXjl4WYhs= -github.com/rivo/tview v0.0.0-20220801133142-711ef394f9b3/go.mod h1:8NHTlQK5nUcLMAPupYd8thZnu/6jlEWYdJZNcaggXFw= +github.com/rivo/tview v0.0.0-20220812085834-0e6b21a48e96 h1:O435d1KIgG6KxpP7NDdmj7SdaLIzq4F+PG8ZB/BHC4c= +github.com/rivo/tview v0.0.0-20220812085834-0e6b21a48e96/go.mod h1:hyzpnqn4KWzZopTEjL1AxvlzOLMH1IuKo4lTw6vyOQc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= -github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw= +github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -48,13 +48,13 @@ github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af h1:6yITBqGTE2lEeTPG0 github.com/tadvi/systray v0.0.0-20190226123456-11a2b8fa57af/go.mod h1:4F09kP5F+am0jAwlQLddpoMDM+iewkxxt6nxUQ5nq5o= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y= github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= -golang.org/x/net v0.0.0-20220728211354-c7608f3a8462 h1:UreQrH7DbFXSi9ZFox6FNT3WBooWmdANpU+IfkT1T4I= -golang.org/x/net v0.0.0-20220728211354-c7608f3a8462/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d h1:Sv5ogFZatcgIMMtBSTTAgMYsicp25MXBubjXNDKwm80= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -63,7 +63,7 @@ golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index bc792a7..0a41ce4 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "github.com/rivo/tview" ) -const version = "1.0.16" +const version = "1.0.17" func main() { util.SetTerminalTitle("tut") diff --git a/ui/cmdbar.go b/ui/cmdbar.go index 9cb752e..288a47a 100644 --- a/ui/cmdbar.go +++ b/ui/cmdbar.go @@ -91,6 +91,9 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { case ":newer": c.tutView.LoadNewerCommand() c.Back() + case ":clear-notifications": + c.tutView.ClearNotificationsCommand() + c.Back() case ":list-placement": if len(parts) < 2 { break @@ -205,7 +208,7 @@ func (c *CmdBar) DoneFunc(key tcell.Key) { func (c *CmdBar) Autocomplete(curr string) []string { var entries []string - words := strings.Split(":blocking,:boosts,:bookmarks,:compose,:favorites,:favorited,:followers,:following,:help,:h,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:requests,:saved,:tag,:timeline,:tl,:user,:window,:quit,:q", ",") + words := strings.Split(":blocking,:boosts,:bookmarks,:clear-notifications,:compose,:favorites,:favorited,:followers,:following,:help,:h,:lists,:list-placement,:list-split,:muting,:newer,:preferences,:profile,:proportions,:requests,:saved,:tag,:timeline,:tl,:user,:window,:quit,:q", ",") if curr == "" { return entries } diff --git a/ui/commands.go b/ui/commands.go index 84bb285..8429869 100644 --- a/ui/commands.go +++ b/ui/commands.go @@ -7,6 +7,7 @@ import ( "github.com/RasmusLindroth/go-mastodon" "github.com/RasmusLindroth/tut/api" "github.com/RasmusLindroth/tut/config" + "github.com/RasmusLindroth/tut/feed" "github.com/RasmusLindroth/tut/util" ) @@ -199,3 +200,18 @@ func (tv *TutView) LoadNewerCommand() { f := tv.GetCurrentFeed() f.LoadNewer(true) } + +func (tv *TutView) ClearNotificationsCommand() { + err := tv.tut.Client.ClearNotifications() + if err != nil { + tv.ShowError(fmt.Sprintf("Couldn't clear notifications. Error: %v\n", err)) + return + } + for _, tl := range tv.Timeline.Feeds { + for _, f := range tl.Feeds { + if f.Data.Type() == feed.Notification { + f.Data.Clear() + } + } + } +} diff --git a/ui/composeview.go b/ui/composeview.go index 7b07d5c..d3cbea9 100644 --- a/ui/composeview.go +++ b/ui/composeview.go @@ -34,7 +34,7 @@ type ComposeView struct { content *tview.TextView input *MediaInput info *tview.TextView - controls *tview.TextView + controls *tview.Flex visibility *tview.DropDown media *MediaList msg *msgToot @@ -59,13 +59,12 @@ func NewComposeView(tv *TutView) *ComposeView { shared: tv.Shared, content: NewTextView(tv.tut.Config), input: NewMediaInput(tv), - controls: NewTextView(tv.tut.Config), + controls: NewControlView(tv.tut.Config), info: NewTextView(tv.tut.Config), visibility: NewDropDown(tv.tut.Config), media: NewMediaList(tv), } cv.content.SetDynamicColors(true) - cv.controls.SetDynamicColors(true) cv.View = newComposeUI(cv) return cv } @@ -109,27 +108,33 @@ func (cv *ComposeView) msgLength() int { } func (cv *ComposeView) SetControls(ctrl ComposeControls) { - var items []string + var items []Control switch ctrl { case ComposeNormal: - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposePost, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeEditText, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeVisibility, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeToggleContentWarning, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeEditSpoiler, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeMediaFocus, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposePoll, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposePost, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeEditText, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeVisibility, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeToggleContentWarning, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeEditSpoiler, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeMediaFocus, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposePoll, true)) if cv.msg.Status != nil { - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeIncludeQuote, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.ComposeIncludeQuote, true)) } case ComposeMedia: - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaAdd, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaDelete, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaEditDesc, true)) - items = append(items, config.ColorFromKey(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.GlobalBack, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaAdd, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaDelete, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.MediaEditDesc, true)) + items = append(items, NewControl(cv.tutView.tut.Config, cv.tutView.tut.Config.Input.GlobalBack, true)) + } + cv.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + cv.controls.AddItem(NewControlButton(cv.tutView, item), item.Len+1, 0, false) + } else { + cv.controls.AddItem(NewControlButton(cv.tutView, item), item.Len, 0, false) + } } - res := strings.Join(items, " ") - cv.controls.SetText(res) } func (cv *ComposeView) SetStatus(status *mastodon.Status) { @@ -374,12 +379,13 @@ func (cv *ComposeView) Post() { } type MediaList struct { - tutView *TutView - View *tview.Flex - heading *tview.TextView - text *tview.TextView - list *tview.List - Files []UploadFile + tutView *TutView + View *tview.Flex + heading *tview.TextView + text *tview.TextView + list *tview.List + Files []UploadFile + scrollSleep *scrollSleep } func NewMediaList(tv *TutView) *MediaList { @@ -389,6 +395,7 @@ func NewMediaList(tv *TutView) *MediaList { text: NewTextView(tv.tut.Config), list: NewList(tv.tut.Config), } + ml.scrollSleep = NewScrollSleep(ml.Next, ml.Prev) ml.heading.SetText(fmt.Sprintf("Media files: %d", ml.list.GetItemCount())) ml.heading.SetBorderPadding(1, 1, 0, 0) ml.View = tview.NewFlex().SetDirection(tview.FlexRow). diff --git a/ui/controls.go b/ui/controls.go new file mode 100644 index 0000000..e1e525b --- /dev/null +++ b/ui/controls.go @@ -0,0 +1,31 @@ +package ui + +import ( + "github.com/RasmusLindroth/tut/config" + "github.com/gdamore/tcell/v2" +) + +type Control struct { + key config.Key + Label string + Len int +} + +func NewControl(c *config.Config, k config.Key, first bool) Control { + label, length := config.ColorFromKey(c, k, first) + return Control{ + key: k, + Label: label, + Len: length, + } +} + +func (c Control) Click() *tcell.EventKey { + for _, k := range c.key.Keys { + return tcell.NewEventKey(k, 0, tcell.ModNone) + } + for _, r := range c.key.Runes { + return tcell.NewEventKey(tcell.KeyRune, r, tcell.ModNone) + } + return tcell.NewEventKey(tcell.KeyRune, 0, tcell.ModNone) +} diff --git a/ui/feed.go b/ui/feed.go index 3e1226d..3fc6738 100644 --- a/ui/feed.go +++ b/ui/feed.go @@ -82,7 +82,7 @@ func (f *Feed) DrawContent() { if id != item.ID() { continue } - DrawItem(f.tutView.tut, item, f.Content.Main, f.Content.Controls, f.Data.Type()) + DrawItem(f.tutView, item, f.Content.Main, f.Content.Controls, f.Data.Type()) f.tutView.ShouldSync() } } @@ -541,7 +541,7 @@ func (fl *FeedList) SetByID(id uint) { type FeedContent struct { Main *tview.TextView - Controls *tview.TextView + Controls *tview.Flex } func NewFeedContent(t *Tut) *FeedContent { @@ -558,8 +558,7 @@ func NewFeedContent(t *Tut) *FeedContent { return x, y, rWidth, height }) } - c := NewTextView(t.Config) - c.SetDynamicColors(true) + c := NewControlView(t.Config) fc := &FeedContent{ Main: m, Controls: c, diff --git a/ui/helpview.go b/ui/helpview.go index d7882c6..6f6f5fa 100644 --- a/ui/helpview.go +++ b/ui/helpview.go @@ -2,7 +2,6 @@ package ui import ( "bytes" - "strings" "github.com/RasmusLindroth/tut/config" "github.com/rivo/tview" @@ -13,7 +12,7 @@ type HelpView struct { shared *Shared View *tview.Flex content *tview.TextView - controls *tview.TextView + controls *tview.Flex } type HelpData struct { @@ -22,7 +21,7 @@ type HelpData struct { func NewHelpView(tv *TutView) *HelpView { content := NewTextView(tv.tut.Config) - controls := NewTextView(tv.tut.Config) + controls := NewControlView(tv.tut.Config) hv := &HelpView{ tutView: tv, shared: tv.Shared, @@ -36,11 +35,16 @@ func NewHelpView(tv *TutView) *HelpView { panic(err) } hv.content.SetText(output.String()) - var items []string - items = append(items, config.ColorFromKey(tv.tut.Config, tv.tut.Config.Input.GlobalBack, true)) - items = append(items, config.ColorFromKey(tv.tut.Config, tv.tut.Config.Input.GlobalExit, true)) - res := strings.Join(items, " ") - hv.controls.SetText(res) + var items []Control + items = append(items, NewControl(tv.tut.Config, tv.tut.Config.Input.GlobalBack, true)) + items = append(items, NewControl(tv.tut.Config, tv.tut.Config.Input.GlobalExit, true)) + for i, item := range items { + if i < len(items)-1 { + hv.controls.AddItem(NewControlButton(hv.tutView, item), item.Len+1, 0, false) + } else { + hv.controls.AddItem(NewControlButton(hv.tutView, item), item.Len, 0, false) + } + } hv.View = newHelpViewUI(hv) return hv } diff --git a/ui/input.go b/ui/input.go index bac2352..b87803e 100644 --- a/ui/input.go +++ b/ui/input.go @@ -4,6 +4,8 @@ import ( "fmt" "strconv" "strings" + "sync" + "time" "github.com/RasmusLindroth/go-mastodon" "github.com/RasmusLindroth/tut/api" @@ -113,6 +115,8 @@ func (tv *TutView) InputLeaderKey(event *tcell.EventKey) *tcell.EventKey { tv.LocalCommand() case config.LeaderFederated: tv.FederatedCommand() + case config.LeaderClearNotifications: + tv.ClearNotificationsCommand() case config.LeaderCompose: tv.ComposeCommand() case config.LeaderBlocking: @@ -896,136 +900,294 @@ func (tv *TutView) InputCmdView(event *tcell.EventKey) *tcell.EventKey { } func (tv *TutView) MouseInput(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { - if event != nil { - tv.mouseX, tv.mouseY = event.Position() - tv.mouseEvent = event + if event == nil { return nil, action } - if tv.PageFocus == MainFocus || tv.PageFocus == ViewFocus { - if action == tview.MouseLeftClick { - f := tv.GetCurrentFeed() - if f.Content.Main.InRect(tv.mouseX, tv.mouseY) { - tv.SetPage(ViewFocus) - return nil, action - } - for i, tl := range tv.Timeline.Feeds { - fl := tl.GetFeedList() - if fl.Text.InRect(tv.mouseX, tv.mouseY) { - tv.feedListMouse(fl.Text, i, action) - return nil, action - } - if fl.Symbol.InRect(tv.mouseX, tv.mouseY) { - tv.feedListMouse(fl.Symbol, i, action) - return nil, action - } - } - } + switch action { + case tview.MouseLeftUp, tview.MouseMiddleUp, tview.MouseRightUp: + return event, action } - if tv.PageFocus == LoginFocus { - if action == tview.MouseLeftClick { - list := tv.LoginView.list - if !list.InRect(tv.mouseX, tv.mouseY) { + + switch tv.PageFocus { + case ViewFocus, MainFocus: + return tv.MouseInputMainView(event, action) + case LoginFocus: + tv.MouseInputLoginView(event, action) + case LinkFocus: + return tv.MouseInputLinkView(event, action) + case MediaFocus: + return tv.MouseInputMediaView(event, action) + case VoteFocus: + return tv.MouseInputVoteView(event, action) + case ModalFocus: + tv.MouseInputModalView(event, action) + case PollFocus: + return tv.MouseInputPollView(event, action) + case HelpFocus: + return tv.MouseInputHelpView(event, action) + case ComposeFocus: + return tv.MouseInputComposeView(event, action) + case PreferenceFocus: + return tv.MouseInputPreferenceView(event, action) + } + + return nil, action +} + +func (tv *TutView) feedListMouse(list *tview.List, i int, event *tcell.EventMouse, action tview.MouseAction) { + tv.SetPage(MainFocus) + tv.FocusFeed(i) + mh := list.MouseHandler() + if mh == nil { + return + } + lastIndex := list.GetCurrentItem() + mh(action, event, func(p tview.Primitive) {}) + newIndex := list.GetCurrentItem() + if lastIndex != newIndex { + tv.Timeline.SetItemFeedIndex(newIndex) + } +} + +var scrollSleepTime time.Duration = 150 + +type scrollSleep struct { + mux sync.Mutex + last time.Time + next func() + prev func() +} + +func NewScrollSleep(next func(), prev func()) *scrollSleep { + return &scrollSleep{ + next: next, + prev: prev, + } +} + +func (sc *scrollSleep) Action(list *tview.List, action tview.MouseAction) { + mh := list.MouseHandler() + if mh == nil { + return + } + lock := sc.mux.TryLock() + if !lock { + return + } + if time.Since(sc.last) < (scrollSleepTime * time.Millisecond) { + sc.mux.Unlock() + return + } + if action == tview.MouseScrollDown { + sc.next() + } + if action == tview.MouseScrollUp { + sc.prev() + } + sc.last = time.Now() + sc.mux.Unlock() +} + +func (tv *TutView) MouseInputMainView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseScrollDown, tview.MouseScrollUp: + f := tv.GetCurrentFeed() + if f.Content.Main.InRect(x, y) { + if action == tview.MouseScrollDown { + tv.Timeline.ScrollDown() return nil, action } - mh := list.MouseHandler() - if mh == nil { + if action == tview.MouseScrollUp { + tv.Timeline.ScrollUp() return nil, action } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) - tv.LoginView.Selected() } - } - if tv.PageFocus == LinkFocus { - if action == tview.MouseLeftClick { - list := tv.LinkView.list - if !list.InRect(tv.mouseX, tv.mouseY) { + for _, tl := range tv.Timeline.Feeds { + fl := tl.GetFeedList() + if fl.Text.InRect(x, y) { + tv.Timeline.scrollSleep.Action(fl.Text, action) return nil, action } - mh := list.MouseHandler() - if mh == nil { + if fl.Symbol.InRect(x, y) { + tv.Timeline.scrollSleep.Action(fl.Symbol, action) return nil, action } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) - tv.LinkView.Open() } - } - if tv.PageFocus == MediaFocus { - if action == tview.MouseLeftClick { - list := tv.ComposeView.media.list - if !list.InRect(tv.mouseX, tv.mouseY) { + case tview.MouseLeftClick: + f := tv.GetCurrentFeed() + if f.Content.Main.InRect(x, y) { + tv.SetPage(ViewFocus) + return nil, action + } + if f.Content.Controls.InRect(x, y) { + return event, action + } + for i, tl := range tv.Timeline.Feeds { + fl := tl.GetFeedList() + if fl.Text.InRect(x, y) { + tv.feedListMouse(fl.Text, i, event, action) return nil, action } - mh := list.MouseHandler() - if mh == nil { + if fl.Symbol.InRect(x, y) { + tv.feedListMouse(fl.Symbol, i, event, action) return nil, action } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) } - if tv.PageFocus == MediaFocus { - if action == tview.MouseLeftClick { - list := tv.ComposeView.media.list - if !list.InRect(tv.mouseX, tv.mouseY) { - return nil, action - } - mh := list.MouseHandler() - if mh == nil { - return nil, action - } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) - } + } + return nil, action +} + +func (tv *TutView) MouseInputLoginView(event *tcell.EventMouse, action tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + list := tv.LoginView.list + if !list.InRect(x, y) { + return } + mh := list.MouseHandler() + if mh == nil { + return + } + mh(action, event, func(p tview.Primitive) {}) + tv.LoginView.Selected() + case tview.MouseScrollDown, tview.MouseScrollUp: + tv.LoginView.scrollSleep.Action(tv.LoginView.list, action) } - if tv.PageFocus == VoteFocus { - if action == tview.MouseLeftClick { - list := tv.VoteView.list - if !list.InRect(tv.mouseX, tv.mouseY) { - return nil, action - } - mh := list.MouseHandler() - if mh == nil { - return nil, action - } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) - tv.VoteView.ToggleSelect() +} + +func (tv *TutView) MouseInputLinkView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.LinkView.controls.InRect(x, y) { + return event, action + } + list := tv.LinkView.list + if !list.InRect(x, y) { + return nil, action + } + mh := list.MouseHandler() + if mh == nil { + return nil, action } + mh(action, event, func(p tview.Primitive) {}) + tv.LinkView.Open() + case tview.MouseScrollDown, tview.MouseScrollUp: + tv.LinkView.scrollSleep.Action(tv.LinkView.list, action) } - if tv.PageFocus == ModalFocus { - if action == tview.MouseLeftClick { - modal := tv.ModalView.View - mh := modal.MouseHandler() - if mh == nil { - return nil, action - } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) + return nil, action +} + +func (tv *TutView) MouseInputMediaView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.ComposeView.controls.InRect(x, y) { + return event, action + } + list := tv.ComposeView.media.list + if !list.InRect(x, y) { + return nil, action + } + mh := list.MouseHandler() + if mh == nil { + return nil, action } + mh(action, event, func(p tview.Primitive) {}) + case tview.MouseScrollDown, tview.MouseScrollUp: + tv.ComposeView.media.scrollSleep.Action(tv.ComposeView.media.list, action) } - if tv.PageFocus == PollFocus { - if action == tview.MouseLeftClick { - list := tv.PollView.list - if !list.InRect(tv.mouseX, tv.mouseY) { - return nil, action - } - mh := list.MouseHandler() - if mh == nil { - return nil, action - } - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) + return nil, action +} + +func (tv *TutView) MouseInputVoteView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.VoteView.controls.InRect(x, y) { + return event, action + } + list := tv.VoteView.list + if !list.InRect(x, y) { + return nil, action } + mh := list.MouseHandler() + if mh == nil { + return nil, action + } + mh(action, event, func(p tview.Primitive) {}) + tv.VoteView.ToggleSelect() + case tview.MouseScrollDown, tview.MouseScrollUp: + tv.VoteView.scrollSleep.Action(tv.VoteView.list, action) } return nil, action } -func (tv *TutView) feedListMouse(list *tview.List, i int, action tview.MouseAction) { - tv.SetPage(MainFocus) - tv.FocusFeed(i) - mh := list.MouseHandler() - if mh == nil { - return +func (tv *TutView) MouseInputModalView(event *tcell.EventMouse, action tview.MouseAction) { + switch action { + case tview.MouseLeftClick: + modal := tv.ModalView.View + mh := modal.MouseHandler() + if mh == nil { + return + } + mh(action, event, func(p tview.Primitive) {}) } - lastIndex := list.GetCurrentItem() - mh(action, tv.mouseEvent, func(p tview.Primitive) {}) - newIndex := list.GetCurrentItem() - if lastIndex != newIndex { - tv.Timeline.SetItemFeedIndex(newIndex) +} + +func (tv *TutView) MouseInputPollView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.PollView.controls.InRect(x, y) { + return event, action + } + list := tv.PollView.list + if !list.InRect(x, y) { + return nil, action + } + mh := list.MouseHandler() + if mh == nil { + return nil, action + } + mh(action, event, func(p tview.Primitive) {}) + case tview.MouseScrollDown, tview.MouseScrollUp: + tv.PollView.scrollSleep.Action(tv.PollView.list, action) + } + return nil, action +} + +func (tv *TutView) MouseInputHelpView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.HelpView.controls.InRect(x, y) { + return event, action + } } + return nil, action +} + +func (tv *TutView) MouseInputPreferenceView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.PreferenceView.controls.InRect(x, y) { + return event, action + } + } + return nil, action +} + +func (tv *TutView) MouseInputComposeView(event *tcell.EventMouse, action tview.MouseAction) (*tcell.EventMouse, tview.MouseAction) { + x, y := event.Position() + switch action { + case tview.MouseLeftClick: + if tv.ComposeView.controls.InRect(x, y) { + return event, action + } + } + return nil, action } diff --git a/ui/item.go b/ui/item.go index 38f5156..399203e 100644 --- a/ui/item.go +++ b/ui/item.go @@ -66,35 +66,35 @@ func DrawListItem(cfg *config.Config, item api.Item) (string, string) { } } -func DrawItem(tut *Tut, item api.Item, main *tview.TextView, controls *tview.TextView, ft feed.FeedType) { +func DrawItem(tv *TutView, item api.Item, main *tview.TextView, controls *tview.Flex, ft feed.FeedType) { switch item.Type() { case api.StatusType: - drawStatus(tut, item, item.Raw().(*mastodon.Status), main, controls, "") + drawStatus(tv, item, item.Raw().(*mastodon.Status), main, controls, "") case api.UserType, api.ProfileType: if ft == feed.FollowRequests { - drawUser(tut, item.Raw().(*api.User), main, controls, "", true) + drawUser(tv, item.Raw().(*api.User), main, controls, "", true) } else { - drawUser(tut, item.Raw().(*api.User), main, controls, "", false) + drawUser(tv, item.Raw().(*api.User), main, controls, "", false) } case api.NotificationType: - drawNotification(tut, item, item.Raw().(*api.NotificationData), main, controls) + drawNotification(tv, item, item.Raw().(*api.NotificationData), main, controls) case api.ListsType: - drawList(tut, item.Raw().(*mastodon.List), main, controls) + drawList(tv, item.Raw().(*mastodon.List), main, controls) } } -func DrawItemControls(tut *Tut, item api.Item, controls *tview.TextView, ft feed.FeedType) { +func DrawItemControls(tv *TutView, item api.Item, controls *tview.Flex, ft feed.FeedType) { switch item.Type() { case api.StatusType: - drawStatus(tut, item, item.Raw().(*mastodon.Status), nil, controls, "") + drawStatus(tv, item, item.Raw().(*mastodon.Status), nil, controls, "") case api.UserType, api.ProfileType: if ft == feed.FollowRequests { - drawUser(tut, item.Raw().(*api.User), nil, controls, "", true) + drawUser(tv, item.Raw().(*api.User), nil, controls, "", true) } else { - drawUser(tut, item.Raw().(*api.User), nil, controls, "", false) + drawUser(tv, item.Raw().(*api.User), nil, controls, "", false) } case api.NotificationType: - drawNotification(tut, item, item.Raw().(*api.NotificationData), nil, controls) + drawNotification(tv, item, item.Raw().(*api.NotificationData), nil, controls) } } diff --git a/ui/item_list.go b/ui/item_list.go index 9e105a2..e5102ef 100644 --- a/ui/item_list.go +++ b/ui/item_list.go @@ -4,17 +4,15 @@ import ( "fmt" "github.com/RasmusLindroth/go-mastodon" - "github.com/RasmusLindroth/tut/config" "github.com/rivo/tview" ) type List struct { } -func drawList(tut *Tut, data *mastodon.List, main *tview.TextView, controls *tview.TextView) { - - controlItem := config.ColorFromKey(tut.Config, tut.Config.Input.ListOpenFeed, true) +func drawList(tv *TutView, data *mastodon.List, main *tview.TextView, controls *tview.Flex) { + btn := NewControl(tv.tut.Config, tv.tut.Config.Input.ListOpenFeed, true) + controls.AddItem(NewControlButton(tv, btn), btn.Len, 0, false) main.SetText(fmt.Sprintf("List %s", tview.Escape(data.Title))) - controls.SetText(controlItem) } diff --git a/ui/item_notification.go b/ui/item_notification.go index ebacc67..086b07d 100644 --- a/ui/item_notification.go +++ b/ui/item_notification.go @@ -8,34 +8,34 @@ import ( "github.com/rivo/tview" ) -func drawNotification(tut *Tut, item api.Item, notification *api.NotificationData, main *tview.TextView, controls *tview.TextView) { +func drawNotification(tv *TutView, item api.Item, notification *api.NotificationData, main *tview.TextView, controls *tview.Flex) { switch notification.Item.Type { case "follow": - drawUser(tut, notification.User.Raw().(*api.User), main, controls, + drawUser(tv, notification.User.Raw().(*api.User), main, controls, fmt.Sprintf("%s started following you", util.FormatUsername(notification.Item.Account)), false, ) case "favourite": - drawStatus(tut, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, fmt.Sprintf("%s favorited your toot", util.FormatUsername(notification.Item.Account)), ) case "reblog": - drawStatus(tut, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, fmt.Sprintf("%s boosted your toot", util.FormatUsername(notification.Item.Account)), ) case "mention": - drawStatus(tut, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, fmt.Sprintf("%s mentioned you", util.FormatUsername(notification.Item.Account)), ) case "status": - drawStatus(tut, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, fmt.Sprintf("%s posted a new toot", util.FormatUsername(notification.Item.Account)), ) case "poll": - drawStatus(tut, notification.Status, notification.Item.Status, main, controls, + drawStatus(tv, notification.Status, notification.Item.Status, main, controls, "A poll of yours or one you participated in has ended", ) case "follow_request": - drawUser(tut, notification.User.Raw().(*api.User), main, controls, + drawUser(tv, notification.User.Raw().(*api.User), main, controls, fmt.Sprintf("%s wants to follow you.", util.FormatUsername(notification.Item.Account)), true, ) diff --git a/ui/item_status.go b/ui/item_status.go index b1a752d..bd367d0 100644 --- a/ui/item_status.go +++ b/ui/item_status.go @@ -70,22 +70,21 @@ type DisplayTootData struct { Style config.Style } -func drawStatus(tut *Tut, item api.Item, status *mastodon.Status, main *tview.TextView, controls *tview.TextView, additional string) { +func drawStatus(tv *TutView, item api.Item, status *mastodon.Status, main *tview.TextView, controls *tview.Flex, additional string) { filtered, phrase := item.Filtered() if filtered { var output string - if tut.Config.General.ShowFilterPhrase { + if tv.tut.Config.General.ShowFilterPhrase { output = fmt.Sprintf("Filtered by phrase: %s", tview.Escape(phrase)) } else { output = "Filtered." } if main != nil { if additional != "" { - additional = fmt.Sprintf("%s\n\n", config.SublteText(tut.Config, additional)) + additional = fmt.Sprintf("%s\n\n", config.SublteText(tv.tut.Config, additional)) } main.SetText(additional + output) } - controls.SetText("") return } @@ -188,57 +187,63 @@ func drawStatus(tut *Tut, item api.Item, status *mastodon.Status, main *tview.Te main.ScrollToBeginning() } - var info []string + var info []Control if status.Favourited { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusFavorite, false)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusFavorite, false)) } else { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusFavorite, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusFavorite, true)) } if status.Reblogged { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusBoost, false)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBoost, false)) } else { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusBoost, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBoost, true)) } - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusThread, true)) - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusReply, true)) - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusViewFocus, true)) - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusUser, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusThread, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusReply, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusViewFocus, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusUser, true)) if len(status.MediaAttachments) > 0 { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusMedia, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusMedia, true)) } _, _, _, length := item.URLs() if length > 0 { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusLinks, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusLinks, true)) } - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusAvatar, true)) - if status.Account.ID == tut.Client.Me.ID { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusDelete, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusAvatar, true)) + if status.Account.ID == tv.tut.Client.Me.ID { + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusDelete, true)) } if !status.Bookmarked { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusBookmark, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBookmark, true)) } else { - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusBookmark, false)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusBookmark, false)) } - info = append(info, config.ColorFromKey(tut.Config, tut.Config.Input.StatusYank, true)) + info = append(info, NewControl(tv.tut.Config, tv.tut.Config.Input.StatusYank, true)) - controlsS := strings.Join(info, " ") + controls.Clear() + for i, item := range info { + if i < len(info)-1 { + controls.AddItem(NewControlButton(tv, item), item.Len+1, 0, false) + } else { + controls.AddItem(NewControlButton(tv, item), item.Len, 0, false) + } + } td := DisplayTootData{ Toot: toot, - Style: tut.Config.Style, + Style: tv.tut.Config.Style, } var output bytes.Buffer - err := tut.Config.Templates.Toot.ExecuteTemplate(&output, "toot.tmpl", td) + err := tv.tut.Config.Templates.Toot.ExecuteTemplate(&output, "toot.tmpl", td) if err != nil { panic(err) } if main != nil { if additional != "" { - additional = fmt.Sprintf("%s\n\n", config.SublteText(tut.Config, additional)) + additional = fmt.Sprintf("%s\n\n", config.SublteText(tv.tut.Config, additional)) } main.SetText(additional + output.String()) } - controls.SetText(controlsS) } diff --git a/ui/item_user.go b/ui/item_user.go index 5a405d7..c68575c 100644 --- a/ui/item_user.go +++ b/ui/item_user.go @@ -3,7 +3,6 @@ package ui import ( "bytes" "fmt" - "strings" "time" "github.com/RasmusLindroth/tut/api" @@ -44,7 +43,7 @@ type DisplayUserData struct { Style config.Style } -func drawUser(tut *Tut, data *api.User, main *tview.TextView, controls *tview.TextView, additional string, fr bool) { +func drawUser(tv *TutView, data *api.User, main *tview.TextView, controls *tview.Flex, additional string, fr bool) { user := data.Data relation := data.Relation showUserControl := true @@ -65,8 +64,6 @@ func drawUser(tut *Tut, data *api.User, main *tview.TextView, controls *tview.Te Bot: user.Bot, } - var controlsS string - var urls []util.URL fields := []Field{} u.Note, urls = util.CleanHTML(user.Note) @@ -81,52 +78,59 @@ func drawUser(tut *Tut, data *api.User, main *tview.TextView, controls *tview.Te } u.Fields = fields - var controlItems []string + var controlItems []Control if fr { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserFollowRequestDecide, false)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserFollowRequestDecide, false)) } - if tut.Client.Me.ID != user.ID { + if tv.tut.Client.Me.ID != user.ID { if relation.Following { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserFollow, false)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserFollow, false)) } else { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserFollow, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserFollow, true)) } if relation.Blocking { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserBlock, false)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserBlock, false)) } else { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserBlock, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserBlock, true)) } if relation.Muting { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserMute, false)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserMute, false)) } else { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserMute, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserMute, true)) } if len(urls) > 0 { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserLinks, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserLinks, true)) } } if showUserControl { - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserUser, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserUser, true)) + } + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserAvatar, true)) + controlItems = append(controlItems, NewControl(tv.tut.Config, tv.tut.Config.Input.UserYank, true)) + + controls.Clear() + for i, item := range controlItems { + if i < len(controlItems)-1 { + controls.AddItem(NewControlButton(tv, item), item.Len+1, 0, false) + } else { + controls.AddItem(NewControlButton(tv, item), item.Len, 0, false) + } } - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserAvatar, true)) - controlItems = append(controlItems, config.ColorFromKey(tut.Config, tut.Config.Input.UserYank, true)) - controlsS = strings.Join(controlItems, " ") ud := DisplayUserData{ User: u, - Style: tut.Config.Style, + Style: tv.tut.Config.Style, } var output bytes.Buffer - err := tut.Config.Templates.User.ExecuteTemplate(&output, "user.tmpl", ud) + err := tv.tut.Config.Templates.User.ExecuteTemplate(&output, "user.tmpl", ud) if err != nil { panic(err) } if main != nil { if additional != "" { - additional = fmt.Sprintf("%s\n\n", config.SublteText(tut.Config, additional)) + additional = fmt.Sprintf("%s\n\n", config.SublteText(tv.tut.Config, additional)) } main.SetText(additional + output.String()) } - controls.SetText(controlsS) } diff --git a/ui/linkview.go b/ui/linkview.go index 36e4cb9..62c2b7b 100644 --- a/ui/linkview.go +++ b/ui/linkview.go @@ -2,44 +2,54 @@ package ui import ( "fmt" - "strings" "github.com/RasmusLindroth/tut/config" "github.com/rivo/tview" ) type LinkView struct { - tutView *TutView - shared *Shared - View *tview.Flex - list *tview.List - controls *tview.TextView + tutView *TutView + shared *Shared + View *tview.Flex + list *tview.List + controls *tview.Flex + scrollSleep *scrollSleep } func NewLinkView(tv *TutView) *LinkView { l := NewList(tv.tut.Config) - txt := NewTextView(tv.tut.Config) + c := NewControlView(tv.tut.Config) lv := &LinkView{ tutView: tv, shared: tv.Shared, list: l, - controls: txt, + controls: c, } + lv.scrollSleep = NewScrollSleep(lv.Next, lv.Prev) lv.View = linkViewUI(lv) return lv } func linkViewUI(lv *LinkView) *tview.Flex { lv.controls.SetBorderPadding(0, 0, 1, 1) - items := []string{ - config.ColorFromKey(lv.tutView.tut.Config, lv.tutView.tut.Config.Input.LinkOpen, true), - config.ColorFromKey(lv.tutView.tut.Config, lv.tutView.tut.Config.Input.LinkYank, true), + items := []Control{ + NewControl(lv.tutView.tut.Config, lv.tutView.tut.Config.Input.LinkOpen, true), + NewControl(lv.tutView.tut.Config, lv.tutView.tut.Config.Input.LinkYank, true), } for _, cust := range lv.tutView.tut.Config.OpenCustom.OpenCustoms { - items = append(items, config.ColorKey(lv.tutView.tut.Config, "", fmt.Sprintf("%d", cust.Index), cust.Name)) + key := config.Key{ + Hint: [][]string{{"", fmt.Sprintf("%d", cust.Index), cust.Name}}, + } + items = append(items, NewControl(lv.tutView.tut.Config, key, true)) + } + lv.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + lv.controls.AddItem(NewControlButton(lv.tutView, item), item.Len+1, 0, false) + } else { + lv.controls.AddItem(NewControlButton(lv.tutView, item), item.Len, 0, false) + } } - res := strings.Join(items, " ") - lv.controls.SetText(res) r := tview.NewFlex().SetDirection(tview.FlexRow) if lv.tutView.tut.Config.General.TerminalTitle < 2 { diff --git a/ui/loginview.go b/ui/loginview.go index b3411d9..ef042a7 100644 --- a/ui/loginview.go +++ b/ui/loginview.go @@ -8,10 +8,11 @@ import ( ) type LoginView struct { - tutView *TutView - accounts *auth.AccountData - View tview.Primitive - list *tview.List + tutView *TutView + accounts *auth.AccountData + View tview.Primitive + list *tview.List + scrollSleep *scrollSleep } func NewLoginView(tv *TutView, accs *auth.AccountData) *LoginView { @@ -28,12 +29,14 @@ func NewLoginView(tv *TutView, accs *auth.AccountData) *LoginView { v.AddItem(list, 0, 1, false). AddItem(tv.Shared.Bottom.View, 2, 0, false) - return &LoginView{ + lv := &LoginView{ tutView: tv, accounts: accs, View: v, list: list, } + lv.scrollSleep = NewScrollSleep(lv.Next, lv.Prev) + return lv } func (l *LoginView) Selected() { diff --git a/ui/media.go b/ui/media.go index aa4cb70..d6cea8a 100644 --- a/ui/media.go +++ b/ui/media.go @@ -3,8 +3,8 @@ package ui import ( "fmt" "io" - "io/ioutil" "net/http" + "os" "os/exec" "github.com/RasmusLindroth/go-mastodon" @@ -12,7 +12,7 @@ import ( ) func downloadFile(url string) (string, error) { - f, err := ioutil.TempFile("", "tutfile") + f, err := os.CreateTemp("", "tutfile") if err != nil { return "", err } diff --git a/ui/pollview.go b/ui/pollview.go index e48687d..03cfe25 100644 --- a/ui/pollview.go +++ b/ui/pollview.go @@ -2,10 +2,8 @@ package ui import ( "fmt" - "strings" "github.com/RasmusLindroth/go-mastodon" - "github.com/RasmusLindroth/tut/config" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -30,14 +28,15 @@ var durationsTime = map[string]int64{ } type PollView struct { - tutView *TutView - shared *Shared - View *tview.Flex - info *tview.TextView - expiration *tview.DropDown - controls *tview.TextView - list *tview.List - poll *mastodon.TootPoll + tutView *TutView + shared *Shared + View *tview.Flex + info *tview.TextView + expiration *tview.DropDown + controls *tview.Flex + list *tview.List + poll *mastodon.TootPoll + scrollSleep *scrollSleep } func NewPollView(tv *TutView) *PollView { @@ -46,9 +45,10 @@ func NewPollView(tv *TutView) *PollView { shared: tv.Shared, info: NewTextView(tv.tut.Config), expiration: NewDropDown(tv.tut.Config), - controls: NewTextView(tv.tut.Config), + controls: NewControlView(tv.tut.Config), list: NewList(tv.tut.Config), } + p.scrollSleep = NewScrollSleep(p.Next, p.Prev) p.Reset() p.View = pollViewUI(p) @@ -56,14 +56,20 @@ func NewPollView(tv *TutView) *PollView { } func pollViewUI(p *PollView) *tview.Flex { - var items []string - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollAdd, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollEdit, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollDelete, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollMultiToggle, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollExpiration, true)) - p.controls.SetText(strings.Join(items, " ")) - + var items []Control + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollAdd, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollEdit, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollDelete, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollMultiToggle, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PollExpiration, true)) + p.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len+1, 0, false) + } else { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len, 0, false) + } + } p.expiration.SetLabel("Expiration: ") p.expiration.SetOptions(durations, p.expirationSelected) p.expiration.SetCurrentOption(4) diff --git a/ui/preferenceview.go b/ui/preferenceview.go index c4bc76c..25b6339 100644 --- a/ui/preferenceview.go +++ b/ui/preferenceview.go @@ -2,10 +2,8 @@ package ui import ( "fmt" - "strings" "github.com/RasmusLindroth/go-mastodon" - "github.com/RasmusLindroth/tut/config" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -31,7 +29,7 @@ type PreferenceView struct { bio *tview.TextView fields *tview.List visibility *tview.DropDown - controls *tview.TextView + controls *tview.Flex preferences *preferences fieldFocus bool } @@ -44,7 +42,7 @@ func NewPreferenceView(tv *TutView) *PreferenceView { bio: NewTextView(tv.tut.Config), fields: NewList(tv.tut.Config), visibility: NewDropDown(tv.tut.Config), - controls: NewTextView(tv.tut.Config), + controls: NewControlView(tv.tut.Config), preferences: &preferences{}, } p.View = preferenceViewUI(p) @@ -125,13 +123,19 @@ func (p *PreferenceView) HasFieldFocus() bool { func (p *PreferenceView) FieldFocus() { p.fieldFocus = true - var items []string - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsAdd, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsEdit, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsDelete, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.GlobalBack, true)) - p.controls.SetText(strings.Join(items, " ")) - + var items []Control + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsAdd, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsEdit, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFieldsDelete, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.GlobalBack, true)) + p.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len+1, 0, false) + } else { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len, 0, false) + } + } cnf := p.tutView.tut.Config p.fields.SetSelectedBackgroundColor(cnf.Style.ListSelectedBackground) p.fields.SetSelectedTextColor(cnf.Style.ListSelectedText) @@ -140,13 +144,20 @@ func (p *PreferenceView) FieldFocus() { func (p *PreferenceView) MainFocus() { p.fieldFocus = false - var items []string - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceName, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceVisibility, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceBio, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFields, true)) - items = append(items, config.ColorFromKey(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceSave, true)) - p.controls.SetText(strings.Join(items, " ")) + var items []Control + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceName, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceVisibility, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceBio, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceFields, true)) + items = append(items, NewControl(p.tutView.tut.Config, p.tutView.tut.Config.Input.PreferenceSave, true)) + p.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len+1, 0, false) + } else { + p.controls.AddItem(NewControlButton(p.tutView, item), item.Len, 0, false) + } + } cnf := p.tutView.tut.Config p.fields.SetSelectedBackgroundColor(cnf.Style.Background) diff --git a/ui/styled_elements.go b/ui/styled_elements.go index b583357..684104e 100644 --- a/ui/styled_elements.go +++ b/ui/styled_elements.go @@ -26,6 +26,28 @@ func NewTextView(cnf *config.Config) *tview.TextView { return tw } +func NewControlView(cnf *config.Config) *tview.Flex { + f := tview.NewFlex().SetDirection(tview.FlexColumn) + f.SetBackgroundColor(cnf.Style.Background) + return f +} + +func NewControlButton(tv *TutView, control Control) *tview.Button { + btn := tview.NewButton(control.Label) + btn.SetBackgroundColor(tv.tut.Config.Style.Background) + btn.SetMouseCapture(func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) { + if !btn.InRect(event.Position()) { + return action, event + } + if action != tview.MouseLeftClick { + return action, event + } + tv.tut.App.QueueEvent(control.Click()) + return action, nil + }) + return btn +} + func NewList(cnf *config.Config) *tview.List { l := tview.NewList() l.ShowSecondaryText(false) diff --git a/ui/timeline.go b/ui/timeline.go index c46ba46..e3d5a26 100644 --- a/ui/timeline.go +++ b/ui/timeline.go @@ -17,6 +17,7 @@ type Timeline struct { Feeds []*FeedHolder FeedFocusIndex int update chan bool + scrollSleep *scrollSleep } func NewTimeline(tv *TutView, update chan bool) *Timeline { @@ -25,6 +26,7 @@ func NewTimeline(tv *TutView, update chan bool) *Timeline { Feeds: []*FeedHolder{}, update: update, } + tl.scrollSleep = NewScrollSleep(tl.NextItemFeed, tl.PrevItemFeed) var nf *Feed for _, f := range tv.tut.Config.General.Timelines { switch f.FeedType { diff --git a/ui/tutview.go b/ui/tutview.go index f1bfb97..51a0018 100644 --- a/ui/tutview.go +++ b/ui/tutview.go @@ -12,7 +12,6 @@ import ( "github.com/RasmusLindroth/tut/api" "github.com/RasmusLindroth/tut/auth" "github.com/RasmusLindroth/tut/config" - "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) @@ -55,9 +54,6 @@ type TutView struct { PreferenceView *PreferenceView HelpView *HelpView ModalView *ModalView - mouseX int - mouseY int - mouseEvent *tcell.EventMouse FileList []string } diff --git a/ui/view.go b/ui/view.go index d4e6ebf..79e09b4 100644 --- a/ui/view.go +++ b/ui/view.go @@ -38,10 +38,10 @@ func (tv *TutView) RedrawContent() { item, err := f.Data.Item(f.List.Text.GetCurrentItem()) if err != nil { f.Content.Main.SetText("") - f.Content.Controls.SetText("") + f.Content.Controls.Clear() return } - DrawItem(tv.tut, item, f.Content.Main, f.Content.Controls, f.Data.Type()) + DrawItem(tv, item, f.Content.Main, f.Content.Controls, f.Data.Type()) } func (tv *TutView) RedrawPoll(poll *mastodon.Poll) { f := tv.GetCurrentFeed() @@ -59,7 +59,7 @@ func (tv *TutView) RedrawPoll(poll *mastodon.Poll) { } else { so.Poll = poll } - DrawItem(tv.tut, item, f.Content.Main, f.Content.Controls, f.Data.Type()) + DrawItem(tv, item, f.Content.Main, f.Content.Controls, f.Data.Type()) } func (tv *TutView) RedrawControls() { f := tv.GetCurrentFeed() @@ -67,7 +67,7 @@ func (tv *TutView) RedrawControls() { if err != nil { return } - DrawItemControls(tv.tut, item, f.Content.Controls, f.Data.Type()) + DrawItemControls(tv, item, f.Content.Controls, f.Data.Type()) } func (tv *TutView) SetPage(f PageFocusAt) { diff --git a/ui/voteview.go b/ui/voteview.go index 55a51e8..599db67 100644 --- a/ui/voteview.go +++ b/ui/voteview.go @@ -2,22 +2,21 @@ package ui import ( "fmt" - "strings" "github.com/RasmusLindroth/go-mastodon" - "github.com/RasmusLindroth/tut/config" "github.com/rivo/tview" ) type VoteView struct { - tutView *TutView - shared *Shared - View *tview.Flex - textTop *tview.TextView - controls *tview.TextView - list *tview.List - poll *mastodon.Poll - selected []int + tutView *TutView + shared *Shared + View *tview.Flex + textTop *tview.TextView + controls *tview.Flex + list *tview.List + poll *mastodon.Poll + selected []int + scrollSleep *scrollSleep } func NewVoteView(tv *TutView) *VoteView { @@ -25,18 +24,26 @@ func NewVoteView(tv *TutView) *VoteView { tutView: tv, shared: tv.Shared, textTop: NewTextView(tv.tut.Config), - controls: NewTextView(tv.tut.Config), + controls: NewControlView(tv.tut.Config), list: NewList(tv.tut.Config), } + v.scrollSleep = NewScrollSleep(v.Next, v.Prev) v.View = voteViewUI(v) return v } func voteViewUI(v *VoteView) *tview.Flex { - var items []string - items = append(items, config.ColorFromKey(v.tutView.tut.Config, v.tutView.tut.Config.Input.VoteSelect, true)) - v.controls.SetText(strings.Join(items, " ")) + var items []Control + items = append(items, NewControl(v.tutView.tut.Config, v.tutView.tut.Config.Input.VoteSelect, true)) + v.controls.Clear() + for i, item := range items { + if i < len(items)-1 { + v.controls.AddItem(NewControlButton(v.tutView, item), item.Len+1, 0, false) + } else { + v.controls.AddItem(NewControlButton(v.tutView, item), item.Len, 0, false) + } + } r := tview.NewFlex().SetDirection(tview.FlexRow) if v.tutView.tut.Config.General.TerminalTitle < 2 {