~whereswaldon/sprig

ref: v0.0.16 sprig/widget/message-list.go -rw-r--r-- 2.4 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
package widget

import (
	"gioui.org/layout"
	"git.sr.ht/~whereswaldon/forest-go"
	"git.sr.ht/~whereswaldon/forest-go/fields"
	"git.sr.ht/~whereswaldon/sprig/anim"
	"git.sr.ht/~whereswaldon/sprig/ds"
)

type MessageList struct {
	layout.List
	States
	ShouldHide     func(reply ds.ReplyData) bool
	StatusOf       func(reply ds.ReplyData) ReplyStatus
	HiddenChildren func(reply ds.ReplyData) int
	UserIsActive   func(identity *fields.QualifiedHash) bool
	Animation
}

// States implements a buffer of reply states such that memory
// is reused each frame, yet grows as the view expands to hold more replies.
type States struct {
	Buffer  []Reply
	Current int
}

// Begin resets the buffer to the start.
func (s *States) Begin() {
	s.Current = 0
}

func (s *States) Next() *Reply {
	defer func() { s.Current++ }()
	if s.Current > len(s.Buffer)-1 {
		s.Buffer = append(s.Buffer, Reply{})
	}
	return &s.Buffer[s.Current]
}

// Animation maintains animation states per reply.
type Animation struct {
	anim.Normal
	animationInit bool
	Collection    map[*forest.Reply]*ReplyAnimationState
}

func (a *Animation) init() {
	a.Collection = make(map[*forest.Reply]*ReplyAnimationState)
	a.animationInit = true
}

// Lookup animation state for the given reply.
// If state doesn't exist, it will be created with using `s` as the
// beginning status.
func (a *Animation) Lookup(r *forest.Reply, s ReplyStatus) *ReplyAnimationState {
	if !a.animationInit {
		a.init()
	}
	_, ok := a.Collection[r]
	if !ok {
		a.Collection[r] = &ReplyAnimationState{
			Normal: &a.Normal,
			Begin:  s,
		}
	}
	return a.Collection[r]
}

// Update animation state for the given reply.
func (a *Animation) Update(gtx layout.Context, r *forest.Reply, s ReplyStatus) *ReplyAnimationState {
	anim := a.Lookup(r, s)
	if a.Animating(gtx) {
		anim.End = s
	} else {
		anim.Begin = s
		anim.End = s
	}
	return anim
}

type ReplyStatus int

const (
	None ReplyStatus = 1 << iota
	Sibling
	Selected
	Ancestor
	Descendant
	ConversationRoot
	// Anchor indicates that this node is visible, but its descendants have been
	// hidden.
	Anchor
	// Hidden indicates that this node is not currently visible.
	Hidden
)

// ReplyAnimationState holds the state of an in-progress animation for a reply.
// The anim.Normal field defines how far through the animation the node is, and
// the Begin and End fields define the two states that the node is transitioning
// between.
type ReplyAnimationState struct {
	*anim.Normal
	Begin, End ReplyStatus
}