aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2023-10-02 16:29:14 +0200
committerRobin Jarry <robin@jarry.cc>2023-10-13 00:31:54 +0200
commit3b1bc60f4e8f11c3244097c714db15bbf34d806e (patch)
tree76dc5defcd1d1060fb620255104114b0f575e6ee
parenta2af6a89abb13cbf255988f28676c849814be45e (diff)
downloadaerc-3b1bc60f4e8f11c3244097c714db15bbf34d806e.tar.gz
aerc-3b1bc60f4e8f11c3244097c714db15bbf34d806e.zip
msgviewer: separate part switcher from viewer
Separate part switcher from message viewer. The part switcher implementation was woven into the message viewer code. Decouple them to make the code more readable. Signed-off-by: Koni Marti <koni.marti@gmail.com> Tested-by: Inwit <inwit@sindominio.net> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--app/msgviewer.go170
-rw-r--r--app/partswitcher.go178
2 files changed, 186 insertions, 162 deletions
diff --git a/app/msgviewer.go b/app/msgviewer.go
index 5da37552..2189779a 100644
--- a/app/msgviewer.go
+++ b/app/msgviewer.go
@@ -37,15 +37,6 @@ type MessageViewer struct {
uiConfig *config.UIConfig
}
-type PartSwitcher struct {
- parts []*PartViewer
- selected int
- alwaysShowMime bool
-
- height int
- mv *MessageViewer
-}
-
func NewMessageViewer(
acct *AccountView, msg lib.MessageView,
) *MessageViewer {
@@ -326,9 +317,7 @@ func (mv *MessageViewer) ToggleKeyPassthrough() bool {
}
func (mv *MessageViewer) SelectedMessagePart() *PartInfo {
- switcher := mv.switcher
- part := switcher.parts[switcher.selected]
-
+ part := mv.switcher.SelectedPart()
return &PartInfo{
Index: part.index,
Msg: part.msg.MessageInfo(),
@@ -338,47 +327,22 @@ func (mv *MessageViewer) SelectedMessagePart() *PartInfo {
}
func (mv *MessageViewer) AttachmentParts(all bool) []*PartInfo {
- var attachments []*PartInfo
-
- for _, p := range mv.switcher.parts {
- if p.part.Disposition == "attachment" || (all && p.part.FileName() != "") {
- pi := &PartInfo{
- Index: p.index,
- Msg: p.msg.MessageInfo(),
- Part: p.part,
- }
- attachments = append(attachments, pi)
- }
- }
-
- return attachments
+ return mv.switcher.AttachmentParts(all)
}
func (mv *MessageViewer) PreviousPart() {
- switcher := mv.switcher
- for {
- switcher.selected--
- if switcher.selected < 0 {
- switcher.selected = len(switcher.parts) - 1
- }
- if switcher.parts[switcher.selected].part.MIMEType != "multipart" {
- break
- }
+ if mv.switcher == nil {
+ return
}
+ mv.switcher.PreviousPart()
mv.Invalidate()
}
func (mv *MessageViewer) NextPart() {
- switcher := mv.switcher
- for {
- switcher.selected++
- if switcher.selected >= len(switcher.parts) {
- switcher.selected = 0
- }
- if switcher.parts[switcher.selected].part.MIMEType != "multipart" {
- break
- }
+ if mv.switcher == nil {
+ return
}
+ mv.switcher.NextPart()
mv.Invalidate()
}
@@ -396,124 +360,6 @@ func (mv *MessageViewer) Close() {
}
}
-func (ps *PartSwitcher) Invalidate() {
- ui.Invalidate()
-}
-
-func (ps *PartSwitcher) Focus(focus bool) {
- if ps.parts[ps.selected].term != nil {
- ps.parts[ps.selected].term.Focus(focus)
- }
-}
-
-func (ps *PartSwitcher) Show(visible bool) {
- if ps.parts[ps.selected].term != nil {
- ps.parts[ps.selected].term.Show(visible)
- }
-}
-
-func (ps *PartSwitcher) Event(event tcell.Event) bool {
- return ps.parts[ps.selected].Event(event)
-}
-
-func (ps *PartSwitcher) Draw(ctx *ui.Context) {
- height := len(ps.parts)
- if height == 1 && !config.Viewer.AlwaysShowMime {
- ps.parts[ps.selected].Draw(ctx)
- return
- }
-
- var styleSwitcher, styleFile, styleMime tcell.Style
-
- // 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 {
- if ps.selected == i {
- styleSwitcher = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_SWITCHER)
- styleFile = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_FILENAME)
- styleMime = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_MIMETYPE)
- } else {
- styleSwitcher = ps.mv.uiConfig.GetStyle(config.STYLE_PART_SWITCHER)
- styleFile = ps.mv.uiConfig.GetStyle(config.STYLE_PART_FILENAME)
- styleMime = ps.mv.uiConfig.GetStyle(config.STYLE_PART_MIMETYPE)
- }
- ctx.Fill(0, y+i, ctx.Width(), 1, ' ', styleSwitcher)
- left := len(part.index) * 2
- if part.part.FileName() != "" {
- name := runewidth.Truncate(part.part.FileName(),
- ctx.Width()-left-1, "…")
- left += ctx.Printf(left, y+i, styleFile, "%s ", name)
- }
- t := "(" + part.part.FullMIMEType() + ")"
- t = runewidth.Truncate(t, ctx.Width()-left, "…")
- ctx.Printf(left, y+i, styleMime, "%s", t)
- }
- ps.parts[ps.selected].Draw(ctx.Subcontext(
- 0, 0, ctx.Width(), ctx.Height()-height))
-}
-
-func (ps *PartSwitcher) MouseEvent(localX int, localY int, event tcell.Event) {
- if event, ok := event.(*tcell.EventMouse); ok {
- switch event.Buttons() {
- case tcell.Button1:
- height := len(ps.parts)
- y := ps.height - height
- if localY < y && ps.parts[ps.selected].term != nil {
- 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 != nil {
- 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 != nil {
- 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 (ps *PartSwitcher) Cleanup() {
- for _, partViewer := range ps.parts {
- partViewer.Cleanup()
- }
-}
-
func (mv *MessageViewer) Event(event tcell.Event) bool {
return mv.switcher.Event(event)
}
diff --git a/app/partswitcher.go b/app/partswitcher.go
new file mode 100644
index 00000000..fa2f56b2
--- /dev/null
+++ b/app/partswitcher.go
@@ -0,0 +1,178 @@
+package app
+
+import (
+ "git.sr.ht/~rjarry/aerc/config"
+ "git.sr.ht/~rjarry/aerc/lib/ui"
+ "github.com/gdamore/tcell/v2"
+ "github.com/mattn/go-runewidth"
+)
+
+type PartSwitcher struct {
+ parts []*PartViewer
+ selected int
+ alwaysShowMime bool
+
+ height int
+ mv *MessageViewer
+}
+
+func (ps *PartSwitcher) PreviousPart() {
+ for {
+ ps.selected--
+ if ps.selected < 0 {
+ ps.selected = len(ps.parts) - 1
+ }
+ if ps.parts[ps.selected].part.MIMEType != "multipart" {
+ break
+ }
+ }
+}
+
+func (ps *PartSwitcher) NextPart() {
+ for {
+ ps.selected++
+ if ps.selected >= len(ps.parts) {
+ ps.selected = 0
+ }
+ if ps.parts[ps.selected].part.MIMEType != "multipart" {
+ break
+ }
+ }
+}
+
+func (ps *PartSwitcher) SelectedPart() *PartViewer {
+ return ps.parts[ps.selected]
+}
+
+func (ps *PartSwitcher) AttachmentParts(all bool) []*PartInfo {
+ var attachments []*PartInfo
+ for _, p := range ps.parts {
+ if p.part.Disposition == "attachment" || (all && p.part.FileName() != "") {
+ pi := &PartInfo{
+ Index: p.index,
+ Msg: p.msg.MessageInfo(),
+ Part: p.part,
+ }
+ attachments = append(attachments, pi)
+ }
+ }
+ return attachments
+}
+
+func (ps *PartSwitcher) Invalidate() {
+ ui.Invalidate()
+}
+
+func (ps *PartSwitcher) Focus(focus bool) {
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Focus(focus)
+ }
+}
+
+func (ps *PartSwitcher) Show(visible bool) {
+ if ps.parts[ps.selected].term != nil {
+ ps.parts[ps.selected].term.Show(visible)
+ }
+}
+
+func (ps *PartSwitcher) Event(event tcell.Event) bool {
+ return ps.parts[ps.selected].Event(event)
+}
+
+func (ps *PartSwitcher) Draw(ctx *ui.Context) {
+ height := len(ps.parts)
+ if height == 1 && !config.Viewer.AlwaysShowMime {
+ ps.parts[ps.selected].Draw(ctx)
+ return
+ }
+
+ var styleSwitcher, styleFile, styleMime tcell.Style
+
+ // 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 {
+ if ps.selected == i {
+ styleSwitcher = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_SWITCHER)
+ styleFile = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_FILENAME)
+ styleMime = ps.mv.uiConfig.GetStyleSelected(config.STYLE_PART_MIMETYPE)
+ } else {
+ styleSwitcher = ps.mv.uiConfig.GetStyle(config.STYLE_PART_SWITCHER)
+ styleFile = ps.mv.uiConfig.GetStyle(config.STYLE_PART_FILENAME)
+ styleMime = ps.mv.uiConfig.GetStyle(config.STYLE_PART_MIMETYPE)
+ }
+ ctx.Fill(0, y+i, ctx.Width(), 1, ' ', styleSwitcher)
+ left := len(part.index) * 2
+ if part.part.FileName() != "" {
+ name := runewidth.Truncate(part.part.FileName(),
+ ctx.Width()-left-1, "…")
+ left += ctx.Printf(left, y+i, styleFile, "%s ", name)
+ }
+ t := "(" + part.part.FullMIMEType() + ")"
+ t = runewidth.Truncate(t, ctx.Width()-left, "…")
+ ctx.Printf(left, y+i, styleMime, "%s", t)
+ }
+ ps.parts[ps.selected].Draw(ctx.Subcontext(
+ 0, 0, ctx.Width(), ctx.Height()-height))
+}
+
+func (ps *PartSwitcher) MouseEvent(localX int, localY int, event tcell.Event) {
+ if event, ok := event.(*tcell.EventMouse); ok {
+ switch event.Buttons() {
+ case tcell.Button1:
+ height := len(ps.parts)
+ y := ps.height - height
+ if localY < y && ps.parts[ps.selected].term != nil {
+ 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 != nil {
+ 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 != nil {
+ 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 (ps *PartSwitcher) Cleanup() {
+ for _, partViewer := range ps.parts {
+ partViewer.Cleanup()
+ }
+}