~sircmpwn/aerc

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH] Apply relevant msglist styles in order

Details
Message ID
<20201026194316.990787-1-chris@vittal.dev>
DKIM signature
pass
Download raw message
Patch: +91 -20
Allow styles to be layered over a base style. The list of styles to
apply is layered over the base style in order, such that if the layer
does not differ from the base it is not used. The order that these
styles are applied in is, from first to last:

msglist_default
msglist_unread
msglist_read    (exclusive with unread, so technically the same level)
msglist_flagged
msglist_marked
msglist_deleted

So, deleted's style dominates.

This fixes an issue where msglist_deleted style was not being applied.
---
Technically, this could be fixed by simply moving the 'is message
deleted' check to after all the other styles are applied, however this
also fixes another issue I've noticed where flagging a message didn't
preserve the read/unread styling as well.

 config/config.go         |  8 ++++++
 config/style.go          | 53 +++++++++++++++++++++++++++++++++++++---
 doc/aerc-stylesets.7.scd | 25 ++++++++++++++++---
 widgets/msglist.go       | 25 ++++++++++---------
 4 files changed, 91 insertions(+), 20 deletions(-)

diff --git a/config/config.go b/config/config.go
index 3ae26c1..610eb21 100644
--- a/config/config.go
+++ b/config/config.go
@@ -699,3 +699,11 @@ func (uiConfig UIConfig) GetStyle(so StyleObject) tcell.Style {
func (uiConfig UIConfig) GetStyleSelected(so StyleObject) tcell.Style {
	return uiConfig.style.Selected(so)
}

func (uiConfig UIConfig) GetComposedStyle(base StyleObject, styles []StyleObject) tcell.Style {
	return uiConfig.style.Compose(base, styles)
}

func (uiConfig UIConfig) GetComposedStyleSelected(base StyleObject, styles []StyleObject) tcell.Style {
	return uiConfig.style.ComposeSelected(base, styles)
}
diff --git a/config/style.go b/config/style.go
index caf9e4f..5a6fa64 100644
--- a/config/style.go
+++ b/config/style.go
@@ -31,9 +31,9 @@ const (
	STYLE_MSGLIST_DEFAULT
	STYLE_MSGLIST_UNREAD
	STYLE_MSGLIST_READ
	STYLE_MSGLIST_DELETED
	STYLE_MSGLIST_MARKED
	STYLE_MSGLIST_FLAGGED
	STYLE_MSGLIST_MARKED
	STYLE_MSGLIST_DELETED

	STYLE_DIRLIST_DEFAULT

@@ -67,9 +67,9 @@ var StyleNames = map[string]StyleObject{
	"msglist_default": STYLE_MSGLIST_DEFAULT,
	"msglist_unread":  STYLE_MSGLIST_UNREAD,
	"msglist_read":    STYLE_MSGLIST_READ,
	"msglist_deleted": STYLE_MSGLIST_DELETED,
	"msglist_marked":  STYLE_MSGLIST_MARKED,
	"msglist_flagged": STYLE_MSGLIST_FLAGGED,
	"msglist_marked":  STYLE_MSGLIST_MARKED,
	"msglist_deleted": STYLE_MSGLIST_DELETED,

	"dirlist_default": STYLE_DIRLIST_DEFAULT,

@@ -180,6 +180,31 @@ func (s *Style) Set(attr, val string) error {
	return nil
}

func (s Style) composeWith(styles []*Style) Style {
	newStyle := s
	for _, st := range styles {
		if st.Fg != s.Fg {
			newStyle.Fg = st.Fg
		}
		if st.Bg != s.Bg {
			newStyle.Bg = st.Bg
		}
		if st.Bold != s.Bold {
			newStyle.Bold = st.Bold
		}
		if st.Blink != s.Blink {
			newStyle.Blink = st.Blink
		}
		if st.Underline != s.Underline {
			newStyle.Underline = st.Underline
		}
		if st.Reverse != s.Reverse {
			newStyle.Reverse = st.Reverse
		}
	}
	return newStyle
}

type StyleSet struct {
	objects  map[StyleObject]*Style
	selected map[StyleObject]*Style
@@ -213,6 +238,26 @@ func (ss StyleSet) Selected(so StyleObject) tcell.Style {
	return ss.selected[so].Get()
}

func (ss StyleSet) Compose(so StyleObject, sos []StyleObject) tcell.Style {
	base := *ss.objects[so]
	styles := make([]*Style, len(sos))
	for i, so := range sos {
		styles[i] = ss.objects[so]
	}

	return base.composeWith(styles).Get()
}

func (ss StyleSet) ComposeSelected(so StyleObject, sos []StyleObject) tcell.Style {
	base := *ss.selected[so]
	styles := make([]*Style, len(sos))
	for i, so := range sos {
		styles[i] = ss.selected[so]
	}

	return base.composeWith(styles).Get()
}

func findStyleSet(stylesetName string, stylesetsDir []string) (string, error) {
	for _, dir := range stylesetsDir {
		stylesetPath, err := homedir.Expand(path.Join(dir, stylesetName))
diff --git a/doc/aerc-stylesets.7.scd b/doc/aerc-stylesets.7.scd
index e3e7b1f..149ae17 100644
--- a/doc/aerc-stylesets.7.scd
+++ b/doc/aerc-stylesets.7.scd
@@ -107,12 +107,12 @@ styling.
:  Unread messages in a message list.
|  msglist_read
:  Read messages in a message list.
|  msglist_deleted
:  The messages marked as deleted.
|  msglist_marked
:  The messages with the marked flag.
|  msglist_flagged
:  The messages with the flagged flag.
|  msglist_marked
:  The messages with the marked flag.
|  msglist_deleted
:  The messages marked as deleted.
|  dirlist_default
:  The default style for directories in the directory list.
|  completion_default
@@ -175,6 +175,23 @@ If we specify the global style selected modifer using fnmatch as below:

This toggles the reverse switch for selected version of all the style objects.

## Layered styles
Some styles, (currently only the `msglist\*` ones) are applied in layers. If
a style differs from the base (in this case `msglist_default`) then that style
applies, unless overridden by a higher layer. The order that `msglist` styles
are applied in is, from first to last:

```
msglist_default
msglist_unread
msglist_read
msglist_flagged
msglist_marked
msglist_deleted
```

So, the deleted style will override all other msglist styles.

## Colors
The color values are set using the values accepted by the tcell library.
The values can be one of the following.
diff --git a/widgets/msglist.go b/widgets/msglist.go
index b7c10d7..1150df8 100644
--- a/widgets/msglist.go
+++ b/widgets/msglist.go
@@ -108,12 +108,7 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
			config.UI_CONTEXT_SUBJECT: msg.Envelope.Subject,
		})

		so := config.STYLE_MSGLIST_DEFAULT

		// deleted message
		if _, ok := store.Deleted[msg.Uid]; ok {
			so = config.STYLE_MSGLIST_DELETED
		}
		msg_styles := []config.StyleObject{}
		// unread message
		seen := false
		flagged := false
@@ -127,25 +122,31 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
		}

		if seen {
			so = config.STYLE_MSGLIST_READ
			msg_styles = append(msg_styles, config.STYLE_MSGLIST_READ)
		} else {
			so = config.STYLE_MSGLIST_UNREAD
			msg_styles = append(msg_styles, config.STYLE_MSGLIST_UNREAD)
		}

		if flagged {
			so = config.STYLE_MSGLIST_FLAGGED
			msg_styles = append(msg_styles, config.STYLE_MSGLIST_FLAGGED)
		}

		// marked message
		if store.IsMarked(msg.Uid) {
			so = config.STYLE_MSGLIST_MARKED
			msg_styles = append(msg_styles, config.STYLE_MSGLIST_MARKED)
		}

		style := uiConfig.GetStyle(so)
		// deleted message
		if _, ok := store.Deleted[msg.Uid]; ok {
			msg_styles = append(msg_styles, config.STYLE_MSGLIST_DELETED)
		}

		var style tcell.Style
		// current row
		if row == ml.store.SelectedIndex()-ml.scroll {
			style = uiConfig.GetStyleSelected(so)
			style = uiConfig.GetComposedStyleSelected(config.STYLE_MSGLIST_DEFAULT, msg_styles)
		} else {
			style = uiConfig.GetComposedStyle(config.STYLE_MSGLIST_DEFAULT, msg_styles)
		}

		ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
-- 
2.29.0
Details
Message ID
<20201027063918.u66qxmpf6lgeylma@feather.localdomain>
In-Reply-To
<20201026194316.990787-1-chris@vittal.dev> (view parent)
DKIM signature
pass
Download raw message
Thanks! That was indeed very quick :)
I appreciate the effort.

On Mon, Oct 26, 2020 at 03:43:16PM -0400, Chris Vittal wrote:
> Allow styles to be layered over a base style. The list of styles to
> apply is layered over the base style in order, such that if the layer
> does not differ from the base it is not used. The order that these
> styles are applied in is, from first to last:
> 
> msglist_default
> msglist_unread
> msglist_read    (exclusive with unread, so technically the same level)
> msglist_flagged
> msglist_marked
> msglist_deleted
> 
> So, deleted's style dominates.

Please flip the priority of deleted and marked.
Marked is an interactive user interface thingy... it should have priority.
Commands you do are still operating on the marked messages even if you already
executed :delete (until the confirmation from the backend comes in).

So this should be made obvious.

Cheers,
Reto
Reply to thread Export thread (mbox)