~whereswaldon/sprig

ref: v0.0.16 sprig/widget/composer.go -rw-r--r-- 2.5 KiB
fbfe7d32Chris Waldon fix: use concrete ds.ReplyData as target of reply methods and focus 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package widget

import (
	"gioui.org/io/clipboard"
	"gioui.org/layout"
	"gioui.org/widget"

	"git.sr.ht/~whereswaldon/sprig/ds"
	"git.sr.ht/~whereswaldon/sprig/platform"
)

// ComposerEvent represents a change in the Composer's state
type ComposerEvent uint

const (
	ComposerSubmitted ComposerEvent = iota
	ComposerCancelled
)

// Composer holds the state for a widget that creates new arbor nodes.
type Composer struct {
	CommunityList layout.List
	Community     widget.Enum

	SendButton, CancelButton, PasteButton widget.Clickable
	widget.Editor

	ReplyingTo ds.ReplyData

	events    []ComposerEvent
	composing bool
}

// update handles all state processing.
func (c *Composer) update(gtx layout.Context) {
	for _, e := range c.Editor.Events() {
		if _, ok := e.(widget.SubmitEvent); ok && !platform.Mobile {
			c.events = append(c.events, ComposerSubmitted)
		}
	}
	if c.PasteButton.Clicked() {
		clipboard.ReadOp{Tag: &c.composing}.Add(gtx.Ops)
	}
	for _, e := range gtx.Events(&c.composing) {
		switch e := e.(type) {
		case clipboard.Event:
			c.Editor.Insert(e.Text)
		}
	}
	if c.CancelButton.Clicked() {
		c.events = append(c.events, ComposerCancelled)
	}
	if c.SendButton.Clicked() {
		c.events = append(c.events, ComposerSubmitted)
	}
}

// Layout updates the state of the composer
func (c *Composer) Layout(gtx layout.Context) layout.Dimensions {
	c.update(gtx)
	return layout.Dimensions{}
}

// StartReply configures the composer to write a reply to the provided
// ReplyData.
func (c *Composer) StartReply(to ds.ReplyData) {
	c.Reset()
	c.composing = true
	c.ReplyingTo = to
	c.Editor.Focus()
}

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

// Reset clears the internal state of the composer.
func (c *Composer) Reset() {
	c.ReplyingTo.Reply = nil
	c.ReplyingTo.Author = nil
	c.ReplyingTo.Community = nil
	c.Editor.SetText("")
	c.composing = false
}

// ComposingConversation returns whether the composer is currently creating
// a conversation (rather than a new reply within an existing conversation)
func (c *Composer) ComposingConversation() bool {
	return c.ReplyingTo.Reply == nil && c.Composing()
}

// Composing indicates whether the composer is composing a message of any
// kind.
func (c Composer) Composing() bool {
	return c.composing
}

// Events returns state change events for the composer since the last call
// to events.
func (c *Composer) Events() (out []ComposerEvent) {
	out, c.events = c.events, c.events[:0]
	return
}