fit.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package gel
   4  
   5  import (
   6  	"image"
   7  	
   8  	"github.com/p9c/gio/f32"
   9  	l "github.com/p9c/gio/layout"
  10  	"github.com/p9c/gio/op"
  11  	"github.com/p9c/gio/op/clip"
  12  )
  13  
  14  // Fit scales a widget to fit and clip to the constraints.
  15  type Fit uint8
  16  
  17  const (
  18  	// Unscaled does not alter the scale of a widget.
  19  	Unscaled Fit = iota
  20  	// Contain scales widget as large as possible without cropping
  21  	// and it preserves aspect-ratio.
  22  	Contain
  23  	// Cover scales the widget to cover the constraint area and
  24  	// preserves aspect-ratio.
  25  	Cover
  26  	// ScaleDown scales the widget smaller without cropping,
  27  	// when it exceeds the constraint area.
  28  	// It preserves aspect-ratio.
  29  	ScaleDown
  30  	// Stretch stretches the widget to the constraints and does not
  31  	// preserve aspect-ratio.
  32  	Stretch
  33  )
  34  
  35  // scale adds clip and scale operations to fit dims to the constraints.
  36  // It positions the widget to the appropriate position.
  37  // It returns dimensions modified accordingly.
  38  func (fit Fit) scale(gtx l.Context, pos l.Direction, dims l.Dimensions) l.Dimensions {
  39  	widgetSize := dims.Size
  40  	
  41  	if fit == Unscaled || dims.Size.X == 0 || dims.Size.Y == 0 {
  42  		dims.Size = gtx.Constraints.Constrain(dims.Size)
  43  		clip.Rect{Max: dims.Size}.Add(gtx.Ops)
  44  		
  45  		offset := pos.Position(widgetSize, dims.Size)
  46  		op.Offset(l.FPt(offset)).Add(gtx.Ops)
  47  		dims.Baseline += offset.Y
  48  		return dims
  49  	}
  50  	
  51  	scale := f32.Point{
  52  		X: float32(gtx.Constraints.Max.X) / float32(dims.Size.X),
  53  		Y: float32(gtx.Constraints.Max.Y) / float32(dims.Size.Y),
  54  	}
  55  	
  56  	switch fit {
  57  	case Contain:
  58  		if scale.Y < scale.X {
  59  			scale.X = scale.Y
  60  		} else {
  61  			scale.Y = scale.X
  62  		}
  63  	case Cover:
  64  		if scale.Y > scale.X {
  65  			scale.X = scale.Y
  66  		} else {
  67  			scale.Y = scale.X
  68  		}
  69  	case ScaleDown:
  70  		if scale.Y < scale.X {
  71  			scale.X = scale.Y
  72  		} else {
  73  			scale.Y = scale.X
  74  		}
  75  		
  76  		// The widget would need to be scaled up, no change needed.
  77  		if scale.X >= 1 {
  78  			dims.Size = gtx.Constraints.Constrain(dims.Size)
  79  			clip.Rect{Max: dims.Size}.Add(gtx.Ops)
  80  			
  81  			offset := pos.Position(widgetSize, dims.Size)
  82  			op.Offset(l.FPt(offset)).Add(gtx.Ops)
  83  			dims.Baseline += offset.Y
  84  			return dims
  85  		}
  86  	case Stretch:
  87  	}
  88  	
  89  	var scaledSize image.Point
  90  	scaledSize.X = int(float32(widgetSize.X) * scale.X)
  91  	scaledSize.Y = int(float32(widgetSize.Y) * scale.Y)
  92  	dims.Size = gtx.Constraints.Constrain(scaledSize)
  93  	dims.Baseline = int(float32(dims.Baseline) * scale.Y)
  94  	
  95  	clip.Rect{Max: dims.Size}.Add(gtx.Ops)
  96  	
  97  	offset := pos.Position(scaledSize, dims.Size)
  98  	op.Affine(f32.Affine2D{}.
  99  		Scale(f32.Point{}, scale).
 100  		Offset(l.FPt(offset)),
 101  	).Add(gtx.Ops)
 102  	
 103  	dims.Baseline += offset.Y
 104  	
 105  	return dims
 106  }
 107