about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJeffas <dev@jeffas.io>2019-09-11 17:37:21 +0100
committerDrew DeVault <sir@cmpwn.com>2019-09-11 12:37:43 -0400
commit618a500341d54ec5bec6d035a86b1307ff1dad0a (patch)
tree090fdff220eeb29e4bee7347a16d9e5ff7fbd0b9
parent572d9ff72883264fbffe35e998b9a6193f20137a (diff)
downloadaerc-618a500341d54ec5bec6d035a86b1307ff1dad0a.tar.gz
Add display of unread messages in dirlist
Add an onUpdateDirs handler. This is used to invalidate the dirlist and
redraw with the correct number of recent/unread/total messages is shown.

A config option and formatting options are provided.
-rw-r--r--config/aerc.conf.in5
-rw-r--r--config/config.go7
-rw-r--r--doc/aerc-config.5.scd16
-rw-r--r--lib/msgstore.go8
-rw-r--r--widgets/dirlist.go99
5 files changed, 131 insertions, 4 deletions
diff --git a/config/aerc.conf.in b/config/aerc.conf.in
index 4d0f9fd..c50b7b9 100644
--- a/config/aerc.conf.in
+++ b/config/aerc.conf.in
@@ -43,6 +43,11 @@ mouse-enabled=false
 # Default: yes
 new-message-bell=true
 
+# Describes the format string to use for the directory list
+#
+# Default: %n %>r
+dirlist-format=%n %>r
+
 [viewer]
 #
 # Specifies the pager to use when displaying emails. Note that some filters
diff --git a/config/config.go b/config/config.go
index 06caec1..738fd1d 100644
--- a/config/config.go
+++ b/config/config.go
@@ -35,6 +35,7 @@ type UIConfig struct {
 	NewMessageBell    bool     `ini:"new-message-bell"`
 	Spinner           string   `ini:"spinner"`
 	SpinnerDelimiter  string   `ini:"spinner-delimiter"`
+	DirListFormat     string   `ini:"dirlist-format"`
 }
 
 const (
@@ -349,9 +350,9 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
 			EmptyDirlist:      "(no folders)",
 			MouseEnabled:      false,
 			NewMessageBell:    true,
-			Spinner:
-				 "[..]    , [..]   ,  [..]  ,   [..] ,    [..],   [..] ,  [..]  , [..]   ",
-			SpinnerDelimiter: ",",
+			Spinner:           "[..]    , [..]   ,  [..]  ,   [..] ,    [..],   [..] ,  [..]  , [..]   ",
+			SpinnerDelimiter:  ",",
+			DirListFormat:     "%n %>r",
 		},
 
 		Viewer: ViewerConfig{
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 9257bde..d422e5d 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -125,6 +125,22 @@ These options are configured in the *[ui]* section of aerc.conf.
 
 	Default: ","
 
+*dirlist-format*
+	Describes the format string to use for the directory list
+
+	Default: %n %>r
+
+[- *Format specifier*
+:[ *Description*
+|  %%
+:  literal %
+|  %n
+:  directory name
+|  %r
+:  recent/unseen/total message count
+|  %>X
+:  make format specifier 'X' be right justified
+
 ## VIEWER
 
 These options are configured in the *[viewer]* section of aerc.conf.
diff --git a/lib/msgstore.go b/lib/msgstore.go
index bbdfa57..2733288 100644
--- a/lib/msgstore.go
+++ b/lib/msgstore.go
@@ -27,6 +27,7 @@ type MessageStore struct {
 
 	// Map of uids we've asked the worker to fetch
 	onUpdate       func(store *MessageStore) // TODO: multiple onUpdate handlers
+	onUpdateDirs   func()
 	pendingBodies  map[uint32]interface{}
 	pendingHeaders map[uint32]interface{}
 	worker         *types.Worker
@@ -234,10 +235,17 @@ func (store *MessageStore) OnUpdate(fn func(store *MessageStore)) {
 	store.onUpdate = fn
 }
 
+func (store *MessageStore) OnUpdateDirs(fn func()) {
+	store.onUpdateDirs = fn
+}
+
 func (store *MessageStore) update() {
 	if store.onUpdate != nil {
 		store.onUpdate(store)
 	}
+	if store.onUpdateDirs != nil {
+		store.onUpdateDirs()
+	}
 }
 
 func (store *MessageStore) Delete(uids []uint32,
diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index ec73082..ef2dd1e 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -1,15 +1,18 @@
 package widgets
 
 import (
+	"fmt"
 	"log"
 	"regexp"
 	"sort"
 
 	"github.com/gdamore/tcell"
+	"github.com/mattn/go-runewidth"
 
 	"git.sr.ht/~sircmpwn/aerc/config"
 	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
+	"git.sr.ht/~sircmpwn/aerc/models"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
@@ -105,6 +108,92 @@ func (dirlist *DirectoryList) Invalidate() {
 	dirlist.DoInvalidate(dirlist)
 }
 
+func (dirlist *DirectoryList) getDirString(name string, width int, recentUnseen func() string) string {
+	percent := false
+	rightJustify := false
+	formatted := ""
+	doRightJustify := func(s string) {
+		formatted = runewidth.FillRight(formatted, width-len(s))
+		formatted = runewidth.Truncate(formatted, width-len(s), "…")
+	}
+	for _, char := range dirlist.uiConf.DirListFormat {
+		switch char {
+		case '%':
+			if percent {
+				formatted += string(char)
+				percent = false
+			} else {
+				percent = true
+			}
+		case '>':
+			if percent {
+				rightJustify = true
+			}
+		case 'n':
+			if percent {
+				if rightJustify {
+					doRightJustify(name)
+					rightJustify = false
+				}
+				formatted += name
+				percent = false
+			}
+		case 'r':
+			if percent {
+				rString := recentUnseen()
+				if rightJustify {
+					doRightJustify(rString)
+					rightJustify = false
+				}
+				formatted += rString
+				percent = false
+			}
+		default:
+			formatted += string(char)
+		}
+	}
+	return formatted
+}
+
+func (dirlist *DirectoryList) getRUEString(name string) string {
+	totalUnseen := 0
+	totalRecent := 0
+	totalExists := 0
+	if msgStore, ok := dirlist.MsgStore(name); ok {
+		for _, msg := range msgStore.Messages {
+			if msg == nil {
+				continue
+			}
+			seen := false
+			recent := false
+			for _, flag := range msg.Flags {
+				if flag == models.SeenFlag {
+					seen = true
+				} else if flag == models.RecentFlag {
+					recent = true
+				}
+			}
+			if !seen {
+				if recent {
+					totalRecent++
+				} else {
+					totalUnseen++
+				}
+			}
+		}
+		totalExists = msgStore.DirInfo.Exists
+	}
+	rueString := ""
+	if totalRecent > 0 {
+		rueString = fmt.Sprintf("%d/%d/%d", totalRecent, totalUnseen, totalExists)
+	} else if totalUnseen > 0 {
+		rueString = fmt.Sprintf("%d/%d", totalUnseen, totalExists)
+	} else if totalExists > 0 {
+		rueString = fmt.Sprintf("%d", totalExists)
+	}
+	return rueString
+}
+
 func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
 	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
 
@@ -132,7 +221,12 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
 			style = style.Foreground(tcell.ColorGray)
 		}
 		ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
-		ctx.Printf(0, row, style, "%s", name)
+
+		dirString := dirlist.getDirString(name, ctx.Width(), func() string {
+			return dirlist.getRUEString(name)
+		})
+
+		ctx.Printf(0, row, style, dirString)
 		row++
 	}
 }
@@ -233,4 +327,7 @@ func (dirlist *DirectoryList) MsgStore(name string) (*lib.MessageStore, bool) {
 
 func (dirlist *DirectoryList) SetMsgStore(name string, msgStore *lib.MessageStore) {
 	dirlist.store.SetMessageStore(name, msgStore)
+	msgStore.OnUpdateDirs(func() {
+		dirlist.Invalidate()
+	})
 }