• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

fyne-io / fyne / 13156141055

05 Feb 2025 11:14AM UTC coverage: 62.563% (-0.06%) from 62.619%
13156141055

Pull #5496

github

dweymouth
address code style comments
Pull Request #5496: Make table scrolling and resizing more efficient

38 of 38 new or added lines in 1 file covered. (100.0%)

266 existing lines in 11 files now uncovered.

24755 of 39568 relevant lines covered (62.56%)

841.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.69
/container/split.go
1
package container
2

3
import (
4
        "fyne.io/fyne/v2"
5
        "fyne.io/fyne/v2/canvas"
6
        "fyne.io/fyne/v2/driver/desktop"
7
        "fyne.io/fyne/v2/theme"
8
        "fyne.io/fyne/v2/widget"
9
)
10

11
// Declare conformity with CanvasObject interface
12
var _ fyne.CanvasObject = (*Split)(nil)
13

14
// Split defines a container whose size is split between two children.
15
//
16
// Since: 1.4
17
type Split struct {
18
        widget.BaseWidget
19
        Offset     float64
20
        Horizontal bool
21
        Leading    fyne.CanvasObject
22
        Trailing   fyne.CanvasObject
23
}
24

25
// NewHSplit creates a horizontally arranged container with the specified leading and trailing elements.
26
// A vertical split bar that can be dragged will be added between the elements.
27
//
28
// Since: 1.4
29
func NewHSplit(leading, trailing fyne.CanvasObject) *Split {
30
        return newSplitContainer(true, leading, trailing)
31
}
32

33
// NewVSplit creates a vertically arranged container with the specified top and bottom elements.
34
// A horizontal split bar that can be dragged will be added between the elements.
15✔
35
//
15✔
36
// Since: 1.4
15✔
37
func NewVSplit(top, bottom fyne.CanvasObject) *Split {
38
        return newSplitContainer(false, top, bottom)
39
}
40

41
func newSplitContainer(horizontal bool, leading, trailing fyne.CanvasObject) *Split {
42
        s := &Split{
11✔
43
                Offset:     0.5, // Sensible default, can be overridden with SetOffset
11✔
44
                Horizontal: horizontal,
11✔
45
                Leading:    leading,
46
                Trailing:   trailing,
26✔
47
        }
26✔
48
        s.BaseWidget.ExtendBaseWidget(s)
26✔
49
        return s
26✔
50
}
26✔
51

26✔
52
// CreateRenderer is a private method to Fyne which links this widget to its renderer
26✔
53
func (s *Split) CreateRenderer() fyne.WidgetRenderer {
26✔
54
        s.BaseWidget.ExtendBaseWidget(s)
26✔
55
        d := newDivider(s)
26✔
56
        return &splitContainerRenderer{
57
                split:   s,
58
                divider: d,
26✔
59
                objects: []fyne.CanvasObject{s.Leading, d, s.Trailing},
26✔
60
        }
26✔
61
}
26✔
62

26✔
63
// ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
26✔
64
//
26✔
65
// Deprecated: Support for extending containers is being removed
26✔
66
func (s *Split) ExtendBaseWidget(wid fyne.Widget) {
26✔
67
        s.BaseWidget.ExtendBaseWidget(wid)
68
}
69

70
// SetOffset sets the offset (0.0 to 1.0) of the Split divider.
UNCOV
71
// 0.0 - Leading is min size, Trailing uses all remaining space.
×
UNCOV
72
// 0.5 - Leading & Trailing equally share the available space.
×
UNCOV
73
// 1.0 - Trailing is min size, Leading uses all remaining space.
×
74
func (s *Split) SetOffset(offset float64) {
75
        if s.Offset == offset {
76
                return
77
        }
78
        s.Offset = offset
79
        s.Refresh()
29✔
80
}
31✔
81

2✔
82
var _ fyne.WidgetRenderer = (*splitContainerRenderer)(nil)
2✔
83

27✔
84
type splitContainerRenderer struct {
27✔
85
        split   *Split
27✔
86
        divider *divider
87
        objects []fyne.CanvasObject
88
}
89

90
func (r *splitContainerRenderer) Destroy() {
91
}
92

93
func (r *splitContainerRenderer) Layout(size fyne.Size) {
94
        var dividerPos, leadingPos, trailingPos fyne.Position
95
        var dividerSize, leadingSize, trailingSize fyne.Size
96

4✔
97
        if r.split.Horizontal {
4✔
98
                lw, tw := r.computeSplitLengths(size.Width, r.minLeadingWidth(), r.minTrailingWidth())
99
                leadingPos.X = 0
58✔
100
                leadingSize.Width = lw
58✔
101
                leadingSize.Height = size.Height
58✔
102
                dividerPos.X = lw
58✔
103
                dividerSize.Width = dividerThickness(r.divider)
93✔
104
                dividerSize.Height = size.Height
35✔
105
                trailingPos.X = lw + dividerSize.Width
35✔
106
                trailingSize.Width = tw
35✔
107
                trailingSize.Height = size.Height
35✔
108
        } else {
35✔
109
                lh, th := r.computeSplitLengths(size.Height, r.minLeadingHeight(), r.minTrailingHeight())
35✔
110
                leadingPos.Y = 0
35✔
111
                leadingSize.Width = size.Width
35✔
112
                leadingSize.Height = lh
35✔
113
                dividerPos.Y = lh
35✔
114
                dividerSize.Width = size.Width
58✔
115
                dividerSize.Height = dividerThickness(r.divider)
23✔
116
                trailingPos.Y = lh + dividerSize.Height
23✔
117
                trailingSize.Width = size.Width
23✔
118
                trailingSize.Height = th
23✔
119
        }
23✔
120

23✔
121
        r.divider.Move(dividerPos)
23✔
122
        r.divider.Resize(dividerSize)
23✔
123
        r.split.Leading.Move(leadingPos)
23✔
124
        r.split.Leading.Resize(leadingSize)
23✔
125
        r.split.Trailing.Move(trailingPos)
23✔
126
        r.split.Trailing.Resize(trailingSize)
127
        canvas.Refresh(r.divider)
58✔
128
}
58✔
129

58✔
130
func (r *splitContainerRenderer) MinSize() fyne.Size {
58✔
131
        s := fyne.NewSize(0, 0)
58✔
132
        for _, o := range r.objects {
58✔
133
                min := o.MinSize()
58✔
134
                if r.split.Horizontal {
135
                        s.Width += min.Width
136
                        s.Height = fyne.Max(s.Height, min.Height)
8✔
137
                } else {
8✔
138
                        s.Width = fyne.Max(s.Width, min.Width)
32✔
139
                        s.Height += min.Height
24✔
140
                }
45✔
141
        }
21✔
142
        return s
21✔
143
}
24✔
144

3✔
145
func (r *splitContainerRenderer) Objects() []fyne.CanvasObject {
3✔
146
        return r.objects
3✔
147
}
148

8✔
149
func (r *splitContainerRenderer) Refresh() {
150
        r.objects[0] = r.split.Leading
151
        // [1] is divider which doesn't change
6✔
152
        r.objects[2] = r.split.Trailing
6✔
153
        r.Layout(r.split.Size())
6✔
154

155
        r.split.Leading.Refresh()
32✔
156
        r.divider.Refresh()
59✔
157
        r.split.Trailing.Refresh()
27✔
158
        canvas.Refresh(r.split)
27✔
159
}
27✔
160

27✔
161
func (r *splitContainerRenderer) computeSplitLengths(total, lMin, tMin float32) (float32, float32) {
162
        available := float64(total - dividerThickness(r.divider))
5✔
163
        if available <= 0 {
5✔
164
                return 0, 0
5✔
165
        }
5✔
166
        ld := float64(lMin)
5✔
167
        tr := float64(tMin)
5✔
168
        offset := r.split.Offset
5✔
169

5✔
170
        min := ld / available
5✔
171
        max := 1 - tr/available
172
        if min <= max {
173
                if offset < min {
58✔
174
                        offset = min
58✔
175
                }
66✔
176
                if offset > max {
8✔
177
                        offset = max
8✔
178
                }
50✔
179
        } else {
50✔
180
                offset = ld / (ld + tr)
50✔
181
        }
50✔
182

50✔
183
        ld = offset * available
50✔
184
        tr = available - ld
98✔
185
        return float32(ld), float32(tr)
52✔
186
}
4✔
187

4✔
188
func (r *splitContainerRenderer) minLeadingWidth() float32 {
52✔
189
        if r.split.Leading.Visible() {
4✔
190
                return r.split.Leading.MinSize().Width
4✔
191
        }
2✔
192
        return 0
2✔
193
}
2✔
194

195
func (r *splitContainerRenderer) minLeadingHeight() float32 {
50✔
196
        if r.split.Leading.Visible() {
50✔
197
                return r.split.Leading.MinSize().Height
50✔
198
        }
199
        return 0
200
}
35✔
201

67✔
202
func (r *splitContainerRenderer) minTrailingWidth() float32 {
32✔
203
        if r.split.Trailing.Visible() {
32✔
204
                return r.split.Trailing.MinSize().Width
3✔
205
        }
206
        return 0
207
}
23✔
208

42✔
209
func (r *splitContainerRenderer) minTrailingHeight() float32 {
19✔
210
        if r.split.Trailing.Visible() {
19✔
211
                return r.split.Trailing.MinSize().Height
4✔
212
        }
213
        return 0
214
}
35✔
215

69✔
216
// Declare conformity with interfaces
34✔
217
var _ fyne.CanvasObject = (*divider)(nil)
34✔
218
var _ fyne.Draggable = (*divider)(nil)
1✔
219
var _ desktop.Cursorable = (*divider)(nil)
220
var _ desktop.Hoverable = (*divider)(nil)
221

23✔
222
type divider struct {
42✔
223
        widget.BaseWidget
19✔
224
        split          *Split
19✔
225
        hovered        bool
4✔
226
        startDragOff   *fyne.Position
227
        currentDragPos fyne.Position
228
}
229

230
func newDivider(split *Split) *divider {
231
        d := &divider{
232
                split: split,
233
        }
234
        d.ExtendBaseWidget(d)
235
        return d
236
}
237

238
// CreateRenderer is a private method to Fyne which links this widget to its renderer
239
func (d *divider) CreateRenderer() fyne.WidgetRenderer {
240
        d.ExtendBaseWidget(d)
241
        th := d.Theme()
242
        v := fyne.CurrentApp().Settings().ThemeVariant()
32✔
243

32✔
244
        background := canvas.NewRectangle(th.Color(theme.ColorNameShadow, v))
32✔
245
        foreground := canvas.NewRectangle(th.Color(theme.ColorNameForeground, v))
32✔
246
        return &dividerRenderer{
32✔
247
                divider:    d,
32✔
248
                background: background,
32✔
249
                foreground: foreground,
250
                objects:    []fyne.CanvasObject{background, foreground},
251
        }
28✔
252
}
28✔
253

28✔
254
func (d *divider) Cursor() desktop.Cursor {
28✔
255
        if d.split.Horizontal {
28✔
256
                return desktop.HResizeCursor
28✔
257
        }
28✔
258
        return desktop.VResizeCursor
28✔
259
}
28✔
260

28✔
261
func (d *divider) DragEnd() {
28✔
262
        d.startDragOff = nil
28✔
263
}
28✔
264

28✔
265
func (d *divider) Dragged(e *fyne.DragEvent) {
266
        if d.startDragOff == nil {
2✔
267
                d.currentDragPos = d.Position().Add(e.Position)
3✔
268
                start := e.Position.Subtract(e.Dragged)
1✔
269
                d.startDragOff = &start
1✔
270
        } else {
1✔
271
                d.currentDragPos = d.currentDragPos.Add(e.Dragged)
272
        }
273

8✔
274
        x, y := d.currentDragPos.Components()
8✔
275
        var offset, leadingRatio, trailingRatio float64
8✔
276
        if d.split.Horizontal {
277
                widthFree := float64(d.split.Size().Width - dividerThickness(d))
12✔
278
                leadingRatio = float64(d.split.Leading.MinSize().Width) / widthFree
20✔
279
                trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Width) / widthFree)
8✔
280
                offset = float64(x-d.startDragOff.X) / widthFree
8✔
281
        } else {
8✔
282
                heightFree := float64(d.split.Size().Height - dividerThickness(d))
12✔
283
                leadingRatio = float64(d.split.Leading.MinSize().Height) / heightFree
4✔
284
                trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Height) / heightFree)
4✔
285
                offset = float64(y-d.startDragOff.Y) / heightFree
286
        }
12✔
287

12✔
288
        if offset < leadingRatio {
18✔
289
                offset = leadingRatio
6✔
290
        }
6✔
291
        if offset > trailingRatio {
6✔
292
                offset = trailingRatio
6✔
293
        }
12✔
294
        d.split.SetOffset(offset)
6✔
295
}
6✔
296

6✔
297
func (d *divider) MouseIn(event *desktop.MouseEvent) {
6✔
298
        d.hovered = true
6✔
299
        d.split.Refresh()
300
}
12✔
UNCOV
301

×
302
func (d *divider) MouseMoved(event *desktop.MouseEvent) {}
×
303

16✔
304
func (d *divider) MouseOut() {
4✔
305
        d.hovered = false
4✔
306
        d.split.Refresh()
12✔
307
}
308

309
var _ fyne.WidgetRenderer = (*dividerRenderer)(nil)
2✔
310

2✔
311
type dividerRenderer struct {
2✔
312
        divider    *divider
2✔
313
        background *canvas.Rectangle
UNCOV
314
        foreground *canvas.Rectangle
×
315
        objects    []fyne.CanvasObject
316
}
2✔
317

2✔
318
func (r *dividerRenderer) Destroy() {
2✔
319
}
2✔
320

321
func (r *dividerRenderer) Layout(size fyne.Size) {
322
        r.background.Resize(size)
323
        var x, y, w, h float32
324
        if r.divider.split.Horizontal {
325
                x = (dividerThickness(r.divider) - handleThickness(r.divider)) / 2
326
                y = (size.Height - handleLength(r.divider)) / 2
327
                w = handleThickness(r.divider)
328
                h = handleLength(r.divider)
329
        } else {
UNCOV
330
                x = (size.Width - handleLength(r.divider)) / 2
×
UNCOV
331
                y = (dividerThickness(r.divider) - handleThickness(r.divider)) / 2
×
332
                w = handleLength(r.divider)
333
                h = handleThickness(r.divider)
39✔
334
        }
39✔
335
        r.foreground.Move(fyne.NewPos(x, y))
39✔
336
        r.foreground.Resize(fyne.NewSize(w, h))
67✔
337
}
28✔
338

28✔
339
func (r *dividerRenderer) MinSize() fyne.Size {
28✔
340
        if r.divider.split.Horizontal {
28✔
341
                return fyne.NewSize(dividerThickness(r.divider), dividerLength(r.divider))
39✔
342
        }
11✔
343
        return fyne.NewSize(dividerLength(r.divider), dividerThickness(r.divider))
11✔
344
}
11✔
345

11✔
346
func (r *dividerRenderer) Objects() []fyne.CanvasObject {
11✔
347
        return r.objects
39✔
348
}
39✔
349

350
func (r *dividerRenderer) Refresh() {
351
        th := r.divider.Theme()
10✔
352
        v := fyne.CurrentApp().Settings().ThemeVariant()
18✔
353

8✔
354
        if r.divider.hovered {
8✔
355
                r.background.FillColor = th.Color(theme.ColorNameHover, v)
2✔
356
        } else {
357
                r.background.FillColor = th.Color(theme.ColorNameShadow, v)
358
        }
6✔
359
        r.background.Refresh()
6✔
360
        r.foreground.FillColor = th.Color(theme.ColorNameForeground, v)
6✔
361
        r.foreground.Refresh()
362
        r.Layout(r.divider.Size())
6✔
363
}
6✔
364

6✔
365
func dividerTheme(d *divider) fyne.Theme {
6✔
366
        if d == nil {
6✔
UNCOV
367
                return theme.Current()
×
368
        }
6✔
369

6✔
370
        return d.Theme()
6✔
371
}
6✔
372

6✔
373
func dividerThickness(d *divider) float32 {
6✔
374
        th := dividerTheme(d)
6✔
375
        return th.Size(theme.SizeNamePadding) * 2
376
}
377

381✔
378
func dividerLength(d *divider) float32 {
419✔
379
        th := dividerTheme(d)
38✔
380
        return th.Size(theme.SizeNamePadding) * 6
38✔
381
}
382

343✔
383
func handleThickness(d *divider) float32 {
384
        th := dividerTheme(d)
385
        return th.Size(theme.SizeNamePadding) / 2
210✔
386

210✔
387
}
210✔
388

210✔
389
func handleLength(d *divider) float32 {
390
        th := dividerTheme(d)
15✔
391
        return th.Size(theme.SizeNamePadding) * 4
15✔
392
}
15✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc