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 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 ( | ||||
| 	artist.Border { Weight: 1, Stroke: strokePattern }, | ||||
| 	artist.Border { | ||||
| @ -79,21 +44,6 @@ func ForegroundPattern (enabled bool) (artist.Pattern) { | ||||
| 		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 | ||||
| 
 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user