Added support for horizontal layouts
This commit is contained in:
		
							parent
							
								
									0cd7fb9be9
								
							
						
					
					
						commit
						0a21f605fb
					
				| @ -11,27 +11,40 @@ type scratchEntry struct { | |||||||
| 	minimum float64 | 	minimum float64 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type VBox struct { | // Box is a container that lays out its children horizontally or vertically. | ||||||
| 	entity  tomo.ContainerEntity | // Child elements can be set to contract to their minimum size, or expand to | ||||||
| 	scratch map[tomo.Element] scratchEntry | // fill remaining space. Boxes can be nested and used together to create more | ||||||
| 	theme   theme.Wrapped | // complex layouts. | ||||||
| 	padding bool | type Box struct { | ||||||
| 	margin  bool | 	entity   tomo.ContainerEntity | ||||||
|  | 	scratch  map[tomo.Element] scratchEntry | ||||||
|  | 	theme    theme.Wrapped | ||||||
|  | 	padding  bool | ||||||
|  | 	margin   bool | ||||||
|  | 	vertical bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewVBox (padding, margin bool) (element *VBox) { | // NewHBox creates a new horizontal box. | ||||||
| 	element = &VBox { padding: padding, margin: margin } | func NewHBox (padding, margin bool) (element *Box) { | ||||||
|  | 	element = &Box { padding: padding, margin: margin } | ||||||
| 	element.scratch = make(map[tomo.Element] scratchEntry) | 	element.scratch = make(map[tomo.Element] scratchEntry) | ||||||
| 	element.theme.Case = tomo.C("tomo", "vBox") | 	element.theme.Case = tomo.C("tomo", "box") | ||||||
| 	element.entity = tomo.NewEntity(element).(tomo.ContainerEntity) | 	element.entity = tomo.NewEntity(element).(tomo.ContainerEntity) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) Entity () tomo.Entity { | // NewHBox creates a new vertical box. | ||||||
|  | func NewVBox (padding, margin bool) (element *Box) { | ||||||
|  | 	element = NewHBox(padding, margin) | ||||||
|  | 	element.vertical = true | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Box) Entity () tomo.Entity { | ||||||
| 	return element.entity | 	return element.entity | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) Draw (destination canvas.Canvas) { | func (element *Box) Draw (destination canvas.Canvas) { | ||||||
| 	rocks := make([]image.Rectangle, element.entity.CountChildren()) | 	rocks := make([]image.Rectangle, element.entity.CountChildren()) | ||||||
| 	for index := 0; index < element.entity.CountChildren(); index ++ { | 	for index := 0; index < element.entity.CountChildren(); index ++ { | ||||||
| 		rocks[index] = element.entity.Child(index).Entity().Bounds() | 		rocks[index] = element.entity.Child(index).Entity().Bounds() | ||||||
| @ -43,14 +56,20 @@ func (element *VBox) Draw (destination canvas.Canvas) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) Layout () { | func (element *Box) Layout () { | ||||||
| 	margin  := element.theme.Margin(tomo.PatternBackground) | 	margin  := element.theme.Margin(tomo.PatternBackground) | ||||||
| 	padding := element.theme.Padding(tomo.PatternBackground) | 	padding := element.theme.Padding(tomo.PatternBackground) | ||||||
| 	bounds  := element.entity.Bounds() | 	bounds  := element.entity.Bounds() | ||||||
| 	if element.padding { bounds = padding.Apply(bounds) } | 	if element.padding { bounds = padding.Apply(bounds) } | ||||||
| 
 | 
 | ||||||
|  | 	var marginSize float64; if element.vertical { | ||||||
|  | 		marginSize = float64(margin.Y) | ||||||
|  | 	} else { | ||||||
|  | 		marginSize = float64(margin.X) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	freeSpace, nExpanding := element.freeSpace() | 	freeSpace, nExpanding := element.freeSpace() | ||||||
| 	expandingElementHeight := freeSpace / nExpanding | 	expandingElementSize := freeSpace / nExpanding | ||||||
| 
 | 
 | ||||||
| 	// set the size and position of each element | 	// set the size and position of each element | ||||||
| 	x := float64(bounds.Min.X) | 	x := float64(bounds.Min.X) | ||||||
| @ -58,23 +77,31 @@ func (element *VBox) Layout () { | |||||||
| 	for index := 0; index < element.entity.CountChildren(); index ++ { | 	for index := 0; index < element.entity.CountChildren(); index ++ { | ||||||
| 		entry := element.scratch[element.entity.Child(index)] | 		entry := element.scratch[element.entity.Child(index)] | ||||||
| 		 | 		 | ||||||
| 		var height float64; if entry.expand { | 		var size float64; if entry.expand { | ||||||
| 			height = expandingElementHeight | 			size = expandingElementSize | ||||||
| 		} else { | 		} else { | ||||||
| 			height = entry.minimum | 			size = entry.minimum | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		element.entity.PlaceChild (index, tomo.Bounds ( | 		var childBounds image.Rectangle; if element.vertical { | ||||||
| 			int(x),      int(y), | 			childBounds = tomo.Bounds(int(x), int(y), bounds.Dx(), int(size)) | ||||||
| 			bounds.Dx(), int(height))) | 		} else { | ||||||
|  | 			childBounds = tomo.Bounds(int(x), int(y), int(size), bounds.Dy()) | ||||||
|  | 		} | ||||||
|  | 		element.entity.PlaceChild(index, childBounds) | ||||||
| 
 | 
 | ||||||
| 		y += height | 		if element.vertical { | ||||||
| 		if element.margin { y += float64(margin.Y) } | 			y += size | ||||||
|  | 			if element.margin { y += marginSize } | ||||||
|  | 		} else { | ||||||
|  | 			x += size | ||||||
|  | 			if element.margin { x += marginSize } | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) Adopt (child tomo.Element, expand bool) { | func (element *Box) Adopt (child tomo.Element, expand bool) { | ||||||
| 	element.entity.Adopt(child) | 	element.entity.Adopt(child) | ||||||
| 	element.scratch[child] = scratchEntry { expand: expand } | 	element.scratch[child] = scratchEntry { expand: expand } | ||||||
| 	element.updateMinimumSize() | 	element.updateMinimumSize() | ||||||
| @ -82,7 +109,7 @@ func (element *VBox) Adopt (child tomo.Element, expand bool) { | |||||||
| 	element.entity.InvalidateLayout() | 	element.entity.InvalidateLayout() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) Disown (child tomo.Element) { | func (element *Box) Disown (child tomo.Element) { | ||||||
| 	index := element.entity.IndexOf(child) | 	index := element.entity.IndexOf(child) | ||||||
| 	if index < 0 { return } | 	if index < 0 { return } | ||||||
| 	element.entity.Disown(index) | 	element.entity.Disown(index) | ||||||
| @ -92,7 +119,7 @@ func (element *VBox) Disown (child tomo.Element) { | |||||||
| 	element.entity.InvalidateLayout() | 	element.entity.InvalidateLayout() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) DisownAll () { | func (element *Box) DisownAll () { | ||||||
| 	func () { | 	func () { | ||||||
| 		for index := 0; index < element.entity.CountChildren(); index ++ { | 		for index := 0; index < element.entity.CountChildren(); index ++ { | ||||||
| 			index := index | 			index := index | ||||||
| @ -105,18 +132,18 @@ func (element *VBox) DisownAll () { | |||||||
| 	element.entity.InvalidateLayout() | 	element.entity.InvalidateLayout() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) HandleChildMinimumSizeChange (child tomo.Element) { | func (element *Box) HandleChildMinimumSizeChange (child tomo.Element) { | ||||||
| 	element.updateMinimumSize() | 	element.updateMinimumSize() | ||||||
| 	element.entity.Invalidate() | 	element.entity.Invalidate() | ||||||
| 	element.entity.InvalidateLayout() | 	element.entity.InvalidateLayout() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) DrawBackground (destination canvas.Canvas) { | func (element *Box) DrawBackground (destination canvas.Canvas) { | ||||||
| 	element.entity.DrawBackground(destination) | 	element.entity.DrawBackground(destination) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetTheme sets the element's theme. | // SetTheme sets the element's theme. | ||||||
| func (element *VBox) SetTheme (theme tomo.Theme) { | func (element *Box) SetTheme (theme tomo.Theme) { | ||||||
| 	if theme == element.theme.Theme { return } | 	if theme == element.theme.Theme { return } | ||||||
| 	element.theme.Theme = theme | 	element.theme.Theme = theme | ||||||
| 	element.updateMinimumSize() | 	element.updateMinimumSize() | ||||||
| @ -124,10 +151,21 @@ func (element *VBox) SetTheme (theme tomo.Theme) { | |||||||
| 	element.entity.InvalidateLayout() | 	element.entity.InvalidateLayout() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) freeSpace () (space float64, nExpanding float64) { | func (element *Box) freeSpace () (space float64, nExpanding float64) { | ||||||
| 	margin  := element.theme.Margin(tomo.PatternBackground) | 	margin  := element.theme.Margin(tomo.PatternBackground) | ||||||
| 	padding := element.theme.Padding(tomo.PatternBackground) | 	padding := element.theme.Padding(tomo.PatternBackground) | ||||||
| 	space    = float64(element.entity.Bounds().Dy()) | 
 | ||||||
|  | 	var marginSize int; if element.vertical { | ||||||
|  | 		marginSize = margin.Y | ||||||
|  | 	} else { | ||||||
|  | 		marginSize = margin.X | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if element.vertical { | ||||||
|  | 		space = float64(element.entity.Bounds().Dy()) | ||||||
|  | 	} else { | ||||||
|  | 		space = float64(element.entity.Bounds().Dx()) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, entry := range element.scratch { | 	for _, entry := range element.scratch { | ||||||
| 		if entry.expand { | 		if entry.expand { | ||||||
| @ -141,34 +179,50 @@ func (element *VBox) freeSpace () (space float64, nExpanding float64) { | |||||||
| 		space -= float64(padding.Vertical()) | 		space -= float64(padding.Vertical()) | ||||||
| 	} | 	} | ||||||
| 	if element.margin { | 	if element.margin { | ||||||
| 		space -= float64(margin.Y * (len(element.scratch) - 1)) | 		space -= float64(marginSize * (len(element.scratch) - 1)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (element *VBox) updateMinimumSize () { | func (element *Box) updateMinimumSize () { | ||||||
| 	margin  := element.theme.Margin(tomo.PatternBackground) | 	margin  := element.theme.Margin(tomo.PatternBackground) | ||||||
| 	padding := element.theme.Padding(tomo.PatternBackground) | 	padding := element.theme.Padding(tomo.PatternBackground) | ||||||
| 	var width, height int | 	var breadth, size int | ||||||
|  | 	var marginSize int; if element.vertical { | ||||||
|  | 		marginSize = margin.Y | ||||||
|  | 	} else { | ||||||
|  | 		marginSize = margin.X | ||||||
|  | 	} | ||||||
| 	 | 	 | ||||||
| 	for index := 0; index < element.entity.CountChildren(); index ++ { | 	for index := 0; index < element.entity.CountChildren(); index ++ { | ||||||
| 		childWidth, childHeight :=  element.entity.ChildMinimumSize(index) | 		childWidth, childHeight := element.entity.ChildMinimumSize(index) | ||||||
|  | 		var childBreadth, childSize int; if element.vertical { | ||||||
|  | 			childBreadth, childSize = childWidth, childHeight | ||||||
|  | 		} else { | ||||||
|  | 			childBreadth, childSize = childHeight, childWidth | ||||||
|  | 		} | ||||||
| 		 | 		 | ||||||
| 		key   := element.entity.Child(index) | 		key   := element.entity.Child(index) | ||||||
| 		entry := element.scratch[key] | 		entry := element.scratch[key] | ||||||
| 		entry.minimum = float64(childHeight) | 		entry.minimum = float64(childSize) | ||||||
| 		element.scratch[key] = entry | 		element.scratch[key] = entry | ||||||
| 		 | 		 | ||||||
| 		if childWidth > width { | 		if childBreadth > breadth { | ||||||
| 			width = childWidth | 			breadth = childBreadth | ||||||
| 		} | 		} | ||||||
| 		height += childHeight | 		size += childSize | ||||||
| 		if element.margin && index > 0 { | 		if element.margin && index > 0 { | ||||||
| 			height += margin.Y | 			size += marginSize | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	var width, height int; if element.vertical { | ||||||
|  | 		width, height = breadth, size | ||||||
|  | 	} else { | ||||||
|  | 		width, height = size, breadth | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if element.padding { | 	if element.padding { | ||||||
| 		width  += padding.Horizontal() | 		width  += padding.Horizontal() | ||||||
| 		height += padding.Vertical() | 		height += padding.Vertical() | ||||||
| @ -1,7 +1,7 @@ | |||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import "git.tebibyte.media/sashakoshka/tomo" | import "git.tebibyte.media/sashakoshka/tomo" | ||||||
| // import "git.tebibyte.media/sashakoshka/tomo/popups" | import "git.tebibyte.media/sashakoshka/tomo/popups" | ||||||
| import "git.tebibyte.media/sashakoshka/tomo/elements" | import "git.tebibyte.media/sashakoshka/tomo/elements" | ||||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/containers" | import "git.tebibyte.media/sashakoshka/tomo/elements/containers" | ||||||
| import _ "git.tebibyte.media/sashakoshka/tomo/backends/all" | import _ "git.tebibyte.media/sashakoshka/tomo/backends/all" | ||||||
| @ -34,11 +34,11 @@ func run () { | |||||||
| 	vsync := elements.NewCheckbox("Enable vsync", false) | 	vsync := elements.NewCheckbox("Enable vsync", false) | ||||||
| 	vsync.OnToggle (func () { | 	vsync.OnToggle (func () { | ||||||
| 		if vsync.Value() { | 		if vsync.Value() { | ||||||
| 			// popups.NewDialog ( | 			popups.NewDialog ( | ||||||
| 				// popups.DialogKindInfo, | 				popups.DialogKindInfo, | ||||||
| 				// window, | 				window, | ||||||
| 				// "Ha!", | 				"Ha!", | ||||||
| 				// "That doesn't do anything.") | 				"That doesn't do anything.") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| 	container.Adopt(vsync, false) | 	container.Adopt(vsync, false) | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ package popups | |||||||
| 
 | 
 | ||||||
| import "image" | import "image" | ||||||
| import "git.tebibyte.media/sashakoshka/tomo" | import "git.tebibyte.media/sashakoshka/tomo" | ||||||
| import "git.tebibyte.media/sashakoshka/tomo/layouts" |  | ||||||
| import "git.tebibyte.media/sashakoshka/tomo/elements" | import "git.tebibyte.media/sashakoshka/tomo/elements" | ||||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/containers" | import "git.tebibyte.media/sashakoshka/tomo/elements/containers" | ||||||
| 
 | 
 | ||||||
| @ -16,6 +15,8 @@ const ( | |||||||
| 	DialogKindError | 	DialogKindError | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // TODO: add ability to have an icon for buttons | ||||||
|  | 
 | ||||||
| // Button represents a dialog response button. | // Button represents a dialog response button. | ||||||
| type Button struct { | type Button struct { | ||||||
| 	// Name contains the text to display on the button. | 	// Name contains the text to display on the button. | ||||||
| @ -43,10 +44,10 @@ func NewDialog ( | |||||||
| 	} | 	} | ||||||
| 	window.SetTitle(title) | 	window.SetTitle(title) | ||||||
| 	 | 	 | ||||||
| 	container := containers.NewContainer(layouts.Dialog { true, true }) | 	box        := containers.NewVBox(true,  true) | ||||||
| 	window.Adopt(container) | 	messageRow := containers.NewHBox(false, true) | ||||||
|  | 	controlRow := containers.NewHBox(false, true) | ||||||
| 
 | 
 | ||||||
| 	messageContainer := containers.NewContainer(layouts.Horizontal { true, false }) |  | ||||||
| 	iconId := tomo.IconInformation | 	iconId := tomo.IconInformation | ||||||
| 	switch kind { | 	switch kind { | ||||||
| 	case DialogKindInfo:     iconId = tomo.IconInformation | 	case DialogKindInfo:     iconId = tomo.IconInformation | ||||||
| @ -55,15 +56,15 @@ func NewDialog ( | |||||||
| 	case DialogKindError:    iconId = tomo.IconError | 	case DialogKindError:    iconId = tomo.IconError | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	messageContainer.Adopt(elements.NewIcon(iconId, tomo.IconSizeLarge), false) | 	messageRow.Adopt(elements.NewIcon(iconId, tomo.IconSizeLarge), false) | ||||||
| 	messageContainer.Adopt(elements.NewLabel(message, false), true) | 	messageRow.Adopt(elements.NewLabel(message, false), true) | ||||||
| 	container.Adopt(messageContainer, true) |  | ||||||
| 	 | 	 | ||||||
|  | 	controlRow.Adopt(elements.NewSpacer(false), true) | ||||||
| 	if len(buttons) == 0 { | 	if len(buttons) == 0 { | ||||||
| 		button := elements.NewButton("OK") | 		button := elements.NewButton("OK") | ||||||
| 		button.SetIcon(tomo.IconYes) | 		button.SetIcon(tomo.IconYes) | ||||||
| 		button.OnClick(window.Close) | 		button.OnClick(window.Close) | ||||||
| 		container.Adopt(button, false) | 		controlRow.Adopt(button, false) | ||||||
| 		button.Focus() | 		button.Focus() | ||||||
| 	} else { | 	} else { | ||||||
| 		var button *elements.Button | 		var button *elements.Button | ||||||
| @ -74,11 +75,14 @@ func NewDialog ( | |||||||
| 				buttonDescriptor.OnPress() | 				buttonDescriptor.OnPress() | ||||||
| 				window.Close() | 				window.Close() | ||||||
| 			}) | 			}) | ||||||
| 			container.Adopt(button, false) | 			controlRow.Adopt(button, false) | ||||||
| 		} | 		} | ||||||
| 		button.Focus() | 		button.Focus() | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
|  | 	box.Adopt(messageRow, true) | ||||||
|  | 	box.Adopt(controlRow, false) | ||||||
|  | 	window.Adopt(box) | ||||||
| 	window.Show() | 	window.Show() | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user