diff --git a/config.example.ini b/config.example.ini index 8be6e52..61481e0 100644 --- a/config.example.ini +++ b/config.example.ini @@ -6,6 +6,10 @@ # default=true confirmation=true +# Enable support for using the mouse in tut to select items. +# default=false +mouse-support=false + # Timelines adds windows of feeds. You can customize the number of feeds, what # they should show and the key to activate them. # diff --git a/config/config.go b/config/config.go index c4a9da8..a46e162 100644 --- a/config/config.go +++ b/config/config.go @@ -88,6 +88,7 @@ type Timeline struct { type General struct { Confirmation bool + MouseSupport bool DateTodayFormat string DateFormat string DateRelative int @@ -735,6 +736,7 @@ func parseGeneral(cfg *ini.File) General { general := General{} general.Confirmation = cfg.Section("general").Key("confirmation").MustBool(true) + general.MouseSupport = cfg.Section("general").Key("mouse-support").MustBool(false) dateFormat := cfg.Section("general").Key("date-format").String() if dateFormat == "" { dateFormat = "2006-01-02 15:04" diff --git a/config/default_config.go b/config/default_config.go index 7051f7d..6cb16c8 100644 --- a/config/default_config.go +++ b/config/default_config.go @@ -8,6 +8,10 @@ var conftext = `# Configuration file for tut # default=true confirmation=true +# Enable support for using the mouse in tut to select items. +# default=false +mouse-support=false + # Timelines adds windows of feeds. You can customize the number of feeds, what # they should show and the key to activate them. # diff --git a/go.mod b/go.mod index 311c6eb..10b9f9e 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,19 @@ module github.com/RasmusLindroth/tut -go 1.17 +go 1.18 require ( github.com/RasmusLindroth/go-mastodon v0.0.8 github.com/atotto/clipboard v0.1.4 - github.com/gdamore/tcell/v2 v2.5.1 + 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/microcosm-cc/bluemonday v1.0.19 github.com/pelletier/go-toml/v2 v2.0.2 - github.com/rivo/tview v0.0.0-20220709181631-73bf2902b59a - github.com/rivo/uniseg v0.3.0 - golang.org/x/net v0.0.0-20220726230323-06994584191e + 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 ) @@ -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-20220727055044-e65921a090b8 // indirect + golang.org/x/sys v0.0.0-20220731174439-a90be440212d // 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 c9d6ea8..557dce2 100644 --- a/go.sum +++ b/go.sum @@ -8,9 +8,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= -github.com/gdamore/tcell/v2 v2.5.1 h1:zc3LPdpK184lBW7syF2a5C6MV827KmErk9jGVnmsl/I= -github.com/gdamore/tcell/v2 v2.5.1/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= +github.com/gdamore/tcell/v2 v2.5.2 h1:tKzG29kO9p2V++3oBY2W9zUjYu7IK1MENFeY/BzJSVY= +github.com/gdamore/tcell/v2 v2.5.2/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc h1:6ZZLxG+lB+Qbg+chtzAEeetwqjlPnY0BXbhL3lQWYOg= github.com/gen2brain/beeep v0.0.0-20220518085355-d7852edf42fc/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw= github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4 h1:qZNfIGkIANxGv/OqtnntR4DfOY2+BgwR60cAcu/i3SE= @@ -37,12 +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-20220709181631-73bf2902b59a h1:ZjJ1XcvsZkNVO+Rq/vQTOXtN3cmuAgpCp8m4fKG5CkY= -github.com/rivo/tview v0.0.0-20220709181631-73bf2902b59a/go.mod h1:WIfMkQNY+oq/mWwtsjOYHIZBuwthioY2srOmljJkTnk= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +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/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.3.0 h1:eyC18g7xB83Dv/xlJXLgNkRidVoR7nqFZBJvqo/K188= -github.com/rivo/uniseg v0.3.0/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= +github.com/rivo/uniseg v0.3.1/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= @@ -50,32 +48,17 @@ 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-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= -golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctgGy/3P4rYWQRVJD4/Yg= -golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM= +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/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220727055044-e65921a090b8 h1:dyU22nBWzrmTQxtNrr4dzVOvaw35nUYE279vF9UmsI8= -golang.org/x/sys v0.0.0-20220727055044-e65921a090b8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +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/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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= diff --git a/main.go b/main.go index fc0cc53..bc792a7 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,7 @@ import ( "github.com/rivo/tview" ) -const version = "1.0.15" +const version = "1.0.16" func main() { util.SetTerminalTitle("tut") @@ -21,6 +21,9 @@ func main() { App: app, Config: config.Load(), } + if t.Config.General.MouseSupport { + app.EnableMouse(true) + } tview.Styles = tview.Theme{ PrimitiveBackgroundColor: t.Config.Style.Background, // background ContrastBackgroundColor: t.Config.Style.Text, //background for button, checkbox, form, modal @@ -36,6 +39,9 @@ func main() { } main := ui.NewTutView(t, accs, selectedUser) app.SetInputCapture(main.Input) + if t.Config.General.MouseSupport { + app.SetMouseCapture(main.MouseInput) + } if err := app.SetRoot(main.View, true).Run(); err != nil { panic(err) } diff --git a/ui/composeview.go b/ui/composeview.go index 8b83fb6..7b07d5c 100644 --- a/ui/composeview.go +++ b/ui/composeview.go @@ -422,7 +422,7 @@ func (m *MediaList) Draw() { topText := "File desc: " index := m.list.GetCurrentItem() - if len(m.Files) != 0 && index < len(m.Files) && m.Files[index].Description != "" { + if len(m.Files) != 0 && index > len(m.Files)-1 && m.Files[index].Description != "" { topText += tview.Escape(m.Files[index].Description) } m.text.SetText(topText) @@ -466,10 +466,11 @@ func (m *MediaList) Next() { func (m *MediaList) Delete() { index := m.list.GetCurrentItem() - if len(m.Files) == 0 || index > len(m.Files) { + if len(m.Files) == 0 || index > len(m.Files)-1 { return } m.list.RemoveItem(index) + m.list.SetCurrentItem(index) m.Files = append(m.Files[:index], m.Files[index+1:]...) m.Draw() } diff --git a/ui/feed.go b/ui/feed.go index 7d39a14..3e1226d 100644 --- a/ui/feed.go +++ b/ui/feed.go @@ -460,6 +460,19 @@ func (fl *FeedList) AddItem(text string, symbols string, id uint) { fl.Symbol.AddItem(symbols, fmt.Sprintf("%d", id), 0, nil) } +func (fl *FeedList) Set(index int) (loadOlder bool, loadNewer bool) { + ni := index + if ni >= fl.Text.GetItemCount() { + ni = fl.Text.GetItemCount() - 1 + } + if ni < 0 { + ni = 0 + } + fl.Text.SetCurrentItem(ni) + fl.Symbol.SetCurrentItem(ni) + return fl.Text.GetItemCount()-(ni+1) < 5, ni-fl.stickyCount < 4 +} + func (fl *FeedList) Next() (loadOlder bool) { ni := fl.Text.GetCurrentItem() + 1 if ni >= fl.Text.GetItemCount() { diff --git a/ui/input.go b/ui/input.go index f223eb6..bac2352 100644 --- a/ui/input.go +++ b/ui/input.go @@ -11,6 +11,7 @@ import ( "github.com/RasmusLindroth/tut/feed" "github.com/RasmusLindroth/tut/util" "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" ) func (tv *TutView) Input(event *tcell.EventKey) *tcell.EventKey { @@ -893,3 +894,138 @@ func (tv *TutView) InputCmdView(event *tcell.EventKey) *tcell.EventKey { } return event } + +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 + 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 + } + } + } + } + if tv.PageFocus == LoginFocus { + if action == tview.MouseLeftClick { + list := tv.LoginView.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.LoginView.Selected() + } + } + if tv.PageFocus == LinkFocus { + if action == tview.MouseLeftClick { + list := tv.LinkView.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.LinkView.Open() + } + } + 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) {}) + } + 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) {}) + } + } + } + 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() + } + } + 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) {}) + } + } + 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) feedListMouse(list *tview.List, i int, action tview.MouseAction) { + tv.SetPage(MainFocus) + tv.FocusFeed(i) + mh := list.MouseHandler() + if mh == nil { + return + } + lastIndex := list.GetCurrentItem() + mh(action, tv.mouseEvent, func(p tview.Primitive) {}) + newIndex := list.GetCurrentItem() + if lastIndex != newIndex { + tv.Timeline.SetItemFeedIndex(newIndex) + } +} diff --git a/ui/timeline.go b/ui/timeline.go index 05c713e..c46ba46 100644 --- a/ui/timeline.go +++ b/ui/timeline.go @@ -221,6 +221,19 @@ func (tl *Timeline) PrevItemFeed() { tl.DrawContent() } +func (tl *Timeline) SetItemFeedIndex(index int) { + fh := tl.Feeds[tl.FeedFocusIndex] + f := fh.Feeds[fh.FeedIndex] + loadOlder, loadNewer := f.List.Set(index) + if loadOlder { + f.LoadOlder() + } + if loadNewer { + f.LoadNewer(false) + } + tl.DrawContent() +} + func (tl *Timeline) HomeItemFeed() { fh := tl.Feeds[tl.FeedFocusIndex] f := fh.Feeds[fh.FeedIndex] diff --git a/ui/tutview.go b/ui/tutview.go index 51a0018..f1bfb97 100644 --- a/ui/tutview.go +++ b/ui/tutview.go @@ -12,6 +12,7 @@ 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" ) @@ -54,6 +55,9 @@ type TutView struct { PreferenceView *PreferenceView HelpView *HelpView ModalView *ModalView + mouseX int + mouseY int + mouseEvent *tcell.EventMouse FileList []string }