asuhfdjkshlk
This commit is contained in:
		
							parent
							
								
									3998d842b1
								
							
						
					
					
						commit
						6936353516
					
				| @ -3,6 +3,7 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| @ -17,51 +18,37 @@ type Button struct { | ||||
| 	pressed bool | ||||
| 	text    string | ||||
| 	 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c      theme.Case | ||||
| 	 | ||||
| 	onClick func () | ||||
| } | ||||
| 
 | ||||
| // NewButton creates a new button with the specified label text. | ||||
| func NewButton (text string) (element *Button) { | ||||
| 	element = &Button { } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.draw, | ||||
| 		element.redo, | ||||
| 		element.redo, | ||||
| 		theme.C("basic", "button")) | ||||
| 	element = &Button { | ||||
| 		c: theme.C("basic", "button"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.draw) | ||||
| 	element.FocusableCore, | ||||
| 	element.focusableControl = core.NewFocusableCore(element.redo) | ||||
| 	element.SetText(text) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (element *Button) redo () { | ||||
| 	element.drawer.SetFace ( | ||||
| 		element.core.FontFace(theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal)) | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Button) HandleMouseDown (x, y int, button input.Button) { | ||||
| 	if !element.Enabled()  { return } | ||||
| 	if !element.Focused() { element.Focus() } | ||||
| 	if button != input.ButtonLeft { return } | ||||
| 	element.pressed = true | ||||
| 	if element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Button) HandleMouseUp (x, y int, button input.Button) { | ||||
| 	if button != input.ButtonLeft { return } | ||||
| 	element.pressed = false | ||||
| 	if element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| 
 | ||||
| 	within := image.Point { x, y }. | ||||
| 		In(element.Bounds()) | ||||
| @ -79,10 +66,7 @@ func (element *Button) HandleKeyDown (key input.Key, modifiers input.Modifiers) | ||||
| 	if !element.Enabled() { return } | ||||
| 	if key == input.KeyEnter { | ||||
| 		element.pressed = true | ||||
| 		if element.core.HasImage() { | ||||
| 			element.draw() | ||||
| 			element.core.DamageAll() | ||||
| 		} | ||||
| 		element.redo() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -113,12 +97,41 @@ func (element *Button) SetText (text string) { | ||||
| 
 | ||||
| 	element.text = text | ||||
| 	element.drawer.SetText([]rune(text)) | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	minimumSize := textBounds.Inset(-element.core.Config().Padding()) | ||||
| 	element.core.SetMinimumSize(minimumSize.Dx(), minimumSize.Dy()) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Button) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.drawer.SetFace (element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c)) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Button) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Button) updateMinimumSize () { | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	minimumSize := textBounds.Inset(-element.config.Padding()) | ||||
| 	element.core.SetMinimumSize(minimumSize.Dx(), minimumSize.Dy()) | ||||
| } | ||||
| 
 | ||||
| func (element *Button) redo () { | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Button) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| @ -128,7 +141,7 @@ func (element *Button) draw () { | ||||
| 		Pressed:  element.pressed, | ||||
| 	} | ||||
| 
 | ||||
| 	pattern := element.core.Pattern(theme.PatternButton, state) | ||||
| 	pattern := element.theme.Pattern(theme.PatternButton, element.c, state) | ||||
| 
 | ||||
| 	artist.FillRectangle(element, pattern, bounds) | ||||
| 
 | ||||
| @ -143,6 +156,6 @@ func (element *Button) draw () { | ||||
| 	offset.Y -= textBounds.Min.Y | ||||
| 	offset.X -= textBounds.Min.X | ||||
| 
 | ||||
| 	foreground := element.core.Pattern(theme.PatternForeground, state) | ||||
| 	foreground := element.theme.Pattern(theme.PatternForeground, element.c, state) | ||||
| 	element.drawer.Draw(element, foreground, offset) | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| @ -18,33 +19,26 @@ type Checkbox struct { | ||||
| 	checked bool | ||||
| 	text    string | ||||
| 	 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c      theme.Case | ||||
| 	 | ||||
| 	onToggle func () | ||||
| } | ||||
| 
 | ||||
| // NewCheckbox creates a new cbeckbox with the specified label text. | ||||
| func NewCheckbox (text string, checked bool) (element *Checkbox) { | ||||
| 	element = &Checkbox { checked: checked } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.draw, | ||||
| 		element.redo, | ||||
| 		element.redo, | ||||
| 		theme.C("basic", "checkbox")) | ||||
| 	element = &Checkbox { | ||||
| 		checked: checked, | ||||
| 		c: theme.C("basic", "checkbox"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.draw) | ||||
| 	element.FocusableCore, | ||||
| 	element.focusableControl = core.NewFocusableCore(element.redo) | ||||
| 	element.SetText(text) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (element *Checkbox) redo () { | ||||
| 	element.drawer.SetFace ( | ||||
| 		element.core.FontFace(theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal)) | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Checkbox) HandleMouseDown (x, y int, button input.Button) { | ||||
| 	if !element.Enabled() { return } | ||||
| 	element.Focus() | ||||
| @ -122,16 +116,44 @@ func (element *Checkbox) SetText (text string) { | ||||
| 
 | ||||
| 	element.text = text | ||||
| 	element.drawer.SetText([]rune(text)) | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	element.updateMinimumSize() | ||||
| 	 | ||||
| 	if text == "" { | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Checkbox) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.drawer.SetFace (element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c)) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Checkbox) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Checkbox) updateMinimumSize () { | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	if element.text == "" { | ||||
| 		element.core.SetMinimumSize(textBounds.Dy(), textBounds.Dy()) | ||||
| 	} else { | ||||
| 		element.core.SetMinimumSize ( | ||||
| 			textBounds.Dy() + element.core.Config().Padding() + textBounds.Dx(), | ||||
| 			textBounds.Dy() + element.config.Padding() + textBounds.Dx(), | ||||
| 			textBounds.Dy()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Checkbox) redo () { | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| @ -149,20 +171,22 @@ func (element *Checkbox) draw () { | ||||
| 		On:       element.checked, | ||||
| 	} | ||||
| 
 | ||||
| 	backgroundPattern := element.core.Pattern(theme.PatternBackground, state) | ||||
| 	backgroundPattern := element.theme.Pattern ( | ||||
| 		theme.PatternBackground, element.c, state) | ||||
| 	artist.FillRectangle(element, backgroundPattern, bounds) | ||||
| 
 | ||||
| 	pattern := element.core.Pattern (theme.PatternButton, state) | ||||
| 	pattern := element.theme.Pattern(theme.PatternButton, element.c, state) | ||||
| 	artist.FillRectangle(element, pattern, boxBounds) | ||||
| 
 | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	offset := bounds.Min.Add(image.Point { | ||||
| 		X: bounds.Dy() + element.core.Config().Padding(), | ||||
| 		X: bounds.Dy() + element.config.Padding(), | ||||
| 	}) | ||||
| 
 | ||||
| 	offset.Y -= textBounds.Min.Y | ||||
| 	offset.X -= textBounds.Min.X | ||||
| 
 | ||||
| 	foreground := element.core.Pattern(theme.PatternForeground, state) | ||||
| 	foreground := element.theme.Pattern ( | ||||
| 		theme.PatternForeground, element.c, state) | ||||
| 	element.drawer.Draw(element, foreground, offset) | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/canvas" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/layouts" | ||||
| @ -23,6 +24,10 @@ type Container struct { | ||||
| 	focusable bool | ||||
| 	flexible  bool | ||||
| 	 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c      theme.Case | ||||
| 	 | ||||
| 	onFocusRequest func () (granted bool) | ||||
| 	onFocusMotionRequest func (input.KeynavDirection) (granted bool) | ||||
| 	onFlexibleHeightChange func () | ||||
| @ -30,12 +35,10 @@ type Container struct { | ||||
| 
 | ||||
| // NewContainer creates a new container. | ||||
| func NewContainer (layout layouts.Layout) (element *Container) { | ||||
| 	element = &Container { } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.redoAll, | ||||
| 		element.handleConfigChange, | ||||
| 		element.handleThemeChange, | ||||
| 		theme.C("basic", "container")) | ||||
| 	element = &Container { | ||||
| 		c: theme.C("basic", "container"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.redoAll) | ||||
| 	element.SetLayout(layout) | ||||
| 	return | ||||
| } | ||||
| @ -207,7 +210,10 @@ func (element *Container) redoAll () { | ||||
| 
 | ||||
| 	// draw a background | ||||
| 	bounds := element.Bounds() | ||||
| 	pattern := element.core.Pattern (theme.PatternBackground, theme.PatternState { }) | ||||
| 	pattern := element.theme.Pattern ( | ||||
| 		theme.PatternBackground, | ||||
| 		element.c, | ||||
| 		theme.PatternState { }) | ||||
| 	artist.FillRectangle(element, pattern, bounds) | ||||
| 
 | ||||
| 	// cut our canvas up and give peices to child elements | ||||
| @ -216,21 +222,28 @@ func (element *Container) redoAll () { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Container) handleConfigChange () { | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Container) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	for _, child := range element.children { | ||||
| 		if child0, ok := child.Element.(elements.Configurable); ok { | ||||
| 			child0.SetConfig(element.core.Config()) | ||||
| 		if child0, ok := child.Element.(elements.Themeable); ok { | ||||
| 			child0.SetTheme(element.theme) | ||||
| 		} | ||||
| 	} | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redoAll() | ||||
| } | ||||
| 
 | ||||
| func (element *Container) handleThemeChange () { | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Container) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	for _, child := range element.children { | ||||
| 		if child0, ok := child.Element.(elements.Themeable); ok { | ||||
| 			child0.SetTheme(element.core.Theme()) | ||||
| 		if child0, ok := child.Element.(elements.Configurable); ok { | ||||
| 			child0.SetConfig(element.config) | ||||
| 		} | ||||
| 	} | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redoAll() | ||||
| } | ||||
| 
 | ||||
| @ -284,7 +297,7 @@ func (element *Container) HandleKeyUp (key input.Key, modifiers input.Modifiers) | ||||
| func (element *Container) FlexibleHeightFor (width int) (height int) { | ||||
| 	return element.layout.FlexibleHeightFor ( | ||||
| 		element.children, | ||||
| 		element.core.Config().Margin(), width) | ||||
| 		element.config.Margin(), width) | ||||
| } | ||||
| 
 | ||||
| func (element *Container) OnFlexibleHeightChange (callback func ()) { | ||||
| @ -487,15 +500,15 @@ func (element *Container) childFocusRequestCallback ( | ||||
| 
 | ||||
| func (element *Container) updateMinimumSize () { | ||||
| 	width, height := element.layout.MinimumSize ( | ||||
| 		element.children, element.core.Config().Margin()) | ||||
| 		element.children, element.config.Margin()) | ||||
| 	if element.flexible { | ||||
| 		height = element.layout.FlexibleHeightFor ( | ||||
| 			element.children, element.core.Config().Margin(), width) | ||||
| 			element.children, element.config.Margin(), width) | ||||
| 	} | ||||
| 	element.core.SetMinimumSize(width, height) | ||||
| } | ||||
| 
 | ||||
| func (element *Container) recalculate () { | ||||
| 	element.layout.Arrange ( | ||||
| 		element.children, element.core.Config().Margin(), element.Bounds()) | ||||
| 		element.children, element.config.Margin(), element.Bounds()) | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package basicElements | ||||
| 
 | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| @ -13,31 +14,28 @@ type Label struct { | ||||
| 	text   string | ||||
| 	drawer artist.TextDrawer | ||||
| 	 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c      theme.Case | ||||
| 	 | ||||
| 	onFlexibleHeightChange func () | ||||
| } | ||||
| 
 | ||||
| // NewLabel creates a new label. If wrap is set to true, the text inside will be | ||||
| // wrapped. | ||||
| func NewLabel (text string, wrap bool) (element *Label) { | ||||
| 	element = &Label {  } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.handleResize, | ||||
| 		element.redo, | ||||
| 		element.redo, | ||||
| 		theme.C("basic", "label")) | ||||
| 	face := element.core.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal) | ||||
| 	element.drawer.SetFace(face) | ||||
| 	element = &Label { c: theme.C("basic", "label") } | ||||
| 	element.Core, element.core = core.NewCore(element.handleResize) | ||||
| 	element.SetWrap(wrap) | ||||
| 	element.SetText(text) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (element *Label) redo () { | ||||
| 	face := element.core.FontFace ( | ||||
| 	face := element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal) | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c) | ||||
| 	element.drawer.SetFace(face) | ||||
| 	element.updateMinimumSize() | ||||
| 	bounds := element.Bounds() | ||||
| @ -109,10 +107,36 @@ func (element *Label) SetWrap (wrap bool) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Label) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.drawer.SetFace (element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c)) | ||||
| 	element.updateMinimumSize() | ||||
| 	 | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Label) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	 | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Label) updateMinimumSize () { | ||||
| 	if element.wrap { | ||||
| 		em := element.drawer.Em().Round() | ||||
| 		if em < 1 { em = element.core.Config().Padding() } | ||||
| 		if em < 1 { em = element.config.Padding() } | ||||
| 		element.core.SetMinimumSize ( | ||||
| 			em, element.drawer.LineHeight().Round()) | ||||
| 		if element.onFlexibleHeightChange != nil { | ||||
| @ -127,15 +151,17 @@ func (element *Label) updateMinimumSize () { | ||||
| func (element *Label) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| 	pattern := element.core.Pattern ( | ||||
| 	pattern := element.theme.Pattern ( | ||||
| 		theme.PatternBackground, | ||||
| 		element.c, | ||||
| 		theme.PatternState { }) | ||||
| 	artist.FillRectangle(element, pattern, bounds) | ||||
| 
 | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 
 | ||||
| 	foreground :=  element.core.Pattern ( | ||||
| 	foreground :=  element.theme.Pattern ( | ||||
| 		theme.PatternForeground, | ||||
| 		element.c, | ||||
| 		theme.PatternState { }) | ||||
| 	element.drawer.Draw(element, foreground, bounds.Min.Sub(textBounds.Min)) | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ import "fmt" | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/canvas" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| @ -25,18 +26,21 @@ type List struct { | ||||
| 	scroll int | ||||
| 	entries []ListEntry | ||||
| 	 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c      theme.Case | ||||
| 	 | ||||
| 	onScrollBoundsChange func () | ||||
| 	onNoEntrySelected func () | ||||
| } | ||||
| 
 | ||||
| // NewList creates a new list element with the specified entries. | ||||
| func NewList (entries ...ListEntry) (element *List) { | ||||
| 	element = &List { selectedEntry: -1 } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.handleResize, | ||||
| 		element.redo, | ||||
| 		element.redo, | ||||
| 		theme.C("basic", "list")) | ||||
| 	element = &List { | ||||
| 		selectedEntry: -1, | ||||
| 		c: theme.C("basic", "list"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.handleResize) | ||||
| 	element.FocusableCore, | ||||
| 	element.focusableControl = core.NewFocusableCore (func () { | ||||
| 		if element.core.HasImage () { | ||||
| @ -65,19 +69,25 @@ func (element *List) handleResize () { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *List) handleConfigChange () { | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *List) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	for index, entry := range element.entries { | ||||
| 		entry.SetConfig(element.core.Config()) | ||||
| 		entry.SetConfig(element.config) | ||||
| 		element.entries[index] = entry | ||||
| 	} | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *List) handleThemeChange () { | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *List) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	for index, entry := range element.entries { | ||||
| 		entry.SetConfig(element.core.Config()) | ||||
| 		entry.SetConfig(element.config) | ||||
| 		element.entries[index] = entry | ||||
| 	} | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| @ -196,7 +206,7 @@ func (element *List) ScrollAxes () (horizontal, vertical bool) { | ||||
| } | ||||
| 
 | ||||
| func (element *List) scrollViewportHeight () (height int) { | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	return element.Bounds().Dy() - inset[0] - inset[2] | ||||
| } | ||||
| 
 | ||||
| @ -228,8 +238,8 @@ func (element *List) CountEntries () (count int) { | ||||
| func (element *List) Append (entry ListEntry) { | ||||
| 	// append | ||||
| 	entry.Collapse(element.forcedMinimumWidth) | ||||
| 	entry.SetTheme(element.core.Theme()) | ||||
| 	entry.SetConfig(element.core.Config()) | ||||
| 	entry.SetTheme(element.theme) | ||||
| 	entry.SetConfig(element.config) | ||||
| 	element.entries = append(element.entries, entry) | ||||
| 
 | ||||
| 	// recalculate, redraw, notify | ||||
| @ -322,7 +332,7 @@ func (element *List) Replace (index int, entry ListEntry) { | ||||
| } | ||||
| 
 | ||||
| func (element *List) selectUnderMouse (x, y int) (updated bool) { | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	bounds := inset.Apply(element.Bounds()) | ||||
| 	mousePoint := image.Pt(x, y) | ||||
| 	dot := image.Pt ( | ||||
| @ -364,7 +374,7 @@ func (element *List) changeSelectionBy (delta int) (updated bool) { | ||||
| } | ||||
| 
 | ||||
| func (element *List) resizeEntryToFit (entry ListEntry) (resized ListEntry) { | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	entry.Collapse(element.forcedMinimumWidth - inset[3] - inset[1]) | ||||
| 	return entry | ||||
| } | ||||
| @ -391,7 +401,7 @@ func (element *List) updateMinimumSize () { | ||||
| 		minimumHeight = element.contentHeight | ||||
| 	} | ||||
| 
 | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	minimumHeight += inset[0] + inset[2] | ||||
| 
 | ||||
| 	element.core.SetMinimumSize(minimumWidth, minimumHeight) | ||||
| @ -400,8 +410,8 @@ func (element *List) updateMinimumSize () { | ||||
| func (element *List) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	pattern := element.core.Pattern (theme.PatternSunken, theme.PatternState { | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	pattern := element.theme.Pattern (theme.PatternSunken, element.c, theme.PatternState { | ||||
| 		Disabled: !element.Enabled(), | ||||
| 		Focused: element.Focused(), | ||||
| 	}) | ||||
|  | ||||
| @ -6,8 +6,6 @@ import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/canvas" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| 
 | ||||
| var listEntryCase = theme.C("basic", "listEntry") | ||||
| 
 | ||||
| // ListEntry is an item that can be added to a list. | ||||
| type ListEntry struct { | ||||
| 	drawer artist.TextDrawer | ||||
| @ -15,15 +13,19 @@ type ListEntry struct { | ||||
| 	textPoint image.Point | ||||
| 	text string | ||||
| 	forcedMinimumWidth int | ||||
| 	onSelect func () | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| 	 | ||||
| 	onSelect func () | ||||
| } | ||||
| 
 | ||||
| func NewListEntry (text string, onSelect func ()) (entry ListEntry) { | ||||
| 	entry = ListEntry  { | ||||
| 		text:     text, | ||||
| 		onSelect: onSelect, | ||||
| 		c: theme.C("basic", "listEntry"), | ||||
| 	} | ||||
| 	entry.drawer.SetText([]rune(text)) | ||||
| 	entry.updateBounds() | ||||
| @ -41,7 +43,7 @@ func (entry *ListEntry) SetTheme (new theme.Theme) { | ||||
| 	entry.drawer.SetFace (entry.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		listEntryCase)) | ||||
| 		entry.c)) | ||||
| 	entry.updateBounds() | ||||
| } | ||||
| 
 | ||||
| @ -58,7 +60,7 @@ func (entry *ListEntry) updateBounds () { | ||||
| 		entry.bounds.Max.X = entry.drawer.LayoutBounds().Dx() | ||||
| 	} | ||||
| 	 | ||||
| 	inset := entry.theme.Inset(theme.PatternRaised, listEntryCase) | ||||
| 	inset := entry.theme.Inset(theme.PatternRaised, entry.c) | ||||
| 	entry.bounds.Max.Y += inset[0] + inset[2] | ||||
| 	 | ||||
| 	entry.textPoint = | ||||
| @ -78,12 +80,12 @@ func (entry *ListEntry) Draw ( | ||||
| 		Focused: focused, | ||||
| 		On: on, | ||||
| 	} | ||||
| 	pattern := entry.theme.Pattern (theme.PatternRaised, listEntryCase, state) | ||||
| 	pattern := entry.theme.Pattern (theme.PatternRaised, entry.c, state) | ||||
| 	artist.FillRectangle ( | ||||
| 		destination, | ||||
| 		pattern, | ||||
| 		entry.Bounds().Add(offset)) | ||||
| 	foreground := entry.theme.Pattern (theme.PatternForeground, listEntryCase, state) | ||||
| 	foreground := entry.theme.Pattern (theme.PatternForeground, entry.c, state) | ||||
| 	return entry.drawer.Draw ( | ||||
| 		destination, | ||||
| 		foreground, | ||||
|  | ||||
| @ -2,6 +2,7 @@ package basicElements | ||||
| 
 | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| @ -10,30 +11,23 @@ type ProgressBar struct { | ||||
| 	*core.Core | ||||
| 	core core.CoreControl | ||||
| 	progress float64 | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| } | ||||
| 
 | ||||
| // NewProgressBar creates a new progress bar displaying the given progress | ||||
| // level. | ||||
| func NewProgressBar (progress float64) (element *ProgressBar) { | ||||
| 	element = &ProgressBar { progress: progress } | ||||
| 	element.Core, element.core = core.NewCore ( | ||||
| 		element.draw, | ||||
| 		element.redo, | ||||
| 		element.redo, | ||||
| 		theme.C("basic", "progressBar")) | ||||
| 	element = &ProgressBar { | ||||
| 		progress: progress, | ||||
| 		c: theme.C("basic", "progressBar"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.draw) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (element *ProgressBar) redo () { | ||||
| 	element.core.SetMinimumSize ( | ||||
| 		element.core.Config().Padding() * 2, | ||||
| 		element.core.Config().Padding() * 2) | ||||
| 	if element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetProgress sets the progress level of the bar. | ||||
| func (element *ProgressBar) SetProgress (progress float64) { | ||||
| 	if progress == element.progress { return } | ||||
| @ -44,21 +38,50 @@ func (element *ProgressBar) SetProgress (progress float64) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *ProgressBar) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *ProgressBar) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element (ProgressBar)) updateMinimumSize() { | ||||
| 	element.core.SetMinimumSize ( | ||||
| 		element.config.Padding() * 2, | ||||
| 		element.config.Padding() * 2) | ||||
| } | ||||
| 
 | ||||
| func (element *ProgressBar) redo () { | ||||
| 	if element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *ProgressBar) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| 	pattern := element.core.Pattern ( | ||||
| 	pattern := element.theme.Pattern ( | ||||
| 		theme.PatternSunken, | ||||
| 		element.c, | ||||
| 		theme.PatternState { }) | ||||
| 	inset := element.core.Inset(theme.PatternSunken) | ||||
| 	inset := element.theme.Inset(theme.PatternSunken, element.c) | ||||
| 	artist.FillRectangle(element, pattern, bounds) | ||||
| 	bounds = inset.Apply(bounds) | ||||
| 	meterBounds := image.Rect ( | ||||
| 		bounds.Min.X, bounds.Min.Y, | ||||
| 		bounds.Min.X + int(float64(bounds.Dx()) * element.progress), | ||||
| 		bounds.Max.Y) | ||||
| 	accent := element.core.Pattern ( | ||||
| 	accent := element.theme.Pattern ( | ||||
| 		theme.PatternSunken, | ||||
| 		element.c, | ||||
| 		theme.PatternState { }) | ||||
| 	artist.FillRectangle(element, accent, meterBounds) | ||||
| } | ||||
|  | ||||
| @ -3,15 +3,12 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/canvas" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| var scrollContainerCase     = theme.C("basic", "scrollContainer") | ||||
| var scrollBarHorizontalCase = theme.C("basic", "scrollBarHorizontal") | ||||
| var scrollBarVerticalCase   = theme.C("basic", "scrollBarVertical") | ||||
| 
 | ||||
| // ScrollContainer is a container that is capable of holding a scrollable | ||||
| // element. | ||||
| type ScrollContainer struct { | ||||
| @ -23,6 +20,7 @@ type ScrollContainer struct { | ||||
| 	childWidth, childHeight int | ||||
| 	 | ||||
| 	horizontal struct { | ||||
| 		c theme.Case | ||||
| 		exists bool | ||||
| 		enabled bool | ||||
| 		dragging bool | ||||
| @ -33,6 +31,7 @@ type ScrollContainer struct { | ||||
| 	} | ||||
| 
 | ||||
| 	vertical struct { | ||||
| 		c theme.Case | ||||
| 		exists bool | ||||
| 		enabled bool | ||||
| 		dragging bool | ||||
| @ -42,6 +41,10 @@ type ScrollContainer struct { | ||||
| 		bar image.Rectangle | ||||
| 	} | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| 
 | ||||
| 	onFocusRequest func () (granted bool) | ||||
| 	onFocusMotionRequest func (input.KeynavDirection) (granted bool) | ||||
| } | ||||
| @ -49,7 +52,10 @@ type ScrollContainer struct { | ||||
| // NewScrollContainer creates a new scroll container with the specified scroll | ||||
| // bars. | ||||
| func NewScrollContainer (horizontal, vertical bool) (element *ScrollContainer) { | ||||
| 	element = &ScrollContainer { } | ||||
| 	element = &ScrollContainer { c: theme.C("basic", "scrollContainer") } | ||||
| 	element.horizontal.c = theme.C("basic", "scrollBarHorizontal") | ||||
| 	element.vertical.c   = theme.C("basic", "scrollBarVertical") | ||||
| 	 | ||||
| 	element.Core, element.core = core.NewCore(element.handleResize) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.horizontal.exists = horizontal | ||||
| @ -85,8 +91,6 @@ func (element *ScrollContainer) Adopt (child elements.Scrollable) { | ||||
| 				element.childFocusMotionRequestCallback) | ||||
| 		} | ||||
| 
 | ||||
| 		// TODO: somehow inform the core that we do not in fact want to | ||||
| 		// redraw the element. | ||||
| 		element.updateMinimumSize() | ||||
| 		 | ||||
| 		element.horizontal.enabled, | ||||
| @ -111,6 +115,7 @@ func (element *ScrollContainer) HandleKeyUp (key input.Key, modifiers input.Modi | ||||
| } | ||||
| 
 | ||||
| func (element *ScrollContainer) HandleMouseDown (x, y int, button input.Button) { | ||||
| 	velocity := element.config.ScrollVelocity() | ||||
| 	point := image.Pt(x, y) | ||||
| 	if point.In(element.horizontal.bar) { | ||||
| 		element.horizontal.dragging = true | ||||
| @ -123,9 +128,9 @@ func (element *ScrollContainer) HandleMouseDown (x, y int, button input.Button) | ||||
| 		// FIXME: x backend and scroll container should pull these | ||||
| 		// values from the same place | ||||
| 		if x > element.horizontal.bar.Min.X { | ||||
| 			element.scrollChildBy(16, 0) | ||||
| 			element.scrollChildBy(velocity, 0) | ||||
| 		} else { | ||||
| 			element.scrollChildBy(-16, 0) | ||||
| 			element.scrollChildBy(-velocity, 0) | ||||
| 		} | ||||
| 		 | ||||
| 	} else if point.In(element.vertical.bar) { | ||||
| @ -137,9 +142,9 @@ func (element *ScrollContainer) HandleMouseDown (x, y int, button input.Button) | ||||
| 		 | ||||
| 	} else if point.In(element.vertical.gutter) { | ||||
| 		if y > element.vertical.bar.Min.Y { | ||||
| 			element.scrollChildBy(0, 16) | ||||
| 			element.scrollChildBy(0, velocity) | ||||
| 		} else { | ||||
| 			element.scrollChildBy(0, -16) | ||||
| 			element.scrollChildBy(0, -velocity) | ||||
| 		} | ||||
| 		 | ||||
| 	} else if child, ok := element.child.(elements.MouseTarget); ok { | ||||
| @ -281,22 +286,19 @@ func (element *ScrollContainer) resizeChildToFit () { | ||||
| } | ||||
| 
 | ||||
| func (element *ScrollContainer) recalculate () { | ||||
| 	_, gutterInsetHorizontal := theme.GutterPattern(theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 	}) | ||||
| 	_, gutterInsetVertical := theme.GutterPattern(theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 	}) | ||||
| 
 | ||||
| 	horizontal := &element.horizontal | ||||
| 	vertical   := &element.vertical | ||||
| 	 | ||||
| 	gutterInsetHorizontal := element.theme.Inset(theme.PatternGutter, horizontal.c) | ||||
| 	gutterInsetVertical   := element.theme.Inset(theme.PatternGutter, vertical.c) | ||||
| 
 | ||||
| 	bounds     := element.Bounds() | ||||
| 	thicknessHorizontal := | ||||
| 		theme.HandleWidth() + | ||||
| 		element.config.HandleWidth() + | ||||
| 		gutterInsetHorizontal[3] + | ||||
| 		gutterInsetHorizontal[1] | ||||
| 	thicknessVertical := | ||||
| 		theme.HandleWidth() + | ||||
| 		element.config.HandleWidth() + | ||||
| 		gutterInsetVertical[3] + | ||||
| 		gutterInsetVertical[1] | ||||
| 
 | ||||
| @ -373,9 +375,8 @@ func (element *ScrollContainer) recalculate () { | ||||
| 
 | ||||
| func (element *ScrollContainer) draw () { | ||||
| 	artist.Paste(element, element.child, image.Point { }) | ||||
| 	deadPattern, _ := theme.DeadPattern(theme.PatternState { | ||||
| 		Case: scrollContainerCase, | ||||
| 	}) | ||||
| 	deadPattern := element.theme.Pattern ( | ||||
| 		theme.PatternDead, element.c, theme.PatternState { }) | ||||
| 	artist.FillRectangle ( | ||||
| 		element, deadPattern, | ||||
| 		image.Rect ( | ||||
| @ -388,32 +389,30 @@ func (element *ScrollContainer) draw () { | ||||
| } | ||||
| 
 | ||||
| func (element *ScrollContainer) drawHorizontalBar () { | ||||
| 	gutterPattern, _ := theme.GutterPattern (theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 		Disabled: !element.horizontal.enabled, | ||||
| 	}) | ||||
| 	artist.FillRectangle(element, gutterPattern, element.horizontal.gutter) | ||||
| 	 | ||||
| 	handlePattern, _ := theme.HandlePattern (theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 	state := theme.PatternState { | ||||
| 		Disabled: !element.horizontal.enabled, | ||||
| 		Pressed:  element.horizontal.dragging, | ||||
| 	}) | ||||
| 	} | ||||
| 	gutterPattern := element.theme.Pattern ( | ||||
| 		theme.PatternGutter, element.horizontal.c, state) | ||||
| 	artist.FillRectangle(element, gutterPattern, element.horizontal.gutter) | ||||
| 	 | ||||
| 	handlePattern := element.theme.Pattern ( | ||||
| 			theme.PatternHandle, element.horizontal.c, state) | ||||
| 	artist.FillRectangle(element, handlePattern, element.horizontal.bar) | ||||
| } | ||||
| 
 | ||||
| func (element *ScrollContainer) drawVerticalBar () { | ||||
| 	gutterPattern, _ := theme.GutterPattern (theme.PatternState { | ||||
| 		Case: scrollBarVerticalCase, | ||||
| 		Disabled: !element.vertical.enabled, | ||||
| 	}) | ||||
| 	artist.FillRectangle(element, gutterPattern, element.vertical.gutter) | ||||
| 	 | ||||
| 	handlePattern, _ := theme.HandlePattern (theme.PatternState { | ||||
| 		Case: scrollBarVerticalCase, | ||||
| 	state := theme.PatternState { | ||||
| 		Disabled: !element.vertical.enabled, | ||||
| 		Pressed:  element.vertical.dragging, | ||||
| 	}) | ||||
| 	} | ||||
| 	gutterPattern := element.theme.Pattern ( | ||||
| 		theme.PatternGutter, element.vertical.c, state) | ||||
| 	artist.FillRectangle(element, gutterPattern, element.vertical.gutter) | ||||
| 	 | ||||
| 	handlePattern := element.theme.Pattern ( | ||||
| 			theme.PatternHandle, element.vertical.c, state) | ||||
| 	artist.FillRectangle(element, handlePattern, element.vertical.bar) | ||||
| } | ||||
| 
 | ||||
| @ -436,19 +435,17 @@ func (element *ScrollContainer) dragVerticalBar (mousePosition image.Point) { | ||||
| } | ||||
| 
 | ||||
| func (element *ScrollContainer) updateMinimumSize () { | ||||
| 	_, gutterInsetHorizontal := theme.GutterPattern(theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 	}) | ||||
| 	_, gutterInsetVertical := theme.GutterPattern(theme.PatternState { | ||||
| 		Case: scrollBarHorizontalCase, | ||||
| 	}) | ||||
| 	gutterInsetHorizontal := element.theme.Inset ( | ||||
| 		theme.PatternGutter, element.horizontal.c) | ||||
| 	gutterInsetVertical := element.theme.Inset ( | ||||
| 		theme.PatternGutter, element.vertical.c) | ||||
| 
 | ||||
| 	thicknessHorizontal := | ||||
| 		theme.HandleWidth() + | ||||
| 		element.config.HandleWidth() + | ||||
| 		gutterInsetHorizontal[3] + | ||||
| 		gutterInsetHorizontal[1] | ||||
| 	thicknessVertical := | ||||
| 		theme.HandleWidth() + | ||||
| 		element.config.HandleWidth() + | ||||
| 		gutterInsetVertical[3] + | ||||
| 		gutterInsetVertical[1] | ||||
| 	 | ||||
|  | ||||
| @ -1,23 +1,26 @@ | ||||
| package basicElements | ||||
| 
 | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| var spacerCase = theme.C("basic", "spacer") | ||||
| 
 | ||||
| // Spacer can be used to put space between two elements.. | ||||
| type Spacer struct { | ||||
| 	*core.Core | ||||
| 	core core.CoreControl | ||||
| 	line bool | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| } | ||||
| 
 | ||||
| // NewSpacer creates a new spacer. If line is set to true, the spacer will be | ||||
| // filled with a line color, and if compressed to its minimum width or height, | ||||
| // will appear as a line. | ||||
| func NewSpacer (line bool) (element *Spacer) { | ||||
| 	element = &Spacer { line: line } | ||||
| 	element = &Spacer { line: line, c: theme.C("basic", "spacer") } | ||||
| 	element.Core, element.core = core.NewCore(element.draw) | ||||
| 	element.core.SetMinimumSize(1, 1) | ||||
| 	return | ||||
| @ -33,20 +36,39 @@ func (element *Spacer) SetLine (line bool) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Spacer) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Spacer) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Spacer) redo () { | ||||
| 	if !element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Spacer) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| 	if element.line { | ||||
| 		pattern, _ := theme.ForegroundPattern(theme.PatternState { | ||||
| 			Case: spacerCase, | ||||
| 			Disabled: true, | ||||
| 		}) | ||||
| 		pattern := element.theme.Pattern ( | ||||
| 			theme.PatternForeground, | ||||
| 			element.c, | ||||
| 			theme.PatternState { }) | ||||
| 		artist.FillRectangle(element, pattern, bounds) | ||||
| 	} else { | ||||
| 		pattern, _ := theme.BackgroundPattern(theme.PatternState { | ||||
| 			Case: spacerCase, | ||||
| 			Disabled: true, | ||||
| 		}) | ||||
| 		pattern := element.theme.Pattern ( | ||||
| 			theme.PatternBackground, | ||||
| 			element.c, | ||||
| 			theme.PatternState { }) | ||||
| 		artist.FillRectangle(element, pattern, bounds) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -3,11 +3,10 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| var switchCase = theme.C("basic", "switch") | ||||
| 
 | ||||
| // Switch is a toggle-able on/off switch with an optional label. It is | ||||
| // functionally identical to Checkbox, but plays a different semantic role. | ||||
| type Switch struct { | ||||
| @ -21,23 +20,25 @@ type Switch struct { | ||||
| 	checked bool | ||||
| 	text    string | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| 	 | ||||
| 	onToggle func () | ||||
| } | ||||
| 
 | ||||
| // NewSwitch creates a new switch with the specified label text. | ||||
| func NewSwitch (text string, on bool) (element *Switch) { | ||||
| 	element = &Switch { checked: on, text: text } | ||||
| 	element = &Switch { | ||||
| 		checked: on, | ||||
| 		text: text, | ||||
| 		c: theme.C("basic", "switch"), | ||||
| 	} | ||||
| 	element.Core, element.core = core.NewCore(element.draw) | ||||
| 	element.FocusableCore, | ||||
| 	element.focusableControl = core.NewFocusableCore (func () { | ||||
| 		if element.core.HasImage () { | ||||
| 			element.draw() | ||||
| 			element.core.DamageAll() | ||||
| 		} | ||||
| 	}) | ||||
| 	element.drawer.SetFace(theme.FontFaceRegular()) | ||||
| 	element.focusableControl = core.NewFocusableCore(element.redo) | ||||
| 	element.drawer.SetText([]rune(text)) | ||||
| 	element.calculateMinimumSize() | ||||
| 	element.updateMinimumSize() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| @ -45,10 +46,7 @@ func (element *Switch) HandleMouseDown (x, y int, button input.Button) { | ||||
| 	if !element.Enabled() { return } | ||||
| 	element.Focus() | ||||
| 	element.pressed = true | ||||
| 	if element.core.HasImage() { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Switch) HandleMouseUp (x, y int, button input.Button) { | ||||
| @ -76,10 +74,7 @@ func (element *Switch) HandleMouseScroll (x, y int, deltaX, deltaY float64) { } | ||||
| func (element *Switch) HandleKeyDown (key input.Key, modifiers input.Modifiers) { | ||||
| 	if key == input.KeyEnter { | ||||
| 		element.pressed = true | ||||
| 		if element.core.HasImage() { | ||||
| 			element.draw() | ||||
| 			element.core.DamageAll() | ||||
| 		} | ||||
| 		element.redo() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -87,10 +82,7 @@ func (element *Switch) HandleKeyUp (key input.Key, modifiers input.Modifiers) { | ||||
| 	if key == input.KeyEnter && element.pressed { | ||||
| 		element.pressed = false | ||||
| 		element.checked = !element.checked | ||||
| 		if element.core.HasImage() { | ||||
| 			element.draw() | ||||
| 			element.core.DamageAll() | ||||
| 		} | ||||
| 		element.redo() | ||||
| 		if element.onToggle != nil { | ||||
| 			element.onToggle() | ||||
| 		} | ||||
| @ -118,15 +110,36 @@ func (element *Switch) SetText (text string) { | ||||
| 
 | ||||
| 	element.text = text | ||||
| 	element.drawer.SetText([]rune(text)) | ||||
| 	element.calculateMinimumSize() | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *Switch) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	element.drawer.SetFace (element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c)) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *Switch) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *Switch) redo () { | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *Switch) calculateMinimumSize () { | ||||
| func (element *Switch) updateMinimumSize () { | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	lineHeight := element.drawer.LineHeight().Round() | ||||
| 	 | ||||
| @ -134,7 +147,9 @@ func (element *Switch) calculateMinimumSize () { | ||||
| 		element.core.SetMinimumSize(lineHeight * 2, lineHeight) | ||||
| 	} else { | ||||
| 		element.core.SetMinimumSize ( | ||||
| 			lineHeight * 2 + theme.Padding() + textBounds.Dx(), | ||||
| 			lineHeight * 2 + | ||||
| 			element.config.Padding() + | ||||
| 			textBounds.Dx(), | ||||
| 			lineHeight) | ||||
| 	} | ||||
| } | ||||
| @ -143,9 +158,14 @@ func (element *Switch) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 	handleBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min) | ||||
| 	gutterBounds := image.Rect(0, 0, bounds.Dy() * 2, bounds.Dy()).Add(bounds.Min) | ||||
| 	backgroundPattern, _ := theme.BackgroundPattern(theme.PatternState { | ||||
| 		Case: switchCase, | ||||
| 	}) | ||||
| 
 | ||||
| 	state := theme.PatternState { | ||||
| 		Disabled: !element.Enabled(), | ||||
| 		Focused:  element.Focused(), | ||||
| 		Pressed:  element.pressed, | ||||
| 	} | ||||
| 	backgroundPattern := element.theme.Pattern ( | ||||
| 		theme.PatternBackground, element.c, state) | ||||
| 	artist.FillRectangle (element, backgroundPattern, bounds) | ||||
| 
 | ||||
| 	if element.checked { | ||||
| @ -162,33 +182,23 @@ func (element *Switch) draw () { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	gutterPattern, _ := theme.GutterPattern(theme.PatternState { | ||||
| 		Case: switchCase, | ||||
| 		Disabled: !element.Enabled(), | ||||
| 		Focused:  element.Focused(), | ||||
| 		Pressed:  element.pressed, | ||||
| 	}) | ||||
| 	gutterPattern := element.theme.Pattern ( | ||||
| 		theme.PatternGutter, element.c, state) | ||||
| 	artist.FillRectangle(element, gutterPattern, gutterBounds) | ||||
| 	 | ||||
| 	handlePattern, _ := theme.HandlePattern(theme.PatternState { | ||||
| 		Case: switchCase, | ||||
| 		Disabled: !element.Enabled(), | ||||
| 		Focused:  element.Focused(), | ||||
| 		Pressed:  element.pressed, | ||||
| 	}) | ||||
| 	handlePattern := element.theme.Pattern ( | ||||
| 		theme.PatternHandle, element.c, state) | ||||
| 	artist.FillRectangle(element, handlePattern, handleBounds) | ||||
| 
 | ||||
| 	textBounds := element.drawer.LayoutBounds() | ||||
| 	offset := bounds.Min.Add(image.Point { | ||||
| 		X: bounds.Dy() * 2 + theme.Padding(), | ||||
| 		X: bounds.Dy() * 2 + element.config.Padding(), | ||||
| 	}) | ||||
| 
 | ||||
| 	offset.Y -= textBounds.Min.Y | ||||
| 	offset.X -= textBounds.Min.X | ||||
| 
 | ||||
| 	foreground, _ := theme.ForegroundPattern (theme.PatternState { | ||||
| 		Case: switchCase, | ||||
| 		Disabled: !element.Enabled(), | ||||
| 	}) | ||||
| 	foreground := element.theme.Pattern ( | ||||
| 		theme.PatternForeground, element.c, state) | ||||
| 	element.drawer.Draw(element, foreground, offset) | ||||
| } | ||||
|  | ||||
| @ -3,12 +3,11 @@ package basicElements | ||||
| import "image" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/input" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/textmanip" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/elements/core" | ||||
| 
 | ||||
| var textBoxCase = theme.C("basic", "textBox") | ||||
| 
 | ||||
| // TextBox is a single-line text input. | ||||
| type TextBox struct { | ||||
| 	*core.Core | ||||
| @ -24,6 +23,10 @@ type TextBox struct { | ||||
| 	placeholderDrawer artist.TextDrawer | ||||
| 	valueDrawer       artist.TextDrawer | ||||
| 	 | ||||
| 	theme  theme.Theme | ||||
| 	config config.Config | ||||
| 	c theme.Case | ||||
| 	 | ||||
| 	onKeyDown func (key input.Key, modifiers input.Modifiers) (handled bool) | ||||
| 	onChange  func () | ||||
| 	onScrollBoundsChange func () | ||||
| @ -33,7 +36,7 @@ type TextBox struct { | ||||
| // a value. When the value is empty, the placeholder will be displayed in gray | ||||
| // text. | ||||
| func NewTextBox (placeholder, value string) (element *TextBox) { | ||||
| 	element = &TextBox { } | ||||
| 	element = &TextBox { c: theme.C("basic", "textBox") } | ||||
| 	element.Core, element.core = core.NewCore(element.handleResize) | ||||
| 	element.FocusableCore, | ||||
| 	element.focusableControl = core.NewFocusableCore (func () { | ||||
| @ -42,8 +45,6 @@ func NewTextBox (placeholder, value string) (element *TextBox) { | ||||
| 			element.core.DamageAll() | ||||
| 		} | ||||
| 	}) | ||||
| 	element.placeholderDrawer.SetFace(theme.FontFaceRegular()) | ||||
| 	element.valueDrawer.SetFace(theme.FontFaceRegular()) | ||||
| 	element.placeholder = placeholder | ||||
| 	element.placeholderDrawer.SetText([]rune(placeholder)) | ||||
| 	element.updateMinimumSize() | ||||
| @ -130,9 +131,8 @@ func (element *TextBox) HandleKeyDown(key input.Key, modifiers input.Modifiers) | ||||
| 		element.onScrollBoundsChange() | ||||
| 	} | ||||
| 	 | ||||
| 	if altered && element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	if altered { | ||||
| 		element.redo() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -145,10 +145,7 @@ func (element *TextBox) SetPlaceholder (placeholder string) { | ||||
| 	element.placeholderDrawer.SetText([]rune(placeholder)) | ||||
| 	 | ||||
| 	element.updateMinimumSize() | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) SetValue (text string) { | ||||
| @ -161,11 +158,7 @@ func (element *TextBox) SetValue (text string) { | ||||
| 		element.cursor = element.valueDrawer.Length() | ||||
| 	} | ||||
| 	element.scrollToCursor() | ||||
| 	 | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) Value () (value string) { | ||||
| @ -203,7 +196,7 @@ func (element *TextBox) ScrollViewportBounds () (bounds image.Rectangle) { | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) scrollViewportWidth () (width int) { | ||||
| 	return element.Bounds().Inset(theme.Padding()).Dx() | ||||
| 	return element.Bounds().Inset(element.config.Padding()).Dx() | ||||
| } | ||||
| 
 | ||||
| // ScrollTo scrolls the viewport to the specified point relative to | ||||
| @ -218,10 +211,7 @@ func (element *TextBox) ScrollTo (position image.Point) { | ||||
| 	maxPosition   := contentBounds.Max.X - element.scrollViewportWidth() | ||||
| 	if element.scroll > maxPosition { element.scroll = maxPosition } | ||||
| 
 | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| 	element.redo() | ||||
| 	if element.onScrollBoundsChange != nil { | ||||
| 		element.onScrollBoundsChange() | ||||
| 	} | ||||
| @ -236,18 +226,6 @@ func (element *TextBox) OnScrollBoundsChange (callback func ()) { | ||||
| 	element.onScrollBoundsChange = callback | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) updateMinimumSize () { | ||||
| 	textBounds := element.placeholderDrawer.LayoutBounds() | ||||
| 	_, inset := theme.InputPattern(theme.PatternState { | ||||
| 		Case: textBoxCase, | ||||
| 	}) | ||||
| 	element.core.SetMinimumSize ( | ||||
| 		textBounds.Dx() + | ||||
| 		theme.Padding() * 2 + inset[3] + inset[1], | ||||
| 		element.placeholderDrawer.LineHeight().Round() + | ||||
| 		theme.Padding() * 2 + inset[0] + inset[2]) | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) runOnChange () { | ||||
| 	if element.onChange != nil { | ||||
| 		element.onChange() | ||||
| @ -257,7 +235,7 @@ func (element *TextBox) runOnChange () { | ||||
| func (element *TextBox) scrollToCursor () { | ||||
| 	if !element.core.HasImage() { return } | ||||
| 
 | ||||
| 	bounds := element.Bounds().Inset(theme.Padding()) | ||||
| 	bounds := element.Bounds().Inset(element.config.Padding()) | ||||
| 	bounds = bounds.Sub(bounds.Min) | ||||
| 	bounds.Max.X -= element.valueDrawer.Em().Round() | ||||
| 	cursorPosition := element.valueDrawer.PositionOf(element.cursor) | ||||
| @ -272,28 +250,64 @@ func (element *TextBox) scrollToCursor () { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme sets the element's theme. | ||||
| func (element *TextBox) SetTheme (new theme.Theme) { | ||||
| 	element.theme = new | ||||
| 	face := element.theme.FontFace ( | ||||
| 		theme.FontStyleRegular, | ||||
| 		theme.FontSizeNormal, | ||||
| 		element.c) | ||||
| 	element.placeholderDrawer.SetFace(face) | ||||
| 	element.valueDrawer.SetFace(face) | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| // SetConfig sets the element's configuration. | ||||
| func (element *TextBox) SetConfig (new config.Config) { | ||||
| 	element.config = new | ||||
| 	element.updateMinimumSize() | ||||
| 	element.redo() | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) updateMinimumSize () { | ||||
| 	textBounds := element.placeholderDrawer.LayoutBounds() | ||||
| 	inset := element.theme.Inset(theme.PatternInput, element.c) | ||||
| 	element.core.SetMinimumSize ( | ||||
| 		textBounds.Dx() + | ||||
| 		element.config.Padding() * 2 + inset[3] + inset[1], | ||||
| 		element.placeholderDrawer.LineHeight().Round() + | ||||
| 		element.config.Padding() * 2 + inset[0] + inset[2]) | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) redo () { | ||||
| 	if element.core.HasImage () { | ||||
| 		element.draw() | ||||
| 		element.core.DamageAll() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (element *TextBox) draw () { | ||||
| 	bounds := element.Bounds() | ||||
| 
 | ||||
| 	// FIXME: take index into account | ||||
| 	pattern, inset := theme.InputPattern(theme.PatternState { | ||||
| 		Case:     textBoxCase, | ||||
| 	state := theme.PatternState { | ||||
| 		Disabled: !element.Enabled(), | ||||
| 		Focused:  element.Focused(), | ||||
| 	}) | ||||
| 	} | ||||
| 	pattern := element.theme.Pattern(theme.PatternSunken, element.c, state) | ||||
| 	artist.FillRectangle(element, pattern, bounds) | ||||
| 
 | ||||
| 	if len(element.text) == 0 && !element.Focused() { | ||||
| 		// draw placeholder | ||||
| 		textBounds := element.placeholderDrawer.LayoutBounds() | ||||
| 		offset := bounds.Min.Add (image.Point { | ||||
| 			X: theme.Padding() + inset[3], | ||||
| 			Y: theme.Padding() + inset[0], | ||||
| 		}) | ||||
| 		foreground, _ := theme.ForegroundPattern(theme.PatternState { | ||||
| 			Case: textBoxCase, | ||||
| 			Disabled: true, | ||||
| 			X: element.config.Padding(), | ||||
| 			Y: element.config.Padding(), | ||||
| 		}) | ||||
| 		foreground := element.theme.Pattern ( | ||||
| 			theme.PatternForeground, element.c, | ||||
| 			theme.PatternState { Disabled: true }) | ||||
| 		element.placeholderDrawer.Draw ( | ||||
| 			element, | ||||
| 			foreground, | ||||
| @ -302,13 +316,11 @@ func (element *TextBox) draw () { | ||||
| 		// draw input value | ||||
| 		textBounds := element.valueDrawer.LayoutBounds() | ||||
| 		offset := bounds.Min.Add (image.Point { | ||||
| 			X: theme.Padding() + inset[3] - element.scroll, | ||||
| 			Y: theme.Padding() + inset[0], | ||||
| 		}) | ||||
| 		foreground, _ := theme.ForegroundPattern(theme.PatternState { | ||||
| 			Case: textBoxCase, | ||||
| 			Disabled: !element.Enabled(), | ||||
| 			X: element.config.Padding() - element.scroll, | ||||
| 			Y: element.config.Padding(), | ||||
| 		}) | ||||
| 		foreground := element.theme.Pattern ( | ||||
| 			theme.PatternForeground, element.c, state) | ||||
| 		element.valueDrawer.Draw ( | ||||
| 			element, | ||||
| 			foreground, | ||||
| @ -318,9 +330,6 @@ func (element *TextBox) draw () { | ||||
| 			// cursor | ||||
| 			cursorPosition := element.valueDrawer.PositionOf ( | ||||
| 				element.cursor) | ||||
| 			foreground, _ := theme.ForegroundPattern(theme.PatternState { | ||||
| 				Case: textBoxCase, | ||||
| 			}) | ||||
| 			artist.Line ( | ||||
| 				element, | ||||
| 				foreground, 1, | ||||
|  | ||||
| @ -2,11 +2,7 @@ package core | ||||
| 
 | ||||
| import "image" | ||||
| import "image/color" | ||||
| import "golang.org/x/image/font" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/theme" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/config" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/canvas" | ||||
| import "git.tebibyte.media/sashakoshka/tomo/artist" | ||||
| 
 | ||||
| // Core is a struct that implements some core functionality common to most | ||||
| // widgets. It is meant to be embedded directly into a struct. | ||||
| @ -18,32 +14,20 @@ type Core struct { | ||||
| 		minimumHeight int | ||||
| 	} | ||||
| 
 | ||||
| 	config config.Config | ||||
| 	theme  theme.Theme | ||||
| 	c theme.Case | ||||
| 
 | ||||
| 	handleSizeChange    func () | ||||
| 	handleConfigChange  func () | ||||
| 	handleThemeChange   func () | ||||
| 	drawSizeChange      func () | ||||
| 	onMinimumSizeChange func () | ||||
| 	onDamage func (region canvas.Canvas) | ||||
| } | ||||
| 
 | ||||
| // NewCore creates a new element core and its corresponding control. | ||||
| func NewCore ( | ||||
| 	handleSizeChange   func (), | ||||
| 	handleConfigChange func (), | ||||
| 	handleThemeChange  func (), | ||||
| 	c theme.Case, | ||||
| 	drawSizeChange func (), | ||||
| ) ( | ||||
| 	core *Core, | ||||
| 	control CoreControl, | ||||
| ) { | ||||
| 	core = &Core { | ||||
| 		handleSizeChange:   handleSizeChange, | ||||
| 		handleConfigChange: handleConfigChange, | ||||
| 		handleThemeChange:  handleThemeChange, | ||||
| 		c: c, | ||||
| 		drawSizeChange: drawSizeChange, | ||||
| 	} | ||||
| 	control = CoreControl { core: core } | ||||
| 	return | ||||
| @ -88,8 +72,8 @@ func (core *Core) MinimumSize () (width, height int) { | ||||
| // overridden. | ||||
| func (core *Core) DrawTo (canvas canvas.Canvas) { | ||||
| 	core.canvas = canvas | ||||
| 	if core.handleSizeChange != nil { | ||||
| 		core.handleSizeChange() | ||||
| 	if core.drawSizeChange != nil { | ||||
| 		core.drawSizeChange() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -105,24 +89,6 @@ func (core *Core) OnMinimumSizeChange (callback func ()) { | ||||
| 	core.onMinimumSizeChange = callback | ||||
| } | ||||
| 
 | ||||
| // SetConfig fulfills the elements.Configurable interface. This should not need | ||||
| // to be overridden. | ||||
| func (core *Core) SetConfig (config config.Config) { | ||||
| 	core.config = config | ||||
| 	if core.handleConfigChange != nil { | ||||
| 		core.handleConfigChange() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetTheme fulfills the elements.Themeable interface. This should not need | ||||
| // to be overridden. | ||||
| func (core *Core) SetTheme (theme theme.Theme) { | ||||
| 	core.theme = theme | ||||
| 	if core.handleThemeChange != nil { | ||||
| 		core.handleThemeChange() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // CoreControl is a struct that can exert control over a Core struct. It can be | ||||
| // used as a canvas. It must not be directly embedded into an element, but | ||||
| // instead kept as a private member. When a Core struct is created, a | ||||
| @ -188,49 +154,3 @@ func (control CoreControl) ConstrainSize ( | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Config returns the current configuration. | ||||
| func (control CoreControl) Config () (config.Config) { | ||||
| 	return control.core.config | ||||
| } | ||||
| 
 | ||||
| // Theme returns the current theme. | ||||
| func (control CoreControl) Theme () (theme.Theme) { | ||||
| 	return control.core.theme | ||||
| } | ||||
| 
 | ||||
| // FontFace is like Theme.FontFace, but it automatically applies the correct | ||||
| // case. | ||||
| func (control CoreControl) FontFace ( | ||||
| 	style theme.FontStyle, | ||||
| 	size  theme.FontSize, | ||||
| ) ( | ||||
| 	face font.Face, | ||||
| ) { | ||||
| 	return control.core.theme.FontFace(style, size, control.core.c) | ||||
| } | ||||
| 
 | ||||
| // Icon is like Theme.Icon, but it automatically applies the correct case. | ||||
| func (control CoreControl) Icon (name string) (artist.Pattern) { | ||||
| 	return control.core.theme.Icon(name, control.core.c) | ||||
| } | ||||
| 
 | ||||
| // Pattern is like Theme.Pattern, but it automatically applies the correct case. | ||||
| func (control CoreControl) Pattern ( | ||||
| 	id    theme.Pattern, | ||||
| 	state theme.PatternState, | ||||
| ) ( | ||||
| 	pattern artist.Pattern, | ||||
| ) { | ||||
| 	return control.core.theme.Pattern(id, control.core.c, state) | ||||
| } | ||||
| 
 | ||||
| // Inset is like Theme.Inset, but it automatically applies the correct case. | ||||
| func (control CoreControl) Inset (id theme.Pattern) (inset theme.Inset) { | ||||
| 	return control.core.theme.Inset(id, control.core.c) | ||||
| } | ||||
| 
 | ||||
| // Sink is like Theme.Sink, but it automatically applies the correct case. | ||||
| func (control CoreControl) Sink (id theme.Pattern) (offset image.Point) { | ||||
| 	return control.core.theme.Sink(id, control.core.c) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user