Sfoglia il codice sorgente

gui builder dev...

leonsal 8 anni fa
parent
commit
0a22ca1b2f
4 ha cambiato i file con 210 aggiunte e 94 eliminazioni
  1. 27 23
      gui/builder.go
  2. 61 40
      gui/hboxlayout.go
  3. 36 21
      gui/panel.go
  4. 86 10
      gui/vboxlayout.go

+ 27 - 23
gui/builder.go

@@ -27,15 +27,17 @@ type Builder struct {
 
 // descLayout describes all layout types
 type descLayout struct {
-	Type    string   // HBox, VBox, Dock
-	Spacing *float32 // spacing in pixels
-	Align   string   // alignment type
+	Type      string  // HBox, VBox, Dock
+	Spacing   float32 // spacing in pixels
+	AlignH    string  // HBox group alignment type
+	AlignV    string  // VBox group alignment type
+	MinHeight bool    // HBox, VBox minimum height flag
+	MinWidth  bool    // HBox, VBox minimum width flag
 }
 
 // descLayoutParam describes all layout parameters types
 type descLayoutParams struct {
 	Expand  *float32 // HBox, VBox expand factor
-	Align   string   // HBox, VBox align
 	Row     int      // Grid layout row
 	Col     int      // Grid layout col
 	ColSpan int      // Grid layout colspan
@@ -74,6 +76,7 @@ type descPanel struct {
 	MaxLength    *uint             // Edit
 	Icon         string            // Button
 	Group        string            // RadioButton
+	Checked      bool              // CheckBox, RadioButton
 	ImageLabel   *descPanel        // DropDown
 	Items        []*descPanel      // Menu, MenuBar
 	Shortcut     string            // Menu
@@ -485,7 +488,7 @@ func (b *Builder) buildCheckBox(pd *descPanel) (IPanel, error) {
 	if err != nil {
 		return nil, err
 	}
-
+	cb.SetValue(pd.Checked)
 	return cb, nil
 }
 
@@ -503,6 +506,7 @@ func (b *Builder) buildRadioButton(pd *descPanel) (IPanel, error) {
 	if pd.Group != "" {
 		rb.SetGroup(pd.Group)
 	}
+	rb.SetValue(pd.Checked)
 	return rb, nil
 }
 
@@ -873,10 +877,10 @@ func (b *Builder) setLayoutParams(dp *descPanel, ipan IPanel) error {
 			params.Expand = *dlp.Expand
 		}
 		// Sets optional align parameter
-		if dlp.Align != "" {
-			align, ok := mapAlignName[dlp.Align]
+		if dlp.AlignV != "" {
+			align, ok := mapAlignName[dlp.AlignV]
 			if !ok {
-				return b.err("align", "Invalid align name:"+dlp.Align)
+				return b.err("align", "Invalid align name:"+dlp.AlignV)
 			}
 			params.AlignV = align
 		}
@@ -893,10 +897,10 @@ func (b *Builder) setLayoutParams(dp *descPanel, ipan IPanel) error {
 			params.Expand = *dlp.Expand
 		}
 		// Sets optional align parameter
-		if dlp.Align != "" {
-			align, ok := mapAlignName[dlp.Align]
+		if dlp.AlignH != "" {
+			align, ok := mapAlignName[dlp.AlignH]
 			if !ok {
-				return b.err("align", "Invalid align name:"+dlp.Align)
+				return b.err("align", "Invalid align name:"+dlp.AlignH)
 			}
 			params.AlignH = align
 		}
@@ -955,16 +959,16 @@ func (b *Builder) setLayout(dp *descPanel, ipan IPanel) error {
 	// HBox layout
 	if dl.Type == descTypeHBoxLayout {
 		hbl := NewHBoxLayout()
-		if dl.Spacing != nil {
-			hbl.SetSpacing(*dl.Spacing)
-		}
-		if dl.Align != "" {
-			align, ok := mapAlignName[dl.Align]
+		hbl.SetSpacing(dl.Spacing)
+		if dl.AlignH != "" {
+			align, ok := mapAlignName[dl.AlignH]
 			if !ok {
-				return b.err("align", "Invalid align name:"+dl.Align)
+				return b.err("align", "Invalid align name:"+dl.AlignV)
 			}
 			hbl.SetAlignH(align)
 		}
+		hbl.SetMinHeight(dl.MinHeight)
+		hbl.SetMinWidth(dl.MinWidth)
 		panel.SetLayout(hbl)
 		return nil
 	}
@@ -972,16 +976,16 @@ func (b *Builder) setLayout(dp *descPanel, ipan IPanel) error {
 	// VBox layout
 	if dl.Type == descTypeVBoxLayout {
 		vbl := NewVBoxLayout()
-		if dl.Spacing != nil {
-			vbl.SetSpacing(*dl.Spacing)
-		}
-		if dl.Align != "" {
-			align, ok := mapAlignName[dl.Align]
+		vbl.SetSpacing(dl.Spacing)
+		if dl.AlignV != "" {
+			align, ok := mapAlignName[dl.AlignV]
 			if !ok {
-				return b.err("align", "Invalid align name:"+dl.Align)
+				return b.err("align", "Invalid align name:"+dl.AlignV)
 			}
 			vbl.SetAlignV(align)
 		}
+		vbl.SetMinHeight(dl.MinHeight)
+		vbl.SetMinWidth(dl.MinWidth)
 		panel.SetLayout(vbl)
 		return nil
 	}

+ 61 - 40
gui/hboxlayout.go

@@ -4,27 +4,28 @@
 
 package gui
 
-/*
-HBoxLayout implements a panel layout which arranges the panel children horizontally.
-The children can be separated by a space in pixels set by SetSpacing().
-The whole group of children can be aligned horizontally by SetAlignH() which can
-accept the following types of alignment:
-
-	AlignLeft: Try to align the group of children to the left if the panel width is
-	greater the the sum of the children widths + spacing.
-
-	AlignRight: Try to align the group of children to the right if the panel width is
-	greater the the sum of the children widths + spacing.
-
-	AlignCenter: Try to align the group of children in the center if the panel width is
-	greater the the sum of the children widths + spacing.
-
-	AlignWidth - Try to align the individual children with the same same space between each other.
-	Each individual child can be aligned vertically by SetLayoutParameters()
-
-If the layout method SetMinHeight(true) is called, the panel minimum height will be the
-height of the child with the largest height.
-*/
+// HBoxLayout implements a panel layout which arranges the panel children horizontally.
+// The children can be separated by a space in pixels set by SetSpacing().
+// The whole group of children can be aligned horizontally by SetAlignH() which can
+// accept the following types of alignment:
+//
+// 	AlignLeft: Try to align the group of children to the left if the panel width is
+// 	greater the the sum of the children widths + spacing.
+//
+// 	AlignRight: Try to align the group of children to the right if the panel width is
+// 	greater the the sum of the children widths + spacing.
+//
+// 	AlignCenter: Try to align the group of children in the center if the panel width is
+// 	greater the the sum of the children widths + spacing.
+//
+// 	AlignWidth - Try to align the individual children with the same same space between each other.
+// 	Each individual child can be aligned vertically by SetLayoutParameters()
+//
+// If the layout method SetMinHeight(true) is called, the panel minimum content height will be the
+// height of the child with the largest height.
+//
+// If the layout method SetMinWidth(true) is called, the panel minimum content width will be the
+// sum of its children's widths plus the spacing.
 type HBoxLayout struct {
 	pan       IPanel
 	spacing   float32
@@ -33,7 +34,7 @@ type HBoxLayout struct {
 	minWidth  bool
 }
 
-// HBoxLayoutParameters specify the vertical alignment of each individual child.
+// HBoxLayoutParams specify the vertical alignment of each individual child.
 type HBoxLayoutParams struct {
 	Expand float32 // item expand horizontally factor (0 - no expand)
 	AlignV Align   // item vertical alignment
@@ -57,7 +58,7 @@ func (bl *HBoxLayout) SetSpacing(spacing float32) {
 }
 
 // SetAlignH sets the horizontal alignment of the whole group of items
-// inside the parent panel and updates the layout if possible.
+// inside the parent panel and updates the layout.
 // This only has any effect if there are no expanded items.
 func (bl *HBoxLayout) SetAlignH(align Align) {
 
@@ -94,44 +95,55 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 		return
 	}
 
-	// If minHeight set, sets the panel content height to the height of the heighest child.
-	newHeight := float32(0)
+	// If minHeight is set, get the maximum height of all the panel's children
+	// and if the panel content height is less than this maximum, set its content height to this value.
 	if bl.minHeight {
+		var maxHeight float32
 		for _, ichild := range parent.Children() {
 			child := ichild.(IPanel).GetPanel()
-			if child.Height() > newHeight {
-				newHeight = child.Height()
+			if !child.Visible() {
+				continue
+			}
+			if child.Height() > maxHeight {
+				maxHeight = child.Height()
 			}
 		}
-		if parent.ContentHeight() < newHeight {
-			parent.SetContentHeight(newHeight)
+		if parent.ContentHeight() < maxHeight {
+			parent.setContentSize(parent.ContentWidth(), maxHeight, false)
 		}
 	}
 
-	// If minWidth set, sets the panel content width to the sum of children widths plus spacing
-	newWidth := float32(0)
+	// If minWidth is set, get the sum of widths of this panel's children plus the spacings.
+	// If the panel content width is less than this width, set its content width to this value.
 	if bl.minWidth {
+		var totalWidth float32
 		for _, ichild := range parent.Children() {
 			child := ichild.(IPanel).GetPanel()
-			newWidth += child.Width()
+			if !child.Visible() {
+				continue
+			}
+			totalWidth += child.Width()
 		}
 		// Adds spacing
-		newWidth += bl.spacing * float32(len(parent.Children())-1)
-		if parent.ContentWidth() < newWidth {
-			parent.SetContentWidth(newWidth)
+		totalWidth += bl.spacing * float32(len(parent.Children())-1)
+		if parent.ContentWidth() < totalWidth {
+			parent.setContentSize(totalWidth, parent.ContentHeight(), false)
 		}
 	}
 
 	// Calculates the total width, expanded width, fixed width and
 	// the sum of the expand factor for all items.
-	var twidth float32 = 0
-	var ewidth float32 = 0
-	var fwidth float32 = 0
-	var texpand float32 = 0
+	var twidth float32
+	var ewidth float32
+	var fwidth float32
+	var texpand float32
 	ecount := 0
 	paramsDef := HBoxLayoutParams{Expand: 0, AlignV: AlignTop}
 	for pos, obj := range parent.Children() {
 		pan := obj.(IPanel).GetPanel()
+		if !pan.Visible() {
+			continue
+		}
 		// Get item layout parameters or use default
 		params := paramsDef
 		if pan.layoutParams != nil {
@@ -161,13 +173,16 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 
 	// If there is at least on expanded item, all free space will be occupied
 	spaceMiddle := bl.spacing
-	var posX float32 = 0
+	var posX float32
 	if texpand > 0 {
 		// If there is free space, distribute space between expanded items
 		totalSpace := parent.ContentWidth() - twidth
 		if totalSpace > 0 {
 			for _, obj := range parent.Children() {
 				pan := obj.(IPanel).GetPanel()
+				if !pan.Visible() {
+					continue
+				}
 				// Get item layout parameters or use default
 				params := paramsDef
 				if pan.layoutParams != nil {
@@ -182,6 +197,9 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 		} else {
 			for _, obj := range parent.Children() {
 				pan := obj.(IPanel).GetPanel()
+				if !pan.Visible() {
+					continue
+				}
 				// Get item layout parameters or use default
 				params := paramsDef
 				if pan.layoutParams != nil {
@@ -222,6 +240,9 @@ func (bl *HBoxLayout) Recalc(ipan IPanel) {
 	height := parent.ContentHeight()
 	for pos, obj := range parent.Children() {
 		pan := obj.(IPanel).GetPanel()
+		if !pan.Visible() {
+			continue
+		}
 		// Get item layout parameters or use default
 		params := paramsDef
 		if pan.layoutParams != nil {

+ 36 - 21
gui/panel.go

@@ -50,7 +50,7 @@ type IPanel interface {
 
 // Panel is 2D rectangular graphic which by default has a quad (2 triangles) geometry.
 // When using the default geometry, a panel has margins, borders, paddings
-// and a content area. The content area can be associated wit a texture
+// and a content area. The content area can be associated with a texture
 // It is the building block of most GUI widgets.
 type Panel struct {
 	*graphic.Graphic                    // Embedded graphic
@@ -148,7 +148,7 @@ func (p *Panel) Initialize(width, height float32) {
 	p.udata.bordersColor = math32.Color4{0, 0, 0, 1}
 	p.bounded = true
 	p.enabled = true
-	p.resize(width, height)
+	p.resize(width, height, true)
 }
 
 // InitializeGraphic initializes this panel with a different graphic
@@ -166,7 +166,7 @@ func (p *Panel) InitializeGraphic(width, height float32, gr *graphic.Graphic) {
 	p.udata.bordersColor = math32.Color4{0, 0, 0, 1}
 	p.bounded = true
 	p.enabled = true
-	p.resize(width, height)
+	p.resize(width, height, true)
 }
 
 // GetPanel satisfies the IPanel interface and
@@ -244,7 +244,7 @@ func (p *Panel) SetSize(width, height float32) {
 		log.Warn("Invalid panel height:%v", height)
 		height = 0
 	}
-	p.resize(width, height)
+	p.resize(width, height, true)
 }
 
 // SetWidth sets this panel external width in pixels.
@@ -314,7 +314,7 @@ func (p *Panel) ContentHeight() float32 {
 func (p *Panel) SetMargins(top, right, bottom, left float32) {
 
 	p.marginSizes.Set(top, right, bottom, left)
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // SetMarginsFrom sets this panel margins sizes from the specified
@@ -322,7 +322,7 @@ func (p *Panel) SetMargins(top, right, bottom, left float32) {
 func (p *Panel) SetMarginsFrom(src *BorderSizes) {
 
 	p.marginSizes = *src
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Margins returns the current margin sizes in pixels
@@ -336,7 +336,7 @@ func (p *Panel) Margins() BorderSizes {
 func (p *Panel) SetBorders(top, right, bottom, left float32) {
 
 	p.borderSizes.Set(top, right, bottom, left)
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // SetBordersFrom sets this panel border sizes from the specified
@@ -344,7 +344,7 @@ func (p *Panel) SetBorders(top, right, bottom, left float32) {
 func (p *Panel) SetBordersFrom(src *BorderSizes) {
 
 	p.borderSizes = *src
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Borders returns this panel current border sizes
@@ -357,7 +357,7 @@ func (p *Panel) Borders() BorderSizes {
 func (p *Panel) SetPaddings(top, right, bottom, left float32) {
 
 	p.paddingSizes.Set(top, right, bottom, left)
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // SetPaddingsFrom sets this panel padding sizes from the specified
@@ -365,7 +365,7 @@ func (p *Panel) SetPaddings(top, right, bottom, left float32) {
 func (p *Panel) SetPaddingsFrom(src *BorderSizes) {
 
 	p.paddingSizes = *src
-	p.resize(p.calcWidth(), p.calcHeight())
+	p.resize(p.calcWidth(), p.calcHeight(), true)
 }
 
 // Paddings returns this panel padding sizes in pixels
@@ -426,16 +426,7 @@ func (p *Panel) Color4() math32.Color4 {
 // the new content size.
 func (p *Panel) SetContentSize(width, height float32) {
 
-	// Calculates the new desired external width and height
-	eWidth := width +
-		p.paddingSizes.Left + p.paddingSizes.Right +
-		p.borderSizes.Left + p.borderSizes.Right +
-		p.marginSizes.Left + p.marginSizes.Right
-	eHeight := height +
-		p.paddingSizes.Top + p.paddingSizes.Bottom +
-		p.borderSizes.Top + p.borderSizes.Bottom +
-		p.marginSizes.Top + p.marginSizes.Bottom
-	p.resize(eWidth, eHeight)
+	p.setContentSize(width, height, true)
 }
 
 // SetContentWidth sets this panel content width to the specified dimension in pixels.
@@ -635,6 +626,24 @@ func (p *Panel) Pix2NDC(px, py float32) (nx, ny float32) {
 	return px / w, -py / h
 }
 
+// setContentSize is an internal version of SetContentSize() which allows
+// to determine if the panel will recalculate its layout and dispatch event.
+// It is normally used by layout managers when setting the panel content size
+// to avoid another invokation of the layout manager.
+func (p *Panel) setContentSize(width, height float32, dispatch bool) {
+
+	// Calculates the new desired external width and height
+	eWidth := width +
+		p.paddingSizes.Left + p.paddingSizes.Right +
+		p.borderSizes.Left + p.borderSizes.Right +
+		p.marginSizes.Left + p.marginSizes.Right
+	eHeight := height +
+		p.paddingSizes.Top + p.paddingSizes.Bottom +
+		p.borderSizes.Top + p.borderSizes.Bottom +
+		p.marginSizes.Top + p.marginSizes.Bottom
+	p.resize(eWidth, eHeight, dispatch)
+}
+
 // setZ sets the Z coordinate for this panel and its children recursively
 // starting at the specified z and zunb coordinates.
 // The z coordinate is used for bound panels and zunb for unbounded panels.
@@ -772,7 +781,9 @@ func (p *Panel) calcHeight() float32 {
 // The margins, borders and padding sizes are kept and the content
 // area size is adjusted. So if the panel is decreased, its minimum
 // size is determined by the margins, borders and paddings.
-func (p *Panel) resize(width, height float32) {
+// Normally it should be called with dispatch=true to recalculate the
+// panel layout and dispatch OnSize event.
+func (p *Panel) resize(width, height float32, dispatch bool) {
 
 	var padding Rect
 	var border Rect
@@ -836,7 +847,11 @@ func (p *Panel) resize(width, height float32) {
 		float32(p.content.Width) / float32(p.width),
 		float32(p.content.Height) / float32(p.height),
 	}
+
 	// Update layout and dispatch event
+	if !dispatch {
+		return
+	}
 	if p.layout != nil {
 		p.layout.Recalc(p)
 	}

+ 86 - 10
gui/vboxlayout.go

@@ -4,13 +4,37 @@
 
 package gui
 
+// VBoxLayout implements a panel layout which arranges the panel children vertically.
+// The children can be separated by a space in pixels set by SetSpacing().
+// The whole group of children can be aligned vertically by SetAlignV() which can
+// accept the following types of alignment:
+//
+// 	AlignTop: Try to align the group of children to the top if the panel height is
+// 	greater the the sum of the children heights + spacing.
+//
+// 	AlignBottom: Try to align the group of children to the bottoom if the panel height is
+// 	greater the the sum of the children heights + spacing.
+//
+// 	AlignCenter: Try to align the group of children in the center if the panel height is
+// 	greater the the sum of the children heights + spacing.
+//
+// 	AlignHeight - Try to align the individual children vertically with the same same space between each other.
+// 	Each individual child can be aligned horizontally by SetLayoutParameters()
+//
+// If the layout method SetMinHeight(true) is called, the panel minimum content height will be the
+// sum of its children's heights plus the spacing.
+//
+// If the layout method SetMinWidth(true) is called, the panel minimum content width will be the
+// width of the widest child.
 type VBoxLayout struct {
-	pan     IPanel
-	spacing float32 // vertical spacing between the children in pixels.
-	alignV  Align   // vertical alignment of the whole block of children
+	pan       IPanel
+	spacing   float32
+	alignV    Align
+	minHeight bool
+	minWidth  bool
 }
 
-// Parameters for individual children
+// VBoxLayoutParams specify the horizontal alignment of each individual child.
 type VBoxLayoutParams struct {
 	Expand float32 // item expand vertically factor (0 - no expand)
 	AlignH Align   // item horizontal alignment
@@ -33,7 +57,7 @@ func (bl *VBoxLayout) SetSpacing(spacing float32) {
 	bl.Recalc(bl.pan)
 }
 
-// SetAlignH sets the horizontal alignment of the whole group of items
+// SetAlignV sets the vertical alignment of the whole group of items
 // inside the parent panel and updates the layout if possible.
 // This only has any effect if there are no expanded items.
 func (bl *VBoxLayout) SetAlignV(align Align) {
@@ -42,6 +66,22 @@ func (bl *VBoxLayout) SetAlignV(align Align) {
 	bl.Recalc(bl.pan)
 }
 
+// SetMinHeight sets if the panel minimum height should be the height of
+// the largest of its children's height.
+func (bl *VBoxLayout) SetMinHeight(state bool) {
+
+	bl.minHeight = state
+	bl.Recalc(bl.pan)
+}
+
+// SetMinWidth sets if the panel minimum width should be sum of its
+// children's width plus the spacing
+func (bl *VBoxLayout) SetMinWidth(state bool) {
+
+	bl.minWidth = state
+	bl.Recalc(bl.pan)
+}
+
 // Recalc recalculates and sets the position and sizes of all children
 func (bl *VBoxLayout) Recalc(ipan IPanel) {
 
@@ -55,12 +95,48 @@ func (bl *VBoxLayout) Recalc(ipan IPanel) {
 		return
 	}
 
+	// If minHeight is set, get the sum of heights of this panel's children plus the spacings.
+	// If the panel content height is less than this height, set its content height to this value.
+	if bl.minHeight {
+		var totalHeight float32
+		for _, ichild := range parent.Children() {
+			child := ichild.(IPanel).GetPanel()
+			if !child.Visible() {
+				continue
+			}
+			totalHeight += child.Height()
+		}
+		// Adds spacing
+		totalHeight += bl.spacing * float32(len(parent.Children())-1)
+		if parent.ContentHeight() < totalHeight {
+			parent.setContentSize(parent.ContentWidth(), totalHeight, false)
+		}
+	}
+
+	// If minWidth is set, get the maximum width of all the panel's children
+	// and if the panel content width is less than this maximum, set its content width to this value.
+	if bl.minWidth {
+		var maxWidth float32
+		for _, ichild := range parent.Children() {
+			child := ichild.(IPanel).GetPanel()
+			if !child.Visible() {
+				continue
+			}
+			if child.Width() > maxWidth {
+				maxWidth = child.Width()
+			}
+		}
+		if parent.ContentWidth() < maxWidth {
+			parent.setContentSize(maxWidth, parent.ContentHeight(), false)
+		}
+	}
+
 	// Calculates the total height, expanded height, fixed height and
 	// the sum of the expand factor for all items.
-	var theight float32 = 0
-	var eheight float32 = 0
-	var fheight float32 = 0
-	var texpand float32 = 0
+	var theight float32
+	var eheight float32
+	var fheight float32
+	var texpand float32
 	ecount := 0
 	paramsDef := VBoxLayoutParams{Expand: 0, AlignH: AlignLeft}
 	for pos, obj := range parent.Children() {
@@ -94,7 +170,7 @@ func (bl *VBoxLayout) Recalc(ipan IPanel) {
 
 	// If there is at least on expanded item, all free space will be occupied
 	spaceMiddle := bl.spacing
-	var posY float32 = 0
+	var posY float32
 	if texpand > 0 {
 		// If there is free space, distribute space between expanded items
 		totalSpace := parent.ContentHeight() - theight