aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffas <dev@jeffas.io>2019-09-05 23:32:36 +0100
committerDrew DeVault <sir@cmpwn.com>2019-09-11 11:41:34 -0400
commitf6216bb6213a15cdcdf50089f3c4a8e9a30d9337 (patch)
tree3075b68379ae7c13cb9eedc53b7cf033672478a9
parenta441e3b3a55d11e7cc9444298051ff339d1b7519 (diff)
downloadaerc-f6216bb6213a15cdcdf50089f3c4a8e9a30d9337.tar.gz
Add Mouseable
This adds the Mouseable interface. When this is implemented for a component that item can accept and process mouseevents. At the top level when a mouse event is received it is passed to the grid's handler and then it trickles down until it reaches a component that can actually handle it, such as the tablist, dirlist or msglist. A mouse event is passed so that components can handle other things such as scrolling with the mousewheel. The components themselves then perform the necessary actions. Clicking emails in the messagelist opens them in a new tab. Textinputs can be clicked to position the cursor inside them. Mouseevents are not forwarded to the terminal at the moment. Elements which do not handle mouse events are not required to implement the Mouseable interface.
-rw-r--r--lib/ui/borders.go7
-rw-r--r--lib/ui/grid.go41
-rw-r--r--lib/ui/interfaces.go15
-rw-r--r--lib/ui/stack.go9
-rw-r--r--lib/ui/tab.go102
-rw-r--r--lib/ui/textinput.go14
-rw-r--r--widgets/account-wizard.go2
-rw-r--r--widgets/account.go11
-rw-r--r--widgets/aerc.go41
-rw-r--r--widgets/compose.go21
-rw-r--r--widgets/dirlist.go29
-rw-r--r--widgets/msglist.go45
-rw-r--r--widgets/msgviewer.go72
-rw-r--r--widgets/terminal.go14
14 files changed, 376 insertions, 47 deletions
diff --git a/lib/ui/borders.go b/lib/ui/borders.go
index cffd3ca3..7a757595 100644
--- a/lib/ui/borders.go
+++ b/lib/ui/borders.go
@@ -66,3 +66,10 @@ func (bordered *Bordered) Draw(ctx *Context) {
subctx := ctx.Subcontext(x, y, width, height)
bordered.content.Draw(subctx)
}
+
+func (bordered *Bordered) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch content := bordered.content.(type) {
+ case Mouseable:
+ content.MouseEvent(localX, localY, event)
+ }
+}
diff --git a/lib/ui/grid.go b/lib/ui/grid.go
index 7f131bdf..b47c6bd6 100644
--- a/lib/ui/grid.go
+++ b/lib/ui/grid.go
@@ -5,6 +5,8 @@ import (
"math"
"sync"
"sync/atomic"
+
+ "github.com/gdamore/tcell"
)
type Grid struct {
@@ -141,6 +143,45 @@ func (grid *Grid) Draw(ctx *Context) {
}
}
+func (grid *Grid) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ invalid := grid.invalid
+
+ grid.mutex.RLock()
+ defer grid.mutex.RUnlock()
+
+ for _, cell := range grid.cells {
+ cellInvalid := cell.invalid.Load().(bool)
+ if !cellInvalid && !invalid {
+ continue
+ }
+ rows := grid.rowLayout[cell.Row : cell.Row+cell.RowSpan]
+ cols := grid.columnLayout[cell.Column : cell.Column+cell.ColSpan]
+ x := cols[0].Offset
+ y := rows[0].Offset
+ width := 0
+ height := 0
+ for _, col := range cols {
+ width += col.Size
+ }
+ for _, row := range rows {
+ height += row.Size
+ }
+ if x <= localX && localX < x+width && y <= localY && localY < y+height {
+ switch content := cell.Content.(type) {
+ case MouseableDrawableInteractive:
+ content.MouseEvent(localX-x, localY-y, event)
+ case Mouseable:
+ content.MouseEvent(localX-x, localY-y, event)
+ case MouseHandler:
+ content.MouseEvent(localX-x, localY-y, event)
+ }
+ }
+ }
+ }
+}
+
func (grid *Grid) reflow(ctx *Context) {
grid.rowLayout = nil
grid.columnLayout = nil
diff --git a/lib/ui/interfaces.go b/lib/ui/interfaces.go
index 2f634248..9e795712 100644
--- a/lib/ui/interfaces.go
+++ b/lib/ui/interfaces.go
@@ -50,9 +50,18 @@ type Container interface {
Children() []Drawable
}
-// A drawable that can be clicked
-type Clickable interface {
+type MouseHandler interface {
+ // Handle a mouse event which occurred at the local x and y positions
+ MouseEvent(localX int, localY int, event tcell.Event)
+}
+
+// A drawable that can be interacted with by the mouse
+type Mouseable interface {
Drawable
+ MouseHandler
+}
- MouseEvent(event tcell.Event)
+type MouseableDrawableInteractive interface {
+ DrawableInteractive
+ MouseHandler
}
diff --git a/lib/ui/stack.go b/lib/ui/stack.go
index 75cc7807..690a8699 100644
--- a/lib/ui/stack.go
+++ b/lib/ui/stack.go
@@ -37,6 +37,15 @@ func (stack *Stack) Draw(ctx *Context) {
}
}
+func (stack *Stack) MouseEvent(localX int, localY int, event tcell.Event) {
+ if len(stack.children) > 0 {
+ switch element := stack.Peek().(type) {
+ case Mouseable:
+ element.MouseEvent(localX, localY, event)
+ }
+ }
+}
+
func (stack *Stack) Push(d Drawable) {
if len(stack.children) != 0 {
stack.Peek().OnInvalidate(nil)
diff --git a/lib/ui/tab.go b/lib/ui/tab.go
index 90c7ce90..1fd2b80b 100644
--- a/lib/ui/tab.go
+++ b/lib/ui/tab.go
@@ -14,6 +14,9 @@ type Tabs struct {
onInvalidateStrip func(d Drawable)
onInvalidateContent func(d Drawable)
+
+ parent *Tabs
+ CloseTab func(index int)
}
type Tab struct {
@@ -28,7 +31,9 @@ type TabContent Tabs
func NewTabs() *Tabs {
tabs := &Tabs{}
tabs.TabStrip = (*TabStrip)(tabs)
+ tabs.TabStrip.parent = tabs
tabs.TabContent = (*TabContent)(tabs)
+ tabs.TabContent.parent = tabs
tabs.history = []int{}
return tabs
}
@@ -114,6 +119,22 @@ func (tabs *Tabs) SelectPrevious() bool {
return true
}
+func (tabs *Tabs) NextTab() {
+ next := tabs.Selected + 1
+ if next >= len(tabs.Tabs) {
+ next = 0
+ }
+ tabs.Select(next)
+}
+
+func (tabs *Tabs) PrevTab() {
+ next := tabs.Selected - 1
+ if next < 0 {
+ next = len(tabs.Tabs) - 1
+ }
+ tabs.Select(next)
+}
+
func (tabs *Tabs) pushHistory(index int) {
tabs.history = append(tabs.history, index)
}
@@ -146,19 +167,6 @@ func (tabs *Tabs) removeHistory(index int) {
tabs.history = newHist
}
-func (tabs *Tabs) MouseEvent(event tcell.Event) {
- switch event := event.(type) {
- case *tcell.EventMouse:
- if event.Buttons()&tcell.Button1 != 0 {
- x, y := event.Position()
- selectedTab, ok := tabs.TabStrip.Clicked(x, y)
- if ok {
- tabs.Select(selectedTab)
- }
- }
- }
-}
-
// TODO: Color repository
func (strip *TabStrip) Draw(ctx *Context) {
x := 0
@@ -187,21 +195,65 @@ func (strip *TabStrip) Invalidate() {
}
}
+func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) {
+ changeFocus := func(focus bool) {
+ interactive, ok := strip.parent.Tabs[strip.parent.Selected].Content.(Interactive)
+ if ok {
+ interactive.Focus(focus)
+ }
+ }
+ unfocus := func() { changeFocus(false) }
+ refocus := func() { changeFocus(true) }
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ switch event.Buttons() {
+ case tcell.Button1:
+ selectedTab, ok := strip.Clicked(localX, localY)
+ if !ok || selectedTab == strip.parent.Selected {
+ return
+ }
+ unfocus()
+ strip.parent.Select(selectedTab)
+ refocus()
+ case tcell.WheelDown:
+ unfocus()
+ strip.parent.NextTab()
+ refocus()
+ case tcell.WheelUp:
+ unfocus()
+ strip.parent.PrevTab()
+ refocus()
+ case tcell.Button3:
+ selectedTab, ok := strip.Clicked(localX, localY)
+ if !ok {
+ return
+ }
+ unfocus()
+ if selectedTab == strip.parent.Selected {
+ strip.parent.CloseTab(selectedTab)
+ } else {
+ current := strip.parent.Selected
+ strip.parent.CloseTab(selectedTab)
+ strip.parent.Select(current)
+ }
+ refocus()
+ }
+ }
+}
+
func (strip *TabStrip) OnInvalidate(onInvalidate func(d Drawable)) {
strip.onInvalidateStrip = onInvalidate
}
func (strip *TabStrip) Clicked(mouseX int, mouseY int) (int, bool) {
x := 0
- if mouseY == 0 {
- for i, tab := range strip.Tabs {
- trunc := runewidth.Truncate(tab.Name, 32, "…")
- length := len(trunc) + 2
- if x <= mouseX && mouseX < x+length {
- return i, true
- }
- x += length
+ for i, tab := range strip.Tabs {
+ trunc := runewidth.Truncate(tab.Name, 32, "…")
+ length := len(trunc) + 2
+ if x <= mouseX && mouseX < x+length {
+ return i, true
}
+ x += length
}
return 0, false
}
@@ -225,6 +277,14 @@ func (content *TabContent) Draw(ctx *Context) {
tab.Content.Draw(ctx)
}
+func (content *TabContent) MouseEvent(localX int, localY int, event tcell.Event) {
+ tab := content.Tabs[content.Selected]
+ switch tabContent := tab.Content.(type) {
+ case Mouseable:
+ tabContent.MouseEvent(localX, localY, event)
+ }
+}
+
func (content *TabContent) Invalidate() {
if content.onInvalidateContent != nil {
content.onInvalidateContent(content)
diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go
index 00e91eee..39351735 100644
--- a/lib/ui/textinput.go
+++ b/lib/ui/textinput.go
@@ -97,6 +97,20 @@ func (ti *TextInput) Draw(ctx *Context) {
}
}
+func (ti *TextInput) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ switch event.Buttons() {
+ case tcell.Button1:
+ if localX >= len(ti.prompt)+1 && localX <= len(ti.text[ti.scroll:])+len(ti.prompt)+1 {
+ ti.index = localX - len(ti.prompt) - 1
+ ti.ensureScroll()
+ ti.Invalidate()
+ }
+ }
+ }
+}
+
func (ti *TextInput) Focus(focus bool) {
ti.focus = focus
if focus && ti.ctx != nil {
diff --git a/widgets/account-wizard.go b/widgets/account-wizard.go
index 5acd26c5..904013f9 100644
--- a/widgets/account-wizard.go
+++ b/widgets/account-wizard.go
@@ -523,7 +523,7 @@ func (wizard *AccountWizard) finish(tutorial bool) {
}
wizard.conf.Accounts = append(wizard.conf.Accounts, account)
- view := NewAccountView(wizard.conf, &account,
+ view := NewAccountView(wizard.aerc, wizard.conf, &account,
wizard.aerc.logger, wizard.aerc)
wizard.aerc.accounts[account.Name] = view
wizard.aerc.NewTab(view, account.Name)
diff --git a/widgets/account.go b/widgets/account.go
index 688b6602..1220753a 100644
--- a/widgets/account.go
+++ b/widgets/account.go
@@ -17,6 +17,7 @@ import (
type AccountView struct {
acct *config.AccountConfig
+ aerc *Aerc
conf *config.AercConfig
dirlist *DirectoryList
grid *ui.Grid
@@ -26,7 +27,7 @@ type AccountView struct {
worker *types.Worker
}
-func NewAccountView(conf *config.AercConfig, acct *config.AccountConfig,
+func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountConfig,
logger *log.Logger, host TabHost) *AccountView {
grid := ui.NewGrid().Rows([]ui.GridSpec{
@@ -42,6 +43,7 @@ func NewAccountView(conf *config.AercConfig, acct *config.AccountConfig,
Color(tcell.ColorDefault, tcell.ColorRed)
return &AccountView{
acct: acct,
+ aerc: aerc,
grid: grid,
host: host,
logger: logger,
@@ -53,11 +55,12 @@ func NewAccountView(conf *config.AercConfig, acct *config.AccountConfig,
grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT))
}
- msglist := NewMessageList(conf, logger)
+ msglist := NewMessageList(conf, logger, aerc)
grid.AddChild(msglist).At(0, 1)
view := &AccountView{
acct: acct,
+ aerc: aerc,
conf: conf,
dirlist: dirlist,
grid: grid,
@@ -124,6 +127,10 @@ func (acct *AccountView) Draw(ctx *ui.Context) {
acct.grid.Draw(ctx)
}
+func (acct *AccountView) MouseEvent(localX int, localY int, event tcell.Event) {
+ acct.grid.MouseEvent(localX, localY, event)
+}
+
func (acct *AccountView) Focus(focus bool) {
// TODO: Unfocus children I guess
}
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 87009cd3..fe3c1e21 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -74,7 +74,7 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger,
conf.Triggers.ExecuteCommand = cmd
for i, acct := range conf.Accounts {
- view := NewAccountView(conf, &conf.Accounts[i], logger, aerc)
+ view := NewAccountView(aerc, conf, &conf.Accounts[i], logger, aerc)
aerc.accounts[acct.Name] = view
tabs.Add(view, acct.Name)
}
@@ -85,6 +85,22 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger,
aerc.NewTab(wizard, "New account")
}
+ tabs.CloseTab = func(index int) {
+ switch content := aerc.tabs.Tabs[index].Content.(type) {
+ case *AccountView:
+ return
+ case *AccountWizard:
+ return
+ case *Composer:
+ aerc.RemoveTab(content)
+ content.Close()
+ case *Terminal:
+ content.Close(nil)
+ case *MessageViewer:
+ aerc.RemoveTab(content)
+ }
+ }
+
return aerc
}
@@ -235,7 +251,12 @@ func (aerc *Aerc) Event(event tcell.Event) bool {
return false
}
case *tcell.EventMouse:
- aerc.tabs.MouseEvent(event)
+ if event.Buttons() == tcell.ButtonNone {
+ return false
+ }
+ x, y := event.Position()
+ aerc.grid.MouseEvent(x, y, event)
+ return true
}
return false
}
@@ -260,8 +281,8 @@ func (aerc *Aerc) SelectedTab() ui.Drawable {
return aerc.tabs.Tabs[aerc.tabs.Selected].Content
}
-func (aerc *Aerc) NewTab(drawable ui.Drawable, name string) *ui.Tab {
- tab := aerc.tabs.Add(drawable, name)
+func (aerc *Aerc) NewTab(clickable ui.Drawable, name string) *ui.Tab {
+ tab := aerc.tabs.Add(clickable, name)
aerc.tabs.Select(len(aerc.tabs.Tabs) - 1)
return tab
}
@@ -275,19 +296,11 @@ func (aerc *Aerc) ReplaceTab(tabSrc ui.Drawable, tabTarget ui.Drawable, name str
}
func (aerc *Aerc) NextTab() {
- next := aerc.tabs.Selected + 1
- if next >= len(aerc.tabs.Tabs) {
- next = 0
- }
- aerc.tabs.Select(next)
+ aerc.tabs.NextTab()
}
func (aerc *Aerc) PrevTab() {
- next := aerc.tabs.Selected - 1
- if next < 0 {
- next = len(aerc.tabs.Tabs) - 1
- }
- aerc.tabs.Select(next)
+ aerc.tabs.PrevTab()
}
func (aerc *Aerc) SelectTab(name string) bool {
diff --git a/widgets/compose.go b/widgets/compose.go
index bd4301ae..0e7f09e6 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -40,10 +40,12 @@ type Composer struct {
worker *types.Worker
layout HeaderLayout
- focusable []ui.DrawableInteractive
+ focusable []ui.MouseableDrawableInteractive
focused int
onClose []func(ti *Composer)
+
+ width int
}
func NewComposer(conf *config.AercConfig,
@@ -87,10 +89,10 @@ func NewComposer(conf *config.AercConfig,
func buildComposeHeader(layout HeaderLayout, defaults map[string]string) (
newLayout HeaderLayout,
editors map[string]*headerEditor,
- focusable []ui.DrawableInteractive,
+ focusable []ui.MouseableDrawableInteractive,
) {
editors = make(map[string]*headerEditor)
- focusable = make([]ui.DrawableInteractive, 0)
+ focusable = make([]ui.MouseableDrawableInteractive, 0)
for _, row := range layout {
for _, h := range row {
@@ -99,7 +101,7 @@ func buildComposeHeader(layout HeaderLayout, defaults map[string]string) (
switch h {
case "From":
// Prepend From to support backtab
- focusable = append([]ui.DrawableInteractive{e}, focusable...)
+ focusable = append([]ui.MouseableDrawableInteractive{e}, focusable...)
default:
focusable = append(focusable, e)
}
@@ -176,6 +178,7 @@ func (c *Composer) OnClose(fn func(composer *Composer)) {
}
func (c *Composer) Draw(ctx *ui.Context) {
+ c.width = ctx.Width()
c.grid.Draw(ctx)
}
@@ -617,6 +620,16 @@ func (he *headerEditor) Draw(ctx *ui.Context) {
he.input.Draw(ctx.Subcontext(size, 0, ctx.Width()-size, 1))
}
+func (he *headerEditor) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ width := runewidth.StringWidth(he.name + " ")
+ if localX >= width {
+ he.input.MouseEvent(localX-width, localY, event)
+ }
+ }
+}
+
func (he *headerEditor) Invalidate() {
he.input.Invalidate()
}
diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index 33119dd2..ec730825 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -137,6 +137,35 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
}
}
+func (dirlist *DirectoryList) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ switch event.Buttons() {
+ case tcell.Button1:
+ clickedDir, ok := dirlist.Clicked(localX, localY)
+ if ok {
+ dirlist.Select(clickedDir)
+ }
+ case tcell.WheelDown:
+ dirlist.Next()
+ case tcell.WheelUp:
+ dirlist.Prev()
+ }
+ }
+}
+
+func (dirlist *DirectoryList) Clicked(x int, y int) (string, bool) {
+ if dirlist.dirs == nil || len(dirlist.dirs) == 0 {
+ return "", false
+ }
+ for i, name := range dirlist.dirs {
+ if i == y {
+ return name, true
+ }
+ }
+ return "", false
+}
+
func (dirlist *DirectoryList) NextPrev(delta int) {
curIdx := sort.SearchStrings(dirlist.dirs, dirlist.selected)
if curIdx == len(dirlist.dirs) {
diff --git a/widgets/msglist.go b/widgets/msglist.go
index 8ed716b0..b7c921c3 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -25,6 +25,7 @@ type MessageList struct {
spinner *Spinner
store *lib.MessageStore
isInitalizing bool
+ aerc *Aerc
}
type msgSorter struct {
@@ -55,12 +56,13 @@ func (s *msgSorter) Swap(i, j int) {
s.uids[j] = tmp
}
-func NewMessageList(conf *config.AercConfig, logger *log.Logger) *MessageList {
+func NewMessageList(conf *config.AercConfig, logger *log.Logger, aerc *Aerc) *MessageList {
ml := &MessageList{
conf: conf,
logger: logger,
spinner: NewSpinner(&conf.Ui),
isInitalizing: true,
+ aerc: aerc,
}
ml.spinner.OnInvalidate(func(_ ui.Drawable) {
ml.Invalidate()
@@ -161,6 +163,47 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
}
}
+func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ switch event.Buttons() {
+ case tcell.Button1:
+ if ml.aerc == nil {
+ return
+ }
+ selectedMsg, ok := ml.Clicked(localX, localY)
+ if ok {
+ ml.Select(selectedMsg)
+ acct := ml.aerc.SelectedAccount()
+ if acct.Messages().Empty() {
+ return
+ }
+ store := acct.Messages().Store()
+ msg := acct.Messages().Selected()
+ if msg == nil {
+ return
+ }
+ viewer := NewMessageViewer(acct, ml.aerc.Config(), store, msg)
+ ml.aerc.NewTab(viewer, msg.Envelope.Subject)
+ }
+ case tcell.WheelDown:
+ ml.store.Next()
+ ml.Scroll()
+ case tcell.WheelUp:
+ ml.store.Prev()
+ ml.Scroll()
+ }
+ }
+}
+
+func (ml *MessageList) Clicked(x, y int) (int, bool) {
+ store := ml.Store()
+ if store == nil || ml.nmsgs == 0 || y >= ml.nmsgs {
+ return 0, false
+ }
+ return y + ml.scroll, true
+}
+
func (ml *MessageList) Height() int {
return ml.height
}
diff --git a/widgets/msgviewer.go b/widgets/msgviewer.go
index e210616a..c1790707 100644
--- a/widgets/msgviewer.go
+++ b/widgets/msgviewer.go
@@ -42,6 +42,9 @@ type PartSwitcher struct {
selected int
showHeaders bool
alwaysShowMime bool
+
+ height int
+ mv *MessageViewer
}
func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
@@ -77,7 +80,7 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
grid.AddChild(header).At(0, 0)
grid.AddChild(switcher).At(1, 0)
- return &MessageViewer{
+ mv := &MessageViewer{
acct: acct,
conf: conf,
grid: grid,
@@ -85,6 +88,9 @@ func NewMessageViewer(acct *AccountView, conf *config.AercConfig,
store: store,
switcher: switcher,
}
+ switcher.mv = mv
+
+ return mv
}
func fmtHeader(msg *models.MessageInfo, header string) string {
@@ -194,6 +200,13 @@ func (mv *MessageViewer) Draw(ctx *ui.Context) {
mv.grid.Draw(ctx)
}
+func (mv *MessageViewer) MouseEvent(localX int, localY int, event tcell.Event) {
+ if mv.err != nil {
+ return
+ }
+ mv.grid.MouseEvent(localX, localY, event)
+}
+
func (mv *MessageViewer) Invalidate() {
mv.grid.Invalidate()
}
@@ -295,6 +308,7 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) {
return
}
// TODO: cap height and add scrolling for messages with many parts
+ ps.height = ctx.Height()
y := ctx.Height() - height
for i, part := range ps.parts {
style := tcell.StyleDefault.Reverse(ps.selected == i)
@@ -311,6 +325,62 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) {
0, 0, ctx.Width(), ctx.Height()-height))
}
+func (ps *PartSwitcher) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ switch event.Buttons() {
+ case tcell.Button1:
+ height := len(ps.parts)
+ y := ps.height - height
+ if localY < y {
+ ps.parts[ps.selected].term.MouseEvent(localX, localY, event)
+ }
+ for i, _ := range ps.parts {
+ if localY != y+i {
+ continue
+ }
+ if ps.parts[i].part.MIMEType == "multipart" {
+ continue
+ }
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(false)
+ }
+ ps.selected = i
+ ps.Invalidate()
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(true)
+ }
+ }
+ case tcell.WheelDown:
+ height := len(ps.parts)
+ y := ps.height - height
+ if localY < y {
+ ps.parts[ps.selected].term.MouseEvent(localX, localY, event)
+ }
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(false)
+ }
+ ps.mv.NextPart()
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(true)
+ }
+ case tcell.WheelUp:
+ height := len(ps.parts)
+ y := ps.height - height
+ if localY < y {
+ ps.parts[ps.selected].term.MouseEvent(localX, localY, event)
+ }
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(false)
+ }
+ ps.mv.PreviousPart()
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(true)
+ }
+ }
+ }
+}
+
func (mv *MessageViewer) Event(event tcell.Event) bool {
return mv.switcher.Event(event)
}
diff --git a/widgets/terminal.go b/widgets/terminal.go
index 008a36f0..6ad6904b 100644
--- a/widgets/terminal.go
+++ b/widgets/terminal.go
@@ -311,6 +311,20 @@ func (term *Terminal) Draw(ctx *ui.Context) {
}
}
+func (term *Terminal) MouseEvent(localX int, localY int, event tcell.Event) {
+ switch event := event.(type) {
+ case *tcell.EventMouse:
+ if term.OnEvent != nil {
+ if term.OnEvent(event) {
+ return
+ }
+ }
+ if term.closed {
+ return
+ }
+ }
+}
+
func (term *Terminal) Focus(focus bool) {
if term.closed {
return