~whereswaldon/arbor-dev

Gray out reply list when drafting a conversation v1 PROPOSED

Andrew Thorp: 1
 Gray out reply list when drafting a conversation

 5 files changed, 72 insertions(+), 13 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~whereswaldon/arbor-dev/patches/33035/mbox | git am -3
Learn more about email & git

[WIP][PATCH] Gray out reply list when drafting a conversation Export this patch

Hey fellas,

I implemented a feature where the message lists gets grayed out a bit when
composing a conversation, as opposed to a reply. It seems to work well, however
it doesn't seem to work at all in dark mode; there doens't appear to be any
rectangle appearing at all... I've been looking through and I can't find out
why it wouldn't work in dark mode. Any advice would be appreciated.

Thanks!

Andrew Thorp

---
 reply-view.go            | 24 +++++++++++++++++++++---
 widget/composer.go       | 37 ++++++++++++++++++++++++++++---------
 widget/theme/composer.go |  2 +-
 widget/theme/theme.go    | 10 ++++++++++
 widget/theme/utils.go    | 12 ++++++++++++
 5 files changed, 72 insertions(+), 13 deletions(-)

diff --git a/reply-view.go b/reply-view.go
index 0f113b6..e3a18f1 100644
--- a/reply-view.go
+++ b/reply-view.go
@@ -165,6 +165,8 @@ type ReplyListView struct {
	maxRepliesVisible int
	// Loading if replies are loading.
	loading bool
	// Whether or not to gray-out the reply list
	hideReplies bool
}

var _ View = &ReplyListView{}
@@ -492,11 +494,13 @@ func (c *ReplyListView) copyFocused(gtx layout.Context) {
// startReply begins replying to the focused message.
func (c *ReplyListView) startReply() {
	data := c.Focused
	c.hideReplies = false
	c.Composer.StartReply(*data)
}

// sendReply sends the reply with the current contents of the editor.
func (c *ReplyListView) sendReply() {
	c.hideReplies = false
	replyText := c.Composer.Text()
	if replyText == "" {
		return
@@ -639,6 +643,7 @@ func (c *ReplyListView) processMessagePointerEvents(gtx C) {

// startConversation triggers composition of a new conversation message.
func (c *ReplyListView) startConversation() {
	c.hideReplies = true
	c.Composer.StartConversation()
}

@@ -807,6 +812,7 @@ func (c *ReplyListView) loadMoreHistory() {

// resetReplyState erases the current contents of the message composer.
func (c *ReplyListView) resetReplyState() {
	c.hideReplies = false
	c.Composer.Reset()
}

@@ -859,6 +865,7 @@ func (c *ReplyListView) shouldDisplayEditor() bool {

// hideEditor makes the editor invisible.
func (c *ReplyListView) hideEditor() {
	c.hideReplies = false
	c.Composer.Reset()
	c.requestKeyboardFocus()
}
@@ -867,6 +874,7 @@ func (c *ReplyListView) hideEditor() {
func (c *ReplyListView) Layout(gtx layout.Context) layout.Dimensions {
	theme := c.Theme().Current()
	c.ShouldRequestKeyboardFocus = false

	return layout.Stack{}.Layout(gtx,
		layout.Expanded(func(gtx C) D {
			sprigTheme.Rect{
@@ -878,7 +886,18 @@ func (c *ReplyListView) Layout(gtx layout.Context) layout.Dimensions {
		layout.Stacked(func(gtx C) D {
			return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
				layout.Flexed(1, func(gtx C) D {
					return c.layoutReplyList(gtx)
					// Cover the reply list with a semi-transparent rectangle if the
					// list is supposed to be hidden entirely
					if c.hideReplies {
						return sprigTheme.Rect{
							Color: theme.ApplyAlpha(theme.Background.Default.Bg),
							Size:  layout.FPt(gtx.Constraints.Max),
						}.LayoutOver(gtx, func(gtx C) D {
							return c.layoutReplyList(gtx)
						})
					} else {
						return c.layoutReplyList(gtx)
					}
				}),
				layout.Rigid(func(gtx C) D {
					if c.shouldDisplayEditor() {
@@ -890,8 +909,7 @@ func (c *ReplyListView) Layout(gtx layout.Context) layout.Dimensions {
					return layout.Dimensions{}
				}),
			)
		}),
	)
		}))
}

const buttonWidthDp = 20
diff --git a/widget/composer.go b/widget/composer.go
index 15bc508..07a7681 100644
--- a/widget/composer.go
+++ b/widget/composer.go
@@ -14,6 +14,14 @@ import (
// ComposerEvent represents a change in the Composer's state
type ComposerEvent uint

type MessageType int32

const (
	MessageTypeNone MessageType = iota
	MessageTypeConversation
	MessageTypeReply
)

const (
	ComposerSubmitted ComposerEvent = iota
	ComposerCancelled
@@ -21,8 +29,8 @@ const (

// Editor prompts
const (
	reply_prompt        = "Compose your reply"
	conversation_prompt = "Start a new conversation"
	replyPrompt        = "Compose your reply"
	conversationPrompt = "Start a new conversation"
)

// Composer holds the state for a widget that creates new arbor nodes.
@@ -37,10 +45,9 @@ type Composer struct {

	ReplyingTo ds.ReplyData

	events    []ComposerEvent
	composing bool

	PromptText string
	events      []ComposerEvent
	composing   bool
	messageType MessageType
}

// update handles all state processing.
@@ -79,23 +86,22 @@ func (c *Composer) StartReply(to ds.ReplyData) {
	c.Reset()
	c.composing = true
	c.ReplyingTo = to
	c.PromptText = reply_prompt
	c.Editor.Focus()
}

// StartConversation configures the composer to write a new conversation.
func (c *Composer) StartConversation() {
	c.Reset()
	c.messageType = MessageTypeConversation
	c.composing = true
	c.PromptText = conversation_prompt
	c.Editor.Focus()
}

// Reset clears the internal state of the composer.
func (c *Composer) Reset() {
	c.messageType = MessageTypeNone
	c.ReplyingTo = ds.ReplyData{}
	c.Editor.SetText("")
	c.PromptText = ""
	c.composing = false
}

@@ -111,6 +117,19 @@ func (c Composer) Composing() bool {
	return c.composing
}

// PromptText returns the text prompt for the composer, based off of the message type
func (c Composer) PromptText() string {
	if c.messageType == MessageTypeConversation {
		return conversationPrompt
	} else {
		return replyPrompt
	}
}

func (c Composer) MessageType() MessageType {
	return c.messageType
}

// Events returns state change events for the composer since the last call
// to events.
func (c *Composer) Events() (out []ComposerEvent) {
diff --git a/widget/theme/composer.go b/widget/theme/composer.go
index ce5ad2b..eee1235 100644
--- a/widget/theme/composer.go
+++ b/widget/theme/composer.go
@@ -113,7 +113,7 @@ func (c ComposerStyle) Layout(gtx layout.Context) layout.Dimensions {
									layout.Stacked(func(gtx C) D {
										return layout.UniformInset(unit.Dp(6)).Layout(gtx, func(gtx C) D {
											c.Editor.Submit = true
											return material.Editor(th.Theme, &c.Editor, c.PromptText).Layout(gtx)
											return material.Editor(th.Theme, &c.Editor, c.PromptText()).Layout(gtx)
										})
									}),
								)
diff --git a/widget/theme/theme.go b/widget/theme/theme.go
index 6c5054b..f3158b4 100644
--- a/widget/theme/theme.go
+++ b/widget/theme/theme.go
@@ -82,6 +82,9 @@ func New() *Theme {
	t.Selected = &t.Secondary.Light.Bg
	t.Unselected = &t.Background.Light.Bg
	t.Siblings = t.Unselected

	t.FadeAlpha = 128

	return &t
}

@@ -131,5 +134,12 @@ type Theme struct {
	Secondary  Swatch
	Background Swatch

	FadeAlpha uint8

	Ancestors, Descendants, Selected, Siblings, Unselected *color.NRGBA
}

func (t *Theme) ApplyAlpha(c color.NRGBA) color.NRGBA {
	c.A = t.FadeAlpha
	return c
}
diff --git a/widget/theme/utils.go b/widget/theme/utils.go
index 3178d13..e844d38 100644
--- a/widget/theme/utils.go
+++ b/widget/theme/utils.go
@@ -36,3 +36,15 @@ func (r Rect) LayoutUnder(gtx C, w layout.Widget) D {
		layout.Stacked(w),
	)
}

// LayoutUnder ignores the Size field and lays the rectangle out beneath the
// provided widget, matching its dimensions.
func (r Rect) LayoutOver(gtx C, w layout.Widget) D {
	return layout.Stack{}.Layout(gtx,
		layout.Stacked(w),
		layout.Expanded(func(gtx C) D {
			r.Size = layout.FPt(gtx.Constraints.Min)
			return r.Layout(gtx)
		}),
	)
}
-- 
2.17.1
I believe I've found the issue. I was using 
`theme.Background.Default.Bg` when
I should have been using `c.Theme().Current().Bg`. I'll have the patch 
ready soon.

Cheers,

Andrew Thorp

On Wed, Jun 15, 2022 at 11:12 PM, Andrew Thorp 
<andrew.thorp.dev@gmail.com> wrote:
Hi Andrew,

Thanks for working on this!

You may wish to use https://pkg.go.dev/gioui.org/x/component#Scrim for
this. It is designed to block input events and obscure content, and
has animation baked into it already. You can trigger it by invoking
methods on the embedded VisibilityAnimation.

Cheers,
Chris

On Wed, Jun 15, 2022 at 11:12 PM Andrew Thorp
<andrew.thorp.dev@gmail.com> wrote: