Checkboxes!
This commit is contained in:
		
							parent
							
								
									37458f4fde
								
							
						
					
					
						commit
						5d64788b68
					
				
							
								
								
									
										211
									
								
								elements/basic/checkbox.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								elements/basic/checkbox.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,211 @@ | |||||||
|  | package basic | ||||||
|  | 
 | ||||||
|  | import "image" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||||
|  | 
 | ||||||
|  | // Checkbox is a toggle-able checkbox with a label. | ||||||
|  | type Checkbox struct { | ||||||
|  | 	*core.Core | ||||||
|  | 	core core.CoreControl | ||||||
|  | 
 | ||||||
|  | 	pressed  bool | ||||||
|  | 	checked  bool | ||||||
|  | 	enabled  bool | ||||||
|  | 	selected bool | ||||||
|  | 	onClick func () | ||||||
|  | 
 | ||||||
|  | 	text   string | ||||||
|  | 	drawer artist.TextDrawer | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewCheckbox creates a new cbeckbox with the specified label text. | ||||||
|  | func NewCheckbox (text string, checked bool) (element *Checkbox) { | ||||||
|  | 	element = &Checkbox { enabled: true } | ||||||
|  | 	element.Core, element.core = core.NewCore(element) | ||||||
|  | 	element.drawer.SetFace(theme.FontFaceRegular()) | ||||||
|  | 	element.SetText(text) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Resize changes this element's size. | ||||||
|  | func (element *Checkbox) Resize (width, height int) { | ||||||
|  | 	element.core.AllocateCanvas(width, height) | ||||||
|  | 	element.draw() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleMouseDown (x, y int, button tomo.Button) { | ||||||
|  | 	element.Select() | ||||||
|  | 	element.pressed = true | ||||||
|  | 	if element.core.HasImage() { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleMouseUp (x, y int, button tomo.Button) { | ||||||
|  | 	if button != tomo.ButtonLeft { return } | ||||||
|  | 
 | ||||||
|  | 	element.pressed = false | ||||||
|  | 	within := image.Point { x, y }. | ||||||
|  | 		In(element.Bounds()) | ||||||
|  | 	if within { | ||||||
|  | 		element.checked = !element.checked | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	if element.core.HasImage() { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | 	if within && element.onClick != nil { | ||||||
|  | 		element.onClick() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleMouseMove (x, y int) { } | ||||||
|  | func (element *Checkbox) HandleScroll (x, y int, deltaX, deltaY float64) { } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleKeyDown ( | ||||||
|  | 	key tomo.Key, | ||||||
|  | 	modifiers tomo.Modifiers, | ||||||
|  | 	repeated bool, | ||||||
|  | ) { | ||||||
|  | 	if key == tomo.KeyEnter { | ||||||
|  | 		element.pressed = true | ||||||
|  | 		if element.core.HasImage() { | ||||||
|  | 			element.draw() | ||||||
|  | 			element.core.PushAll() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleKeyUp (key tomo.Key, modifiers tomo.Modifiers) { | ||||||
|  | 	if key == tomo.KeyEnter && element.pressed { | ||||||
|  | 		element.pressed = false | ||||||
|  | 		element.checked = !element.checked | ||||||
|  | 		if element.core.HasImage() { | ||||||
|  | 			element.draw() | ||||||
|  | 			element.core.PushAll() | ||||||
|  | 		} | ||||||
|  | 		if element.onClick != nil { | ||||||
|  | 			element.onClick() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Selected returns whether or not this element is selected. | ||||||
|  | func (element *Checkbox) Selected () (selected bool) { | ||||||
|  | 	return element.selected | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Select requests that this element be selected. | ||||||
|  | func (element *Checkbox) Select () { | ||||||
|  | 	element.core.RequestSelection() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleSelection ( | ||||||
|  | 	direction tomo.SelectionDirection, | ||||||
|  | ) ( | ||||||
|  | 	accepted bool, | ||||||
|  | ) { | ||||||
|  | 	direction = direction.Canon() | ||||||
|  | 	if !element.enabled { return false } | ||||||
|  | 	if element.selected && direction != tomo.SelectionDirectionNeutral { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	element.selected = true | ||||||
|  | 	if element.core.HasImage() { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) HandleDeselection () { | ||||||
|  | 	element.selected = false | ||||||
|  | 	if element.core.HasImage() { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // OnClick sets the function to be called when the checkbox is toggled. | ||||||
|  | func (element *Checkbox) OnClick (callback func ()) { | ||||||
|  | 	element.onClick = callback | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Value reports whether or not the checkbox is currently checked. | ||||||
|  | func (element *Checkbox) Value () (checked bool) { | ||||||
|  | 	return element.checked | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetEnabled sets whether this checkbox can be toggled or not. | ||||||
|  | func (element *Checkbox) SetEnabled (enabled bool) { | ||||||
|  | 	if element.enabled == enabled { return } | ||||||
|  | 	element.enabled = enabled | ||||||
|  | 	if element.core.HasImage () { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetText sets the checkbox's label text. | ||||||
|  | func (element *Checkbox) SetText (text string) { | ||||||
|  | 	if element.text == text { return } | ||||||
|  | 
 | ||||||
|  | 	element.text = text | ||||||
|  | 	element.drawer.SetText(text) | ||||||
|  | 	textBounds := element.drawer.LayoutBounds() | ||||||
|  | 	element.core.SetMinimumSize ( | ||||||
|  | 		textBounds.Dy() + theme.Padding() + textBounds.Dx(), | ||||||
|  | 		textBounds.Dy()) | ||||||
|  | 	if element.core.HasImage () { | ||||||
|  | 		element.draw() | ||||||
|  | 		element.core.PushAll() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (element *Checkbox) draw () { | ||||||
|  | 	bounds := element.core.Bounds() | ||||||
|  | 	boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()) | ||||||
|  | 
 | ||||||
|  | 	artist.FillRectangle ( element.core, theme.BackgroundPattern(), bounds) | ||||||
|  | 	artist.FillRectangle ( | ||||||
|  | 		element.core, | ||||||
|  | 		theme.ButtonPattern ( | ||||||
|  | 			element.enabled, | ||||||
|  | 			element.Selected(), | ||||||
|  | 			element.pressed), | ||||||
|  | 		boxBounds) | ||||||
|  | 		 | ||||||
|  | 	innerBounds := bounds | ||||||
|  | 	innerBounds.Min.X += theme.Padding() | ||||||
|  | 	innerBounds.Min.Y += theme.Padding() | ||||||
|  | 	innerBounds.Max.X -= theme.Padding() | ||||||
|  | 	innerBounds.Max.Y -= theme.Padding() | ||||||
|  | 
 | ||||||
|  | 	textBounds := element.drawer.LayoutBounds() | ||||||
|  | 	offset := image.Point { | ||||||
|  | 		X: bounds.Dy() + theme.Padding(), | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	offset.Y -= textBounds.Min.Y | ||||||
|  | 	offset.X -= textBounds.Min.X | ||||||
|  | 
 | ||||||
|  | 	foreground := theme.ForegroundPattern(element.enabled) | ||||||
|  | 	element.drawer.Draw(element.core, foreground, offset) | ||||||
|  | 	 | ||||||
|  | 	if element.checked { | ||||||
|  | 		checkBounds := boxBounds.Inset(4) | ||||||
|  | 		if element.pressed { | ||||||
|  | 			checkBounds = checkBounds.Add(theme.SinkOffsetVector()) | ||||||
|  | 		} | ||||||
|  | 		artist.FillRectangle ( | ||||||
|  | 			element.core, | ||||||
|  | 			theme.ForegroundPattern(element.enabled), | ||||||
|  | 			checkBounds) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								examples/checkbox/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								examples/checkbox/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/layouts" | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/elements/basic" | ||||||
|  | import _ "git.tebibyte.media/sashakoshka/tomo/backends/x" | ||||||
|  | 
 | ||||||
|  | func main () { | ||||||
|  | 	tomo.Run(run) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func run () { | ||||||
|  | 	window, _ := tomo.NewWindow(2, 2) | ||||||
|  | 	window.SetTitle("Checkboxes") | ||||||
|  | 
 | ||||||
|  | 	container := basic.NewContainer(layouts.Vertical { true, true }) | ||||||
|  | 	window.Adopt(container) | ||||||
|  | 
 | ||||||
|  | 	container.Adopt(basic.NewCheckbox("Oh god", false), false) | ||||||
|  | 	container.Adopt(basic.NewCheckbox("Can you hear them", true), false) | ||||||
|  | 	container.Adopt(basic.NewCheckbox("They are in the walls", false), false) | ||||||
|  | 	container.Adopt(basic.NewCheckbox("They are coming for us", false), false) | ||||||
|  | 	disabledCheckbox := basic.NewCheckbox("We are but their helpless prey", false) | ||||||
|  | 	disabledCheckbox.SetEnabled(false) | ||||||
|  | 	container.Adopt(disabledCheckbox, false) | ||||||
|  | 	container.Adopt(basic.NewCheckbox("Enable vsync", true), false) | ||||||
|  | 	button := basic.NewButton("What") | ||||||
|  | 	button.OnClick(tomo.Stop) | ||||||
|  | 	container.Adopt(button, false) | ||||||
|  | 	button.Select() | ||||||
|  | 		 | ||||||
|  | 	window.OnClose(tomo.Stop) | ||||||
|  | 	window.Show() | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								theme/button.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								theme/button.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,54 @@ | |||||||
|  | package theme | ||||||
|  | 
 | ||||||
|  | import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||||
|  | 
 | ||||||
|  | var buttonPattern = artist.NewMultiBorder ( | ||||||
|  | 	artist.Border { Weight: 1, Stroke: strokePattern }, | ||||||
|  | 	artist.Border { | ||||||
|  | 		Weight: 1, | ||||||
|  | 		Stroke: artist.Chiseled { | ||||||
|  | 			Highlight: artist.NewUniform(hex(0xCCD5D2FF)), | ||||||
|  | 			Shadow:    artist.NewUniform(hex(0x4B5B59FF)), | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) | ||||||
|  | var selectedButtonPattern = artist.NewMultiBorder ( | ||||||
|  | 	artist.Border { Weight: 1, Stroke: strokePattern }, | ||||||
|  | 	artist.Border { | ||||||
|  | 		Weight: 1, | ||||||
|  | 		Stroke: artist.Chiseled { | ||||||
|  | 			Highlight: artist.NewUniform(hex(0xCCD5D2FF)), | ||||||
|  | 			Shadow:    artist.NewUniform(hex(0x4B5B59FF)), | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 	artist.Border { Weight: 1, Stroke: accentPattern }, | ||||||
|  | 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) | ||||||
|  | var pressedButtonPattern = artist.NewMultiBorder ( | ||||||
|  | 	artist.Border { Weight: 1, Stroke: strokePattern }, | ||||||
|  | 	artist.Border { | ||||||
|  | 		Weight: 1, | ||||||
|  | 		Stroke: artist.Chiseled { | ||||||
|  | 			Highlight: artist.NewUniform(hex(0x4B5B59FF)), | ||||||
|  | 			Shadow:    artist.NewUniform(hex(0x8D9894FF)), | ||||||
|  | 		}, | ||||||
|  | 	}, | ||||||
|  | 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) | ||||||
|  | var disabledButtonPattern = artist.NewMultiBorder ( | ||||||
|  | 	artist.Border { Weight: 1, Stroke: weakForegroundPattern }, | ||||||
|  | 	artist.Border { Stroke: backgroundPattern }) | ||||||
|  | 
 | ||||||
|  | func ButtonPattern (enabled, selected, pressed bool) (artist.Pattern) { | ||||||
|  | 	if enabled { | ||||||
|  | 		if pressed { | ||||||
|  | 			return pressedButtonPattern | ||||||
|  | 		} else { | ||||||
|  | 			if selected { | ||||||
|  | 				return selectedButtonPattern | ||||||
|  | 			} else { | ||||||
|  | 				return buttonPattern | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		return disabledButtonPattern | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -23,41 +23,6 @@ var foregroundPattern     = artist.NewUniform(color.Gray16 { 0x0000 }) | |||||||
| var weakForegroundPattern = artist.NewUniform(color.Gray16 { 0x4444 }) | var weakForegroundPattern = artist.NewUniform(color.Gray16 { 0x4444 }) | ||||||
| var strokePattern         = artist.NewUniform(color.Gray16 { 0x0000 }) | var strokePattern         = artist.NewUniform(color.Gray16 { 0x0000 }) | ||||||
| 
 | 
 | ||||||
| var buttonPattern = artist.NewMultiBorder ( |  | ||||||
| 	artist.Border { Weight: 1, Stroke: strokePattern }, |  | ||||||
| 	artist.Border { |  | ||||||
| 		Weight: 1, |  | ||||||
| 		Stroke: artist.Chiseled { |  | ||||||
| 			Highlight: artist.NewUniform(hex(0xCCD5D2FF)), |  | ||||||
| 			Shadow:    artist.NewUniform(hex(0x4B5B59FF)), |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) |  | ||||||
| var selectedButtonPattern = artist.NewMultiBorder ( |  | ||||||
| 	artist.Border { Weight: 1, Stroke: strokePattern }, |  | ||||||
| 	artist.Border { |  | ||||||
| 		Weight: 1, |  | ||||||
| 		Stroke: artist.Chiseled { |  | ||||||
| 			Highlight: artist.NewUniform(hex(0xCCD5D2FF)), |  | ||||||
| 			Shadow:    artist.NewUniform(hex(0x4B5B59FF)), |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	artist.Border { Weight: 1, Stroke: accentPattern }, |  | ||||||
| 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) |  | ||||||
| var pressedButtonPattern = artist.NewMultiBorder ( |  | ||||||
| 	artist.Border { Weight: 1, Stroke: strokePattern }, |  | ||||||
| 	artist.Border { |  | ||||||
| 		Weight: 1, |  | ||||||
| 		Stroke: artist.Chiseled { |  | ||||||
| 			Highlight: artist.NewUniform(hex(0x4B5B59FF)), |  | ||||||
| 			Shadow:    artist.NewUniform(hex(0x8D9894FF)), |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 	artist.Border { Stroke: artist.NewUniform(hex(0x8D9894FF)) }) |  | ||||||
| var disabledButtonPattern = artist.NewMultiBorder ( |  | ||||||
| 	artist.Border { Weight: 1, Stroke: weakForegroundPattern }, |  | ||||||
| 	artist.Border { Stroke: backgroundPattern }) |  | ||||||
| 
 |  | ||||||
| var sunkenPattern = artist.NewMultiBorder ( | var sunkenPattern = artist.NewMultiBorder ( | ||||||
| 	artist.Border { Weight: 1, Stroke: strokePattern }, | 	artist.Border { Weight: 1, Stroke: strokePattern }, | ||||||
| 	artist.Border { | 	artist.Border { | ||||||
| @ -79,21 +44,6 @@ func ForegroundPattern (enabled bool) (artist.Pattern) { | |||||||
| 		return weakForegroundPattern | 		return weakForegroundPattern | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| func ButtonPattern (enabled, selected, pressed bool) (artist.Pattern) { |  | ||||||
| 	if enabled { |  | ||||||
| 		if pressed { |  | ||||||
| 			return pressedButtonPattern |  | ||||||
| 		} else { |  | ||||||
| 			if selected { |  | ||||||
| 				return selectedButtonPattern |  | ||||||
| 			} else { |  | ||||||
| 				return buttonPattern |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		return disabledButtonPattern |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| // TODO: load fonts from an actual source instead of using defaultfont | // TODO: load fonts from an actual source instead of using defaultfont | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user