float.go raw

   1  // SPDX-License-Identifier: Unlicense OR MIT
   2  
   3  package gel
   4  
   5  import (
   6  	"image"
   7  
   8  	"github.com/p9c/gio/gesture"
   9  	"github.com/p9c/gio/io/pointer"
  10  	l "github.com/p9c/gio/layout"
  11  	"github.com/p9c/gio/op"
  12  )
  13  
  14  // Float is for selecting a value in a range.
  15  type Float struct {
  16  	value      float32
  17  	drag       gesture.Drag
  18  	pos        float32 // position normalized to [0, 1]
  19  	length     float32
  20  	changed    bool
  21  	changeHook func(float32)
  22  }
  23  
  24  func (th *Theme) Float() *Float {
  25  	return &Float{changeHook: func(float32) {}}
  26  }
  27  
  28  func (f *Float) SetValue(value float32) *Float {
  29  	f.value = value
  30  	return f
  31  }
  32  func (f *Float) Value() float32 {
  33  	return f.value
  34  }
  35  
  36  func (f *Float) SetHook(fn func(fl float32)) *Float {
  37  	f.changeHook = fn
  38  	return f
  39  }
  40  
  41  // Fn processes events.
  42  func (f *Float) Fn(gtx l.Context, pointerMargin int, min, max float32) l.Dimensions {
  43  	size := gtx.Constraints.Min
  44  	f.length = float32(size.X)
  45  	var de *pointer.Event
  46  	for _, ev := range f.drag.Events(gtx.Metric, gtx, gesture.Horizontal) {
  47  		if ev.Type == pointer.Press || ev.Type == pointer.Drag {
  48  			de = &ev
  49  		}
  50  		if ev.Type == pointer.Release {
  51  			f.changeHook(f.value)
  52  		}
  53  	}
  54  	value := f.value
  55  	if de != nil {
  56  		f.pos = de.Position.X / f.length
  57  		value = min + (max-min)*f.pos
  58  	} else if min != max {
  59  		f.pos = value/(max-min) - min
  60  	}
  61  	// Unconditionally call setValue in case min, max, or value changed.
  62  	f.setValue(value, min, max)
  63  
  64  	if f.pos < 0 {
  65  		f.pos = 0
  66  	} else if f.pos > 1 {
  67  		f.pos = 1
  68  	}
  69  
  70  	defer op.Save(gtx.Ops).Load()
  71  	rect := image.Rectangle{Max: size}
  72  	rect.Min.X -= pointerMargin
  73  	rect.Max.X += pointerMargin
  74  	pointer.Rect(rect).Add(gtx.Ops)
  75  	f.drag.Add(gtx.Ops)
  76  
  77  	return l.Dimensions{Size: size}
  78  }
  79  
  80  func (f *Float) setValue(value, min, max float32) {
  81  	if min > max {
  82  		min, max = max, min
  83  	}
  84  	if value < min {
  85  		value = min
  86  	} else if value > max {
  87  		value = max
  88  	}
  89  	if f.value != value {
  90  		f.value = value
  91  		f.changed = true
  92  	}
  93  }
  94  
  95  // Pos reports the selected position.
  96  func (f *Float) Pos() float32 {
  97  	return f.pos * f.length
  98  }
  99  
 100  // Changed reports whether the value has changed since the last call to Changed.
 101  func (f *Float) Changed() bool {
 102  	changed := f.changed
 103  	f.changed = false
 104  	return changed
 105  }
 106