Andrew Thorp: 1 Gray out reply list when drafting a conversation 5 files changed, 72 insertions(+), 13 deletions(-)
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 -3Learn more about email & git
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: