fit.go 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package widget
  3. import (
  4. "image"
  5. "gioui.org/f32"
  6. "gioui.org/layout"
  7. )
  8. // Fit scales a widget to fit and clip to the constraints.
  9. type Fit uint8
  10. const (
  11. // Unscaled does not alter the scale of a widget.
  12. Unscaled Fit = iota
  13. // Contain scales widget as large as possible without cropping
  14. // and it preserves aspect-ratio.
  15. Contain
  16. // Cover scales the widget to cover the constraint area and
  17. // preserves aspect-ratio.
  18. Cover
  19. // ScaleDown scales the widget smaller without cropping,
  20. // when it exceeds the constraint area.
  21. // It preserves aspect-ratio.
  22. ScaleDown
  23. // Fill stretches the widget to the constraints and does not
  24. // preserve aspect-ratio.
  25. Fill
  26. )
  27. // scale computes the new dimensions and transformation required to fit dims to cs, given the position.
  28. func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Dimensions) (layout.Dimensions, f32.Affine2D) {
  29. widgetSize := dims.Size
  30. if fit == Unscaled || dims.Size.X == 0 || dims.Size.Y == 0 {
  31. dims.Size = cs.Constrain(dims.Size)
  32. offset := pos.Position(widgetSize, dims.Size)
  33. dims.Baseline += offset.Y
  34. return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
  35. }
  36. scale := f32.Point{
  37. X: float32(cs.Max.X) / float32(dims.Size.X),
  38. Y: float32(cs.Max.Y) / float32(dims.Size.Y),
  39. }
  40. switch fit {
  41. case Contain:
  42. if scale.Y < scale.X {
  43. scale.X = scale.Y
  44. } else {
  45. scale.Y = scale.X
  46. }
  47. case Cover:
  48. if scale.Y > scale.X {
  49. scale.X = scale.Y
  50. } else {
  51. scale.Y = scale.X
  52. }
  53. case ScaleDown:
  54. if scale.Y < scale.X {
  55. scale.X = scale.Y
  56. } else {
  57. scale.Y = scale.X
  58. }
  59. // The widget would need to be scaled up, no change needed.
  60. if scale.X >= 1 {
  61. dims.Size = cs.Constrain(dims.Size)
  62. offset := pos.Position(widgetSize, dims.Size)
  63. dims.Baseline += offset.Y
  64. return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
  65. }
  66. case Fill:
  67. }
  68. var scaledSize image.Point
  69. scaledSize.X = int(float32(widgetSize.X) * scale.X)
  70. scaledSize.Y = int(float32(widgetSize.Y) * scale.Y)
  71. dims.Size = cs.Constrain(scaledSize)
  72. dims.Baseline = int(float32(dims.Baseline) * scale.Y)
  73. offset := pos.Position(scaledSize, dims.Size)
  74. trans := f32.Affine2D{}.
  75. Scale(f32.Point{}, scale).
  76. Offset(layout.FPt(offset))
  77. dims.Baseline += offset.Y
  78. return dims, trans
  79. }