Skip to content

Commit 38ebd5d

Browse files
author
rj
committed
Merge.
2 parents f8eaa0a + 03ce748 commit 38ebd5d

17 files changed

+297
-215
lines changed

align.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -87,18 +87,7 @@ func (*alignElement) Kind() *base.Kind {
8787
}
8888

8989
func (w *alignElement) Layout(bc base.Constraints) base.Size {
90-
if w.child == nil {
91-
size := base.Size{}
92-
if bc.HasBoundedWidth() {
93-
size.Width = base.Inf
94-
}
95-
if bc.HasBoundedHeight() {
96-
size.Height = base.Inf
97-
}
98-
return bc.Constrain(size)
99-
}
100-
101-
size := w.child.Layout(bc.Loosen())
90+
size := base.Layout(w.child, bc.Loosen())
10291
w.childSize = size
10392
if w.widthFactor > 0 {
10493
size.Width = base.Length(float64(size.Width) * w.widthFactor)

base/constraints.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package base
22

33
const (
4-
// Inf is a sentinel value indicating an unbounded width or height.
4+
// Inf is a sentinel value indicating an unbounded (or infinite) length.
55
Inf Length = 0x7fffffff
66
)
77

@@ -30,31 +30,32 @@ func max(a, b Length) Length {
3030
//
3131
// A sentinel value can be used to indicate that the maximum size for a
3232
// dimension is infinite. The constraints on that dimension are called
33-
// 'unbounded'. If the constraints on a dimension are both tight and unbounded,
34-
// the dimension is 'expanding'.
33+
// 'unbounded'.
3534
//
3635
// (This type is similar to BoxConstraints type in flutter library rendering)
3736
type Constraints struct {
3837
Min, Max Size
3938
}
4039

41-
// Expand creates box constraints that force elements to expand to as large as
42-
// possible. The constraints for both width and height will be tight and
40+
// Expand creates box constraints that allows elements to expand to as large as
41+
// possible. The constraints for both width and height will be loose and
4342
// unbounded.
4443
func Expand() Constraints {
45-
return Constraints{Size{Inf, Inf}, Size{Inf, Inf}}
44+
return Constraints{Size{0, 0}, Size{Inf, Inf}}
4645
}
4746

4847
// ExpandHeight creates box constraints with a fixed width and that forces
49-
// elements to expand to as high as possible.
48+
// elements to expand to as high as possible. The constraint for width will
49+
// be tight. The constraint for height will be loose and unbounded.
5050
func ExpandHeight(width Length) Constraints {
51-
return Constraints{Size{width, Inf}, Size{width, Inf}}
51+
return Constraints{Size{width, 0}, Size{width, Inf}}
5252
}
5353

5454
// ExpandWidth creates box constraints with a fixed height and that forces
55-
// elements to expand to as wide as possible.
55+
// elements to expand to as wide as possible. The constraint for width will
56+
// be loose and unbounded. The constraint for height will be tight.
5657
func ExpandWidth(height Length) Constraints {
57-
return Constraints{Size{Inf, height}, Size{Inf, height}}
58+
return Constraints{Size{0, height}, Size{Inf, height}}
5859
}
5960

6061
// Loose creates box constraints that forbid sizes larger than the given size.
@@ -198,12 +199,15 @@ func (bc Constraints) IsNormalized() bool {
198199
}
199200

200201
// IsSatisfiedBy returns true if the passed size satisfies the both the width
201-
// and height constraints.
202+
// and height constraints. Additionally, both width and height must be finite
203+
// (i.e. not equal to the sentinal value Inf).
202204
func (bc Constraints) IsSatisfiedBy(size Size) bool {
203205
return bc.Min.Width <= size.Width &&
204206
size.Width <= bc.Max.Width &&
205207
bc.Min.Height <= size.Height &&
206-
size.Height <= bc.Max.Height
208+
size.Height <= bc.Max.Height &&
209+
size.Width != Inf &&
210+
size.Height != Inf
207211
}
208212

209213
// IsTight returns true if both the width and height are tightly constrained.

base/constraints_test.go

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ func TestConstraints(t *testing.T) {
1010
isNormalized, isTight, hasTightWidth, hasTightHeight bool
1111
isBounded, hasBoundedWidth, hasBoundedHeight bool
1212
}{
13-
{Expand(), true, true, true, true, false, false, false},
14-
{ExpandHeight(10 * DIP), true, true, true, true, false, true, false},
15-
{ExpandWidth(10 * DIP), true, true, true, true, false, false, true},
13+
{Expand(), true, false, false, false, false, false, false},
14+
{ExpandHeight(10 * DIP), true, false, true, false, false, true, false},
15+
{ExpandWidth(10 * DIP), true, false, false, true, false, false, true},
1616
{Loose(Size{10 * DIP, 15 * DIP}), true, false, false, false, true, true, true},
1717
{Tight(Size{10 * DIP, 15 * DIP}), true, true, true, true, true, true, true},
1818
{TightWidth(10 * DIP), true, false, true, false, false, true, false},
@@ -202,21 +202,23 @@ func TestConstraints_IsSatisfiedBy(t *testing.T) {
202202
}
203203

204204
func TestConstraints_Tighten(t *testing.T) {
205+
size1 := Size{10 * DIP, 10 * DIP}
206+
205207
cases := []struct {
206208
in Constraints
207209
size Size
208210
out Constraints
209211
outH Constraints
210212
outV Constraints
211213
}{
212-
{Expand(), Size{10 * DIP, 10 * DIP}, Expand(), Expand(), Expand()},
213-
{ExpandHeight(10 * DIP), Size{10 * DIP, 10 * DIP}, ExpandHeight(10 * DIP), ExpandHeight(10 * DIP), ExpandHeight(10 * DIP)},
214-
{ExpandWidth(10 * DIP), Size{10 * DIP, 10 * DIP}, ExpandWidth(10 * DIP), ExpandWidth(10 * DIP), ExpandWidth(10 * DIP)},
215-
{Loose(Size{20 * DIP, 25 * DIP}), Size{10 * DIP, 10 * DIP}, Tight(Size{10 * DIP, 10 * DIP}), Constraints{Size{0, 10 * DIP}, Size{20 * DIP, 10 * DIP}}, Constraints{Size{10 * DIP, 0}, Size{10 * DIP, 25 * DIP}}},
214+
{Expand(), size1, Tight(size1), ExpandWidth(size1.Height), ExpandHeight(size1.Width)},
215+
{ExpandHeight(10 * DIP), size1, Tight(size1), Tight(size1), ExpandHeight(10 * DIP)},
216+
{ExpandWidth(10 * DIP), size1, Tight(size1), ExpandWidth(10 * DIP), Tight(size1)},
217+
{Loose(Size{20 * DIP, 25 * DIP}), size1, Tight(size1), Constraints{Size{0, 10 * DIP}, Size{20 * DIP, 10 * DIP}}, Constraints{Size{10 * DIP, 0}, Size{10 * DIP, 25 * DIP}}},
216218
{Loose(Size{20 * DIP, 25 * DIP}), Size{30 * DIP, 30 * DIP}, Tight(Size{20 * DIP, 25 * DIP}), Constraints{Size{0, 25 * DIP}, Size{20 * DIP, 25 * DIP}}, Constraints{Size{20 * DIP, 0}, Size{20 * DIP, 25 * DIP}}},
217-
{Tight(Size{10 * DIP, 15 * DIP}), Size{10 * DIP, 10 * DIP}, Tight(Size{10 * DIP, 15 * DIP}), Tight(Size{10 * DIP, 15 * DIP}), Tight(Size{10 * DIP, 15 * DIP})},
218-
{TightWidth(15 * DIP), Size{10 * DIP, 10 * DIP}, Tight(Size{15 * DIP, 10 * DIP}), Tight(Size{15 * DIP, 10 * DIP}), TightWidth(15 * DIP)},
219-
{TightHeight(15 * DIP), Size{10 * DIP, 10 * DIP}, Tight(Size{10 * DIP, 15 * DIP}), TightHeight(15 * DIP), Tight(Size{10 * DIP, 15 * DIP})},
219+
{Tight(Size{10 * DIP, 15 * DIP}), size1, Tight(Size{10 * DIP, 15 * DIP}), Tight(Size{10 * DIP, 15 * DIP}), Tight(Size{10 * DIP, 15 * DIP})},
220+
{TightWidth(15 * DIP), size1, Tight(Size{15 * DIP, 10 * DIP}), Tight(Size{15 * DIP, 10 * DIP}), TightWidth(15 * DIP)},
221+
{TightHeight(15 * DIP), size1, Tight(Size{10 * DIP, 15 * DIP}), TightHeight(15 * DIP), Tight(Size{10 * DIP, 15 * DIP})},
220222
}
221223

222224
for i, v := range cases {

base/diff.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,27 @@ func DiffChildren(parent Control, lhs []Element, rhs []Widget) ([]Element, error
123123
return lhs, nil
124124
}
125125

126+
// Layout will layout a non-nil element, otherwise is will return a default
127+
// size respecting the layout contraints.
128+
func Layout(elem Element, bc Constraints) Size {
129+
if elem == nil {
130+
if bc.IsBounded() {
131+
return bc.Max
132+
} else if bc.HasBoundedWidth() {
133+
return Size{bc.Max.Width, bc.Min.Height}
134+
} else if bc.HasBoundedHeight() {
135+
return Size{bc.Min.Width, bc.Max.Height}
136+
}
137+
return bc.Min
138+
}
139+
140+
size := elem.Layout(bc)
141+
if !bc.IsSatisfiedBy(size) {
142+
panic("Error in element layout, size does not satisfy constraints")
143+
}
144+
return size
145+
}
146+
126147
// Mount will try to mount a non-nil widget, otherwise it returns nil for the
127148
// element.
128149
func Mount(parent Control, widget Widget) (Element, error) {

base/diff_test.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ func (m *mockElement) Kind() *Kind {
4545
return m.kind
4646
}
4747

48-
func (m *mockElement) Layout(Constraints) Size {
49-
return Size{}
48+
func (m *mockElement) Layout(bc Constraints) Size {
49+
return bc.Constrain(Size{})
5050
}
5151
func (m *mockElement) MinIntrinsicHeight(width Length) Length {
5252
return 0
@@ -288,6 +288,33 @@ func TestDiffChildren(t *testing.T) {
288288
}
289289
}
290290

291+
func TestLayout(t *testing.T) {
292+
size1 := Size{96 * DIP, 2 * 96 * DIP}
293+
cases := []struct {
294+
in Element
295+
bc Constraints
296+
out Size
297+
}{
298+
{nil, Tight(size1), size1},
299+
{nil, Loose(size1), size1},
300+
{nil, TightWidth(size1.Width), Size{size1.Width, 0}},
301+
{nil, TightHeight(size1.Height), Size{0, size1.Height}},
302+
{nil, Expand(), Size{}},
303+
{&mockElement{}, Tight(size1), size1},
304+
{&mockElement{}, Loose(size1), Size{}},
305+
{&mockElement{}, TightWidth(size1.Width), Size{size1.Width, 0}},
306+
{&mockElement{}, TightHeight(size1.Height), Size{0, size1.Height}},
307+
{&mockElement{}, Expand(), Size{}},
308+
}
309+
310+
for i, v := range cases {
311+
out := Layout(v.in, v.bc)
312+
if out != v.out {
313+
t.Errorf("Case %d: Returned size does not match, got %v, want %v", i, out, v.out)
314+
}
315+
}
316+
}
317+
291318
func TestMount(t *testing.T) {
292319
kind1 := NewKind("bitbucket.org/rj/goey/base.Mock1")
293320
kind2 := NewKind("bitbucket.org/rj/goey/base.Mock2")

decoration.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type Decoration struct {
2121
Stroke color.RGBA // Stroke colour used to draw outline.
2222
Insets Insets // Space between border of the decoration and the child element.
2323
Radius base.Length // Radius of the widgets corners.
24-
Child base.Widget // Child.
24+
Child base.Widget // Child widget.
2525
}
2626

2727
// Kind returns the concrete type for use in the Widget interface.
@@ -45,12 +45,8 @@ func (w *decorationElement) Layout(bc base.Constraints) base.Size {
4545
hinset := w.insets.Left + w.insets.Right
4646
vinset := w.insets.Top + w.insets.Bottom
4747

48-
if w.child == nil {
49-
return bc.Constrain(base.Size{hinset, vinset})
50-
}
51-
5248
innerConstraints := bc.Inset(hinset, vinset)
53-
w.childSize = w.child.Layout(innerConstraints)
49+
w.childSize = base.Layout(w.child, innerConstraints)
5450
return base.Size{
5551
w.childSize.Width + hinset,
5652
w.childSize.Height + vinset,

example/controls/main.go

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func main() {
2525
}
2626

2727
func createWindow() error {
28-
w, err := goey.NewWindow("Example", renderWindow())
28+
w, err := goey.NewWindow("Controls", renderWindow())
2929
if err != nil {
3030
return err
3131
}
@@ -39,57 +39,85 @@ func updateWindow() {
3939
}
4040

4141
func renderWindow() base.Widget {
42-
// Fixed part at top
43-
widgets := []base.Widget{
44-
&goey.Checkbox{Text: "Show text of lorem ipsum.", Value: showLorem, OnChange: func(ok bool) {
45-
showLorem = ok
46-
updateWindow()
47-
}},
48-
&goey.HR{},
49-
}
50-
51-
// Depends
52-
if showLorem {
53-
widgets = append(widgets,
54-
&goey.P{Text: lorem},
55-
)
56-
} else {
57-
widgets = append(widgets,
58-
&goey.P{Text: "This is a paragraph, but without much text.", Align: goey.JustifyCenter},
59-
&goey.Label{Text: "Text input:"},
60-
&goey.TextInput{Value: "Some input...", Placeholder: "Type some text here. And some more. And something really long.",
61-
OnChange: func(v string) { println("text input ", v) }, OnEnterKey: func(v string) { println("t1* ", v) }},
62-
&goey.Label{Text: "Password input:"},
63-
&goey.TextInput{Value: "", Placeholder: "Don't share", Password: true,
64-
OnChange: func(v string) { println("password input ", v) }},
65-
&goey.Label{Text: "Integer input:"},
66-
&goey.IntInput{Value: 3, Placeholder: "Please enter a number",
67-
OnChange: func(v int64) { println("int input ", v) }},
68-
&goey.Label{Text: "Date input:"},
69-
&goey.DateInput{Value: time.Now().Add(24 * time.Hour),
70-
OnChange: func(v time.Time) { println("date input: ", v.String()) }},
71-
&goey.HR{},
72-
&goey.HBox{Children: []base.Widget{
73-
&goey.Button{Text: "C1", Default: true},
74-
&goey.Button{Text: "C2"},
75-
}},
76-
&goey.HBox{Children: []base.Widget{
77-
&goey.Button{Text: "D1"},
78-
&goey.Button{Text: "D2", Disabled: true},
79-
&goey.Button{Text: "D3"},
42+
widget := &goey.Tabs{
43+
Insets: goey.DefaultInsets(),
44+
Children: []goey.TabItem{
45+
{
46+
Caption: "Input",
47+
Child: &goey.VBox{
48+
Children: []base.Widget{
49+
&goey.Label{Text: "Text input:"},
50+
&goey.TextInput{Value: "Some input...", Placeholder: "Type some text here. And some more. And something really long.",
51+
OnChange: func(v string) { println("text input ", v) }, OnEnterKey: func(v string) { println("t1* ", v) }},
52+
&goey.Label{Text: "Password input:"},
53+
&goey.TextInput{Value: "", Placeholder: "Don't share", Password: true,
54+
OnChange: func(v string) { println("password input ", v) }},
55+
&goey.Label{Text: "Integer input:"},
56+
&goey.IntInput{Value: 3, Placeholder: "Please enter a number",
57+
OnChange: func(v int64) { println("int input ", v) }},
58+
&goey.Label{Text: "Date input:"},
59+
&goey.DateInput{Value: time.Now().Add(24 * time.Hour),
60+
OnChange: func(v time.Time) { println("date input: ", v.String()) }},
61+
&goey.Label{Text: "Select input (combobox):"},
62+
&goey.SelectInput{Items: []string{"Choice 1", "Choice 2", "Choice 3"},
63+
OnChange: func(v int) { println("select input: ", v) }},
64+
&goey.Label{Text: "Number input:"},
65+
&goey.Slider{Value: 25, Min: 0, Max: 100,
66+
OnChange: func(v float64) { println("slider input: ", v) }},
67+
&goey.HR{},
68+
&goey.Expand{Child: &goey.TextArea{Value: "", Placeholder: "Room to write",
69+
OnChange: func(v string) { println("text area: ", v) },
70+
}},
71+
},
72+
},
8073
},
81-
AlignMain: goey.MainEnd,
74+
{
75+
Caption: "Buttons",
76+
Child: &goey.VBox{
77+
Children: []base.Widget{
78+
&goey.HBox{Children: []base.Widget{
79+
&goey.Button{Text: "Left 1", Default: true},
80+
&goey.Button{Text: "Left 2"},
81+
}},
82+
&goey.HBox{
83+
Children: []base.Widget{
84+
&goey.Button{Text: "Center"},
85+
},
86+
AlignMain: goey.MainCenter,
87+
},
88+
&goey.HBox{
89+
Children: []base.Widget{
90+
&goey.Button{Text: "D1"},
91+
&goey.Button{Text: "D2", Disabled: true},
92+
&goey.Button{Text: "D3"},
93+
},
94+
AlignMain: goey.MainEnd,
95+
},
96+
&goey.HR{},
97+
&goey.Label{Text: "Check boxes:"},
98+
&goey.Checkbox{Value: true, Text: "Please click on the checkbox A",
99+
OnChange: func(v bool) { println("check box input: ", v) }},
100+
&goey.Checkbox{Text: "Please click on the checkbox B",
101+
OnChange: func(v bool) { println("check box input: ", v) }},
102+
},
103+
},
82104
},
83-
&goey.HR{},
84-
&goey.SelectInput{Items: []string{"Choice 1", "Choice 2", "Choice 3"},
85-
OnChange: func(v int) { println("select input: ", v) }},
86-
&goey.TextArea{Value: "", Placeholder: "Room to write",
87-
OnChange: func(v string) { println("text area: ", v) }},
88-
)
105+
{
106+
Caption: "Lorem",
107+
Child: &goey.VBox{
108+
Children: []base.Widget{
109+
&goey.P{Text: lorem, Align: goey.JustifyFull},
110+
&goey.P{Text: "This is a paragraph, but without much text.", Align: goey.JustifyLeft},
111+
&goey.P{Text: "This is a paragraph, but without much text.", Align: goey.JustifyCenter},
112+
&goey.P{Text: "This is a paragraph, but without much text.", Align: goey.JustifyRight},
113+
},
114+
AlignMain: goey.MainCenter,
115+
},
116+
},
117+
},
89118
}
90-
91119
return &goey.Padding{
92120
Insets: goey.DefaultInsets(),
93-
Child: &goey.VBox{Children: widgets},
121+
Child: widget,
94122
}
95123
}

example/threebuttons/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func render() base.Widget {
109109
OnFocus: onfocus(1),
110110
OnBlur: onblur(1),
111111
},
112-
&goey.Button{Text: "Cyel main axis align",
112+
&goey.Button{Text: "Cycle main axis align",
113113
OnClick: cycleMainAxisAlign,
114114
OnFocus: onfocus(2),
115115
OnBlur: onblur(2),

hbox.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,7 @@ func (w *hboxElement) Layout(bc base.Constraints) base.Size {
143143
for i, v := range w.childrenInfo {
144144
if v.flex > 0 {
145145
oldWidth := v.size.Width
146-
fbc := base.TightWidth(v.size.Width + extraWidth.Scale(v.flex, w.totalFlex))
147-
fbc.Min.Height = cbc.Min.Height
148-
fbc.Max.Height = cbc.Max.Height
146+
fbc := cbc.TightenWidth(v.size.Width + extraWidth.Scale(v.flex, w.totalFlex))
149147
size := w.children[i].Layout(fbc)
150148
w.childrenInfo[i].size = size
151149
w.totalWidth += size.Width - oldWidth

0 commit comments

Comments
 (0)