Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
diamondburned
GitHub Repository: diamondburned/gtkcord4
Path: blob/main/internal/components/hoverpopover/hoverpopover.go
366 views
1
package hoverpopover
2
3
import (
4
"github.com/diamondburned/gotk4/pkg/glib/v2"
5
"github.com/diamondburned/gotk4/pkg/gtk/v4"
6
"github.com/diamondburned/gotkit/gtkutil/cssutil"
7
)
8
9
var _ = cssutil.WriteCSS(`
10
.popover-label {
11
padding: 0 0.25em;
12
}
13
`)
14
15
// MarkupHoverPopover is a struct that represents a hover popover
16
// that is displayed when the user hovers over a widget. The popover
17
// only displays markup text. Markup texts are persisted across
18
// hovers.
19
//
20
// Popovers are lazily allocated, so they are only created when
21
// the user hovers over a widget. They are automatically destroyed
22
// a moment after the user stops hovering over the widget.
23
type MarkupHoverPopover struct {
24
hover *gtk.EventControllerMotion
25
controller *PopoverController
26
initFn func(*MarkupHoverPopoverWidget) bool
27
}
28
29
// MarkupHoverPopoverWidget is a struct that represents a popover
30
// widget that displays markup text.
31
type MarkupHoverPopoverWidget struct {
32
*gtk.Popover
33
Label *gtk.Label
34
}
35
36
var markupHoverPopoverClasses = []string{
37
"hover-popover",
38
"hover-popover-markup",
39
}
40
41
// NewMarkupHoverPopover creates a new MarkupHoverPopover.
42
func NewMarkupHoverPopover(parent gtk.Widgetter, initFn func(*MarkupHoverPopoverWidget) bool) *MarkupHoverPopover {
43
p := &MarkupHoverPopover{
44
initFn: initFn,
45
}
46
p.controller = NewPopoverController(parent, p.initPopover)
47
48
p.hover = gtk.NewEventControllerMotion()
49
50
// Implement a very primitive delay. This is to prevent the popover
51
// from popping up when the user is scrolling on a touchpad.
52
var delayID glib.SourceHandle
53
var hovered bool
54
const hoverDelay = 100 // ms
55
56
p.hover.ConnectEnter(func(_, _ float64) {
57
hovered = true
58
59
if delayID != 0 {
60
return
61
}
62
63
delayID = glib.TimeoutAdd(hoverDelay, func() {
64
delayID = 0
65
66
if hovered {
67
p.controller.Popup()
68
}
69
})
70
})
71
72
p.hover.ConnectLeave(func() {
73
hovered = false
74
75
if delayID != 0 {
76
glib.SourceRemove(delayID)
77
delayID = 0
78
return
79
}
80
81
p.controller.Popdown()
82
})
83
84
parentWidget := gtk.BaseWidget(parent)
85
parentWidget.AddController(p.hover)
86
87
var windowSignal glib.SignalHandle
88
var window *gtk.Window
89
90
attachWindow := func() {
91
if window != nil {
92
window.HandlerDisconnect(windowSignal)
93
window = nil
94
}
95
96
root := parentWidget.Root()
97
if root != nil {
98
window = root.CastType(gtk.GTypeWindow).(*gtk.Window)
99
windowSignal = window.NotifyProperty("is-active", func() {
100
if p.controller.IsPoppedUp() && !window.IsActive() {
101
p.controller.Popdown()
102
}
103
})
104
}
105
}
106
107
parentWidget.NotifyProperty("root", attachWindow)
108
attachWindow()
109
110
return p
111
}
112
113
func (p *MarkupHoverPopover) initPopover(popover *gtk.Popover) bool {
114
current := &MarkupHoverPopoverWidget{Popover: popover}
115
116
current.Label = gtk.NewLabel("")
117
current.Label.AddCSSClass("popover-label")
118
current.Label.AddCSSClass("hover-popover-label")
119
120
current.AddCSSClass("hover-popover")
121
current.AddCSSClass("hover-popover-markup")
122
current.SetAutohide(false)
123
current.SetMnemonicsVisible(false)
124
current.SetCanFocus(false)
125
current.SetCanTarget(false)
126
current.SetChild(current.Label)
127
128
return p.initFn(current)
129
}
130
131