User interface toolkit for Go with support for SDL2 and HTML Canvas render targets.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

153 lines
3.1 KiB

  1. package ui
  2. import (
  3. "errors"
  4. "fmt"
  5. "git.kirsle.net/go/render"
  6. "git.kirsle.net/go/ui/style"
  7. )
  8. // Button is a clickable button.
  9. type Button struct {
  10. BaseWidget
  11. child Widget
  12. style *style.Button
  13. // Private options.
  14. hovering bool
  15. clicked bool
  16. }
  17. // NewButton creates a new Button.
  18. func NewButton(name string, child Widget) *Button {
  19. w := &Button{
  20. child: child,
  21. style: &style.DefaultButton,
  22. }
  23. w.IDFunc(func() string {
  24. return fmt.Sprintf("Button<%s>", name)
  25. })
  26. w.SetStyle(Theme.Button)
  27. w.Handle(MouseOver, func(e EventData) error {
  28. w.hovering = true
  29. w.SetBackground(w.style.HoverBackground)
  30. if label, ok := w.child.(*Label); ok {
  31. label.Font.Color = w.style.HoverForeground
  32. }
  33. return nil
  34. })
  35. w.Handle(MouseOut, func(e EventData) error {
  36. w.hovering = false
  37. w.SetBackground(w.style.Background)
  38. if label, ok := w.child.(*Label); ok {
  39. label.Font.Color = w.style.Foreground
  40. }
  41. return nil
  42. })
  43. w.Handle(MouseDown, func(e EventData) error {
  44. w.clicked = true
  45. w.SetBorderStyle(BorderSunken)
  46. return nil
  47. })
  48. w.Handle(MouseUp, func(e EventData) error {
  49. w.clicked = false
  50. w.SetBorderStyle(BorderStyle(w.style.BorderStyle))
  51. return nil
  52. })
  53. return w
  54. }
  55. // SetStyle sets the button style.
  56. func (w *Button) SetStyle(v *style.Button) {
  57. if v == nil {
  58. v = &style.DefaultButton
  59. }
  60. w.style = v
  61. w.Configure(Config{
  62. BorderSize: w.style.BorderSize,
  63. BorderStyle: BorderStyle(w.style.BorderStyle),
  64. OutlineSize: w.style.OutlineSize,
  65. OutlineColor: w.style.OutlineColor,
  66. Background: w.style.Background,
  67. })
  68. // If the child is a Label, apply the foreground color.
  69. if label, ok := w.child.(*Label); ok {
  70. label.Font.Color = w.style.Foreground
  71. }
  72. }
  73. // Children returns the button's child widget.
  74. func (w *Button) Children() []Widget {
  75. return []Widget{w.child}
  76. }
  77. // Compute the size of the button.
  78. func (w *Button) Compute(e render.Engine) {
  79. // Compute the size of the inner widget first.
  80. w.child.Compute(e)
  81. // Auto-resize only if we haven't been given a fixed size.
  82. if !w.FixedSize() {
  83. size := w.child.Size()
  84. w.Resize(render.Rect{
  85. W: size.W + w.BoxThickness(2),
  86. H: size.H + w.BoxThickness(2),
  87. })
  88. }
  89. w.BaseWidget.Compute(e)
  90. }
  91. // SetText conveniently sets the button text, for Label children only.
  92. func (w *Button) SetText(text string) error {
  93. if label, ok := w.child.(*Label); ok {
  94. label.Text = text
  95. }
  96. return errors.New("child is not a Label widget")
  97. }
  98. // Present the button.
  99. func (w *Button) Present(e render.Engine, P render.Point) {
  100. if w.Hidden() {
  101. return
  102. }
  103. w.Compute(e)
  104. var (
  105. S = w.Size()
  106. ChildSize = w.child.Size()
  107. )
  108. // Draw the widget's border and everything.
  109. w.DrawBox(e, P)
  110. // Offset further if we are currently sunken.
  111. var clickOffset int
  112. if w.clicked {
  113. clickOffset++
  114. }
  115. // Where to place the child widget.
  116. moveTo := render.Point{
  117. X: P.X + w.BoxThickness(1) + clickOffset,
  118. Y: P.Y + w.BoxThickness(1) + clickOffset,
  119. }
  120. // If we're bigger than we need to be, center the child widget.
  121. if S.Bigger(ChildSize) {
  122. moveTo.X = P.X + (S.W / 2) - (ChildSize.W / 2)
  123. }
  124. // Draw the text label inside.
  125. w.child.Present(e, moveTo)
  126. w.BaseWidget.Present(e, P)
  127. }