popup_surface.go RawHistoryFolder
1package cef
2
3import (
4 "context"
5 "sync/atomic"
6
7 purecef "github.com/bnema/purego-cef/cef"
8 cef2gtk "github.com/bnema/purego-cef2gtk"
9 "github.com/bnema/puregotk/v4/glib"
10 "github.com/bnema/puregotk/v4/gtk"
11
12 "github.com/bnema/dumber/internal/logging"
13)
14
15type popupBridgeSurface struct {
16 ctx context.Context
17 root *gtk.Widget
18 mainWidget *gtk.Widget
19 bridge *Cef2gtkAdapter
20 popupWidget *gtk.Widget
21 prepared atomic.Uint32
22}
23
24// newPopupBridgeSurface must be called on the GTK thread because it creates
25// and packs GTK widgets around the primary CEF widget.
26func newPopupBridgeSurface(ctx context.Context, mainWidget *gtk.Widget, plan cef2gtk.RenderStackPlan) *popupBridgeSurface {
27 mainContext := glib.MainContextDefault()
28 if mainContext == nil || !mainContext.IsOwner() {
29 logging.FromContext(popupSurfaceLogContext(ctx)).Error().Msg("cef: newPopupBridgeSurface called off GTK thread")
30 return nil
31 }
32 if mainWidget == nil {
33 return nil
34 }
35 bridge := NewCef2gtkAdapter(plan)
36 if bridge == nil {
37 return nil
38 }
39 popupWidget := bridge.Widget()
40 if popupWidget == nil {
41 _ = bridge.Destroy()
42 return nil
43 }
44 overlay := gtk.NewOverlay()
45 if overlay == nil {
46 _ = bridge.Destroy()
47 return nil
48 }
49 overlay.SetChild(mainWidget)
50 popupWidget.SetVisible(false)
51 popupWidget.SetHalign(gtk.AlignStartValue)
52 popupWidget.SetValign(gtk.AlignStartValue)
53 popupWidget.SetHexpand(false)
54 popupWidget.SetVexpand(false)
55 popupWidget.SetCanTarget(false)
56 popupWidget.SetCanFocus(false)
57 popupWidget.SetFocusable(false)
58 overlay.AddOverlay(popupWidget)
59 overlay.SetClipOverlay(popupWidget, true)
60 overlay.SetMeasureOverlay(popupWidget, false)
61 root := &overlay.Widget
62 root.SetHexpand(true)
63 root.SetVexpand(true)
64 root.SetCanFocus(true)
65 root.SetFocusable(true)
66 root.SetFocusOnClick(true)
67 return &popupBridgeSurface{
68 ctx: ctx,
69 root: root,
70 mainWidget: mainWidget,
71 bridge: bridge,
72 popupWidget: popupWidget,
73 }
74}
75
76func (s *popupBridgeSurface) RootWidget() *gtk.Widget {
77 if s == nil {
78 return nil
79 }
80 return s.root
81}
82
83func (s *popupBridgeSurface) RenderHandler(hooks cef2gtk.Hooks) purecef.RenderHandler {
84 if s == nil || s.bridge == nil {
85 return nil
86 }
87 return s.bridge.RenderHandler(hooks)
88}
89
90func (s *popupBridgeSurface) PrepareOnGTKThread() error {
91 if s == nil || s.bridge == nil {
92 return nil
93 }
94 if !s.prepared.CompareAndSwap(0, 1) {
95 return nil
96 }
97 if err := s.bridge.PrepareOnGTKThread(); err != nil {
98 s.prepared.Store(0)
99 return err
100 }
101 return nil
102}
103
104func (s *popupBridgeSurface) SetPopupVisible(visible bool) {
105 if s == nil || s.popupWidget == nil {
106 return
107 }
108 if visible {
109 if err := s.PrepareOnGTKThread(); err != nil {
110 logging.FromContext(popupSurfaceLogContext(s.ctx)).Error().
111 Err(err).
112 Msg("cef: popupBridgeSurface.SetPopupVisible failed to PrepareOnGTKThread")
113 return
114 }
115 }
116 s.popupWidget.SetVisible(visible)
117 if s.root != nil {
118 s.root.QueueAllocate()
119 }
120}
121
122func (s *popupBridgeSurface) SetPopupRect(rect purecef.Rect) {
123 if s == nil || s.popupWidget == nil {
124 return
125 }
126 if rect.Width <= 0 || rect.Height <= 0 {
127 s.popupWidget.SetVisible(false)
128 return
129 }
130 s.popupWidget.SetMarginStart(int(rect.X))
131 s.popupWidget.SetMarginTop(int(rect.Y))
132 s.popupWidget.SetSizeRequest(int(rect.Width), int(rect.Height))
133 s.popupWidget.QueueResize()
134 if s.root != nil {
135 s.root.QueueResize()
136 }
137}
138
139func popupSurfaceLogContext(ctx context.Context) context.Context {
140 if ctx != nil {
141 return ctx
142 }
143 return context.Background()
144}
145
146func (s *popupBridgeSurface) DestroyOnGTKThread() {
147 if s == nil || s.bridge == nil {
148 return
149 }
150 _ = s.bridge.Destroy()
151 s.bridge = nil
152 s.popupWidget = nil
153 s.mainWidget = nil
154 s.root = nil
155 s.prepared.Store(0)
156}