diff --git a/app/app.go b/app/app.go index c1862d6e..c660bee2 100644 --- a/app/app.go +++ b/app/app.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "gioui.org/ui" + "gioui.org/unit" ) // An UpdateEvent is generated when a Window's Update @@ -39,7 +39,7 @@ type DestroyEvent struct { // system decoration such as translucent // system bars and software keyboards. type Insets struct { - Top, Bottom, Left, Right ui.Value + Top, Bottom, Left, Right unit.Value } // A StageEvent is generated whenever the stage of a @@ -146,7 +146,7 @@ func Main() { main() } -// Config implements the ui.Config interface. +// Config implements the layout.Config interface. type Config struct { // Device pixels per dp. pxPerDp float32 @@ -159,14 +159,14 @@ func (c *Config) Now() time.Time { return c.now } -func (c *Config) Px(v ui.Value) int { +func (c *Config) Px(v unit.Value) int { var r float32 switch v.U { - case ui.UnitPx: + case unit.UnitPx: r = v.V - case ui.UnitDp: + case unit.UnitDp: r = c.pxPerDp * v.V - case ui.UnitSp: + case unit.UnitSp: r = c.pxPerSp * v.V default: panic("unknown unit") diff --git a/app/doc.go b/app/doc.go index 6eb37edc..95a7b84f 100644 --- a/app/doc.go +++ b/app/doc.go @@ -18,7 +18,7 @@ contents and state. For example: - import "gioui.org/ui" + import "gioui.org/unit" w := app.NewWindow() for e := range w.Events() { diff --git a/app/os_android.go b/app/os_android.go index e4c05685..c77b9cf9 100644 --- a/app/os_android.go +++ b/app/os_android.go @@ -27,7 +27,7 @@ import ( "gioui.org/f32" "gioui.org/io/key" "gioui.org/io/pointer" - "gioui.org/ui" + "gioui.org/unit" ) type window struct { diff --git a/app/os_ios.go b/app/os_ios.go index 65f07906..65dd198e 100644 --- a/app/os_ios.go +++ b/app/os_ios.go @@ -25,7 +25,7 @@ import ( "gioui.org/f32" "gioui.org/io/key" "gioui.org/io/pointer" - "gioui.org/ui" + "gioui.org/unit" ) type window struct { diff --git a/app/os_wayland.go b/app/os_wayland.go index bf3c17ae..248c7317 100644 --- a/app/os_wayland.go +++ b/app/os_wayland.go @@ -583,8 +583,7 @@ func (w *window) flushFling() { w.fling.yExtrapolation = fling.Extrapolation{} vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) _, _, c := w.config() - c.now = time.Now() - if !w.fling.anim.Start(&c, vel) { + if !w.fling.anim.Start(&c, time.Now(), vel) { return } invDist := 1 / vel diff --git a/app/window.go b/app/window.go index 361f3b90..f0302e51 100644 --- a/app/window.go +++ b/app/window.go @@ -13,7 +13,7 @@ import ( "gioui.org/io/event" "gioui.org/io/profile" "gioui.org/op" - "gioui.org/ui" + "gioui.org/unit" ) // WindowOption configures a Window. @@ -22,7 +22,7 @@ type WindowOption struct { } type windowOptions struct { - Width, Height ui.Value + Width, Height unit.Value Title string } @@ -86,8 +86,8 @@ var ackEvent event.Event // BUG: Calling NewWindow more than once is not yet supported. func NewWindow(options ...WindowOption) *Window { opts := &windowOptions{ - Width: ui.Dp(800), - Height: ui.Dp(600), + Width: unit.Dp(800), + Height: unit.Dp(600), Title: "Gio", } @@ -339,7 +339,7 @@ func WithTitle(t string) WindowOption { } // WithWidth returns an option that sets the window width. -func WithWidth(w ui.Value) WindowOption { +func WithWidth(w unit.Value) WindowOption { if w.V <= 0 { panic("width must be larger than or equal to 0") } @@ -351,7 +351,7 @@ func WithWidth(w ui.Value) WindowOption { } // WithHeight returns an option that sets the window height. -func WithHeight(h ui.Value) WindowOption { +func WithHeight(h unit.Value) WindowOption { if h.V <= 0 { panic("height must be larger than or equal to 0") } diff --git a/cmd/gogio/js_test.go b/cmd/gogio/js_test.go index 9241891b..5a4749f7 100644 --- a/cmd/gogio/js_test.go +++ b/cmd/gogio/js_test.go @@ -15,7 +15,7 @@ import ( "github.com/chromedp/chromedp" - _ "gioui.org/ui" // the build tool adds it to go.mod, so keep it there + _ "gioui.org/unit" // the build tool adds it to go.mod, so keep it there ) func TestJSOnChrome(t *testing.T) { diff --git a/gesture/gesture.go b/gesture/gesture.go index d914ea91..9449825d 100644 --- a/gesture/gesture.go +++ b/gesture/gesture.go @@ -11,13 +11,14 @@ package gesture import ( "math" + "time" "gioui.org/f32" "gioui.org/internal/fling" "gioui.org/io/event" "gioui.org/io/pointer" "gioui.org/op" - "gioui.org/ui" + "gioui.org/unit" ) // Click detects click gestures in the form @@ -93,7 +94,7 @@ const ( StateFlinging ) -var touchSlop = ui.Dp(3) +var touchSlop = unit.Dp(3) // Add the handler to the operation list to receive click events. func (c *Click) Add(ops *op.Ops) { @@ -156,7 +157,7 @@ func (s *Scroll) Stop() { // Scroll detects the scrolling distance from the available events and // ongoing fling gestures. -func (s *Scroll) Scroll(cfg ui.Config, q event.Queue, axis Axis) int { +func (s *Scroll) Scroll(cfg unit.Converter, q event.Queue, t time.Time, axis Axis) int { if s.axis != axis { s.axis = axis return 0 @@ -185,7 +186,7 @@ func (s *Scroll) Scroll(cfg ui.Config, q event.Queue, axis Axis) int { } fling := s.estimator.Estimate() if slop, d := float32(cfg.Px(touchSlop)), fling.Distance; d < -slop || d > slop { - s.flinger.Start(cfg, fling.Velocity) + s.flinger.Start(cfg, t, fling.Velocity) } fallthrough case pointer.Cancel: @@ -221,7 +222,7 @@ func (s *Scroll) Scroll(cfg ui.Config, q event.Queue, axis Axis) int { } } } - total += s.flinger.Tick(cfg.Now()) + total += s.flinger.Tick(t) return total } diff --git a/internal/fling/animation.go b/internal/fling/animation.go index 6d9a004d..66dfb880 100644 --- a/internal/fling/animation.go +++ b/internal/fling/animation.go @@ -7,7 +7,7 @@ import ( "runtime" "time" - "gioui.org/ui" + "gioui.org/unit" ) type Animation struct { @@ -21,8 +21,8 @@ type Animation struct { var ( // Pixels/second. - minFlingVelocity = ui.Dp(50) - maxFlingVelocity = ui.Dp(8000) + minFlingVelocity = unit.Dp(50) + maxFlingVelocity = unit.Dp(8000) ) const ( @@ -31,7 +31,7 @@ const ( // Start a fling given a starting velocity. Returns whether a // fling was started. -func (f *Animation) Start(c ui.Config, velocity float32) bool { +func (f *Animation) Start(c unit.Converter, now time.Time, velocity float32) bool { min := float32(c.Px(minFlingVelocity)) v := velocity if -min <= v && v <= min { @@ -43,7 +43,7 @@ func (f *Animation) Start(c ui.Config, velocity float32) bool { } else if v < -max { v = -max } - f.init(c.Now(), v) + f.init(now, v) return true } diff --git a/layout/doc.go b/layout/doc.go index aedb1c69..12bc7c7a 100644 --- a/layout/doc.go +++ b/layout/doc.go @@ -17,7 +17,7 @@ For example, to add space above a widget: gtx.Reset(...) // Configure a top inset. - inset := layout.Inset{Top: ui.Dp(8), ...} + inset := layout.Inset{Top: unit.Dp(8), ...} // Use the inset to lay out a widget. inset.Layout(gtx, func() { // Lay out widget and determine its size given the constraints. diff --git a/layout/layout.go b/layout/layout.go index 7cb9715c..4f890bb8 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -4,10 +4,11 @@ package layout import ( "image" + "time" "gioui.org/io/event" "gioui.org/op" - "gioui.org/ui" + "gioui.org/unit" ) // Constraints represent a set of acceptable ranges for @@ -52,11 +53,20 @@ type Context struct { // operation. Dimensions Dimensions - ui.Config + Config event.Queue *op.Ops } +// Config define the essential properties of +// the environment. +type Config interface { + // Now returns the current animation time. + Now() time.Time + + unit.Converter +} + const ( Start Alignment = iota End @@ -93,7 +103,7 @@ func (s *Context) Layout(cs Constraints, w Widget) Dimensions { } // Reset the context. -func (c *Context) Reset(cfg ui.Config, cs Constraints) { +func (c *Context) Reset(cfg Config, cs Constraints) { c.Constraints = cs c.Dimensions = Dimensions{} c.Config = cfg @@ -129,7 +139,7 @@ func RigidConstraints(size image.Point) Constraints { // Inset adds space around a widget. type Inset struct { - Top, Right, Bottom, Left ui.Value + Top, Right, Bottom, Left unit.Value } // Align aligns a widget in the available space. @@ -171,7 +181,7 @@ func (in Inset) Layout(gtx *Context, w Widget) { // UniformInset returns an Inset with a single inset applied to all // edges. -func UniformInset(v ui.Value) Inset { +func UniformInset(v unit.Value) Inset { return Inset{Top: v, Right: v, Bottom: v, Left: v} } diff --git a/layout/layout_test.go b/layout/layout_test.go index 5c5a8dd5..0524e0d2 100644 --- a/layout/layout_test.go +++ b/layout/layout_test.go @@ -7,7 +7,7 @@ import ( "gioui.org/io/event" "gioui.org/layout" - "gioui.org/ui" + "gioui.org/unit" ) type queue struct{} @@ -26,7 +26,7 @@ func ExampleInset() { gtx.Reset(cfg, cs) // Inset all edges by 10. - inset := layout.UniformInset(ui.Dp(10)) + inset := layout.UniformInset(unit.Dp(10)) inset.Layout(gtx, func() { // Lay out a 50x50 sized widget. layoutWidget(gtx, 50, 50) @@ -146,7 +146,7 @@ func (config) Now() time.Time { return time.Now() } -func (config) Px(v ui.Value) int { +func (config) Px(v unit.Value) int { return int(v.V + .5) } diff --git a/layout/list.go b/layout/list.go index 37f03b09..7a60cfeb 100644 --- a/layout/list.go +++ b/layout/list.go @@ -110,7 +110,7 @@ func (l *List) Dragging() bool { } func (l *List) update() { - d := l.scroll.Scroll(l.ctx.Config, l.ctx.Queue, gesture.Axis(l.Axis)) + d := l.scroll.Scroll(l.ctx.Config, l.ctx.Queue, l.ctx.Now(), gesture.Axis(l.Axis)) l.scrollDelta = d l.offset += d } diff --git a/op/op.go b/op/op.go index dace4a40..de851ef1 100644 --- a/op/op.go +++ b/op/op.go @@ -15,7 +15,7 @@ to a ui/app.Window's Update method. Drawing a colored square: - import "gioui.org/ui" + import "gioui.org/unit" import "gioui.org/app" import "gioui.org/op/paint" diff --git a/text/editor.go b/text/editor.go index 59c93fc4..74448c9f 100644 --- a/text/editor.go +++ b/text/editor.go @@ -16,7 +16,7 @@ import ( "gioui.org/layout" "gioui.org/op" "gioui.org/op/paint" - "gioui.org/ui" + "gioui.org/unit" "golang.org/x/image/math/fixed" ) @@ -88,7 +88,7 @@ const ( // Event returns the next available editor event, or false if none are available. func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) { // Crude configuration change detection. - if scale := gtx.Px(ui.Sp(100)); scale != e.oldScale { + if scale := gtx.Px(unit.Sp(100)); scale != e.oldScale { e.invalidate() e.oldScale = scale } @@ -102,7 +102,7 @@ func (e *Editor) Event(gtx *layout.Context) (EditorEvent, bool) { axis = gesture.Vertical smin, smax = sbounds.Min.Y, sbounds.Max.Y } - sdist := e.scroller.Scroll(gtx.Config, gtx.Queue, axis) + sdist := e.scroller.Scroll(gtx.Config, gtx.Queue, gtx.Now(), axis) var soff int if e.SingleLine { e.scrollOff.X += sdist @@ -167,8 +167,8 @@ func (e *Editor) editorEvent(gtx *layout.Context) (EditorEvent, bool) { return nil, false } -func (e *Editor) caretWidth(c ui.Config) fixed.Int26_6 { - oneDp := c.Px(ui.Dp(1)) +func (e *Editor) caretWidth(c unit.Converter) fixed.Int26_6 { + oneDp := c.Px(unit.Dp(1)) return fixed.Int26_6(oneDp * 64) } @@ -182,7 +182,7 @@ func (e *Editor) Layout(gtx *layout.Context) { cs := gtx.Constraints for _, ok := e.Event(gtx); ok; _, ok = e.Event(gtx) { } - twoDp := gtx.Px(ui.Dp(2)) + twoDp := gtx.Px(unit.Dp(2)) e.padLeft, e.padRight = twoDp, twoDp maxWidth := cs.Width.Max if e.SingleLine { @@ -275,7 +275,7 @@ func (e *Editor) Layout(gtx *layout.Context) { stack.Pop() baseline := e.padTop + e.dims.Baseline - pointerPadding := gtx.Px(ui.Dp(4)) + pointerPadding := gtx.Px(unit.Dp(4)) r := image.Rectangle{Max: e.viewSize} r.Min.X -= pointerPadding r.Min.Y -= pointerPadding @@ -563,7 +563,7 @@ func (e *Editor) moveEnd() { e.carXOff = l.Width + a - x } -func (e *Editor) scrollToCaret(c ui.Config) { +func (e *Editor) scrollToCaret(c unit.Converter) { carWidth := e.caretWidth(c) carLine, _, x, y := e.layoutCaret() l := e.lines[carLine] diff --git a/text/shape/measure.go b/text/shape/measure.go index 1c832dbe..df5643f8 100644 --- a/text/shape/measure.go +++ b/text/shape/measure.go @@ -14,7 +14,7 @@ import ( "gioui.org/op" "gioui.org/op/paint" "gioui.org/text" - "gioui.org/ui" + "gioui.org/unit" "golang.org/x/image/font" "golang.org/x/image/font/sfnt" "golang.org/x/image/math/fixed" @@ -22,7 +22,7 @@ import ( // Faces is a cache of text layouts and paths. type Faces struct { - config ui.Config + config unit.Converter faceCache map[faceKey]*Face layoutCache map[layoutKey]cachedLayout pathCache map[pathKey]cachedPath @@ -53,19 +53,19 @@ type pathKey struct { type faceKey struct { font *sfnt.Font - size ui.Value + size unit.Value } // Face is a cached implementation of text.Face. type Face struct { faces *Faces - size ui.Value + size unit.Value font *opentype } // Reset the cache, discarding any measures or paths that // haven't been used since the last call to Reset. -func (f *Faces) Reset(c ui.Config) { +func (f *Faces) Reset(c unit.Converter) { f.config = c f.init() for pk, p := range f.pathCache { @@ -87,7 +87,7 @@ func (f *Faces) Reset(c ui.Config) { } // For returns a Face for the given font and size. -func (f *Faces) For(fnt *sfnt.Font, size ui.Value) *Face { +func (f *Faces) For(fnt *sfnt.Font, size unit.Value) *Face { f.init() fk := faceKey{fnt, size} if f, exist := f.faceCache[fk]; exist { diff --git a/ui/doc.go b/ui/doc.go deleted file mode 100644 index 65ee355d..00000000 --- a/ui/doc.go +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -/* -Package ui defines operations buffers, units and common operations -for GUI programs written with the Gio module. - -Units - -A Value is a value with a Unit attached. - -Device independent pixel, or dp, is the unit for sizes independent of -the underlying display device. - -Scaled pixels, or sp, is the unit for text sizes. An sp is like dp with -text scaling applied. - -Finally, pixels, or px, is the unit for display dependent pixels. Their -size vary between platforms and displays. - -To maintain a constant visual size across platforms and displays, always -use dps or sps to define user interfaces. Only use pixels for derived -values. - -*/ -package ui diff --git a/ui/ui.go b/ui/ui.go deleted file mode 100644 index 8f083d4f..00000000 --- a/ui/ui.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package ui - -import ( - "time" -) - -// Config define the essential properties of -// the environment. -type Config interface { - // Now returns the current animation time. - Now() time.Time - // Px converts a Value to pixels. - Px(v Value) int -} diff --git a/ui/unit.go b/unit/unit.go similarity index 65% rename from ui/unit.go rename to unit/unit.go index 418fbfdf..0fdc60a1 100644 --- a/ui/unit.go +++ b/unit/unit.go @@ -1,6 +1,26 @@ // SPDX-License-Identifier: Unlicense OR MIT -package ui +/* + +Package unit implements device independent units and values. + +A Value is a value with a Unit attached. + +Device independent pixel, or dp, is the unit for sizes independent of +the underlying display device. + +Scaled pixels, or sp, is the unit for text sizes. An sp is like dp with +text scaling applied. + +Finally, pixels, or px, is the unit for display dependent pixels. Their +size vary between platforms and displays. + +To maintain a constant visual size across platforms and displays, always +use dps or sps to define user interfaces. Only use pixels for derived +values. + +*/ +package unit import "fmt" @@ -13,6 +33,11 @@ type Value struct { // Unit represents a unit for a Value. type Unit uint8 +// Converter converts Values to pixels. +type Converter interface { + Px(v Value) int +} + const ( // UnitPx represent device pixels in the resolution of // the underlying display. @@ -59,7 +84,7 @@ func (u Unit) String() string { } // Add a list of Values. -func Add(c Config, values ...Value) Value { +func Add(c Converter, values ...Value) Value { var sum Value for _, v := range values { sum, v = compatible(c, sum, v) @@ -69,7 +94,7 @@ func Add(c Config, values ...Value) Value { } // Max returns the maximum of a list of Values. -func Max(c Config, values ...Value) Value { +func Max(c Converter, values ...Value) Value { var max Value for _, v := range values { max, v = compatible(c, max, v) @@ -80,7 +105,7 @@ func Max(c Config, values ...Value) Value { return max } -func compatible(c Config, v1, v2 Value) (Value, Value) { +func compatible(c Converter, v1, v2 Value) (Value, Value) { if v1.U == v2.U { return v1, v2 } diff --git a/widget/image.go b/widget/image.go index a77e1e3f..8fc7cb38 100644 --- a/widget/image.go +++ b/widget/image.go @@ -9,7 +9,7 @@ import ( "gioui.org/f32" "gioui.org/layout" "gioui.org/op/paint" - "gioui.org/ui" + "gioui.org/unit" ) // Image is a widget that displays an image. @@ -31,7 +31,7 @@ func (im Image) Layout(gtx *layout.Context) { var w, h int if im.Scale == 0 { const dpPrPx = 160 / 72 - w, h = gtx.Px(ui.Dp(wf*dpPrPx)), gtx.Px(ui.Dp(hf*dpPrPx)) + w, h = gtx.Px(unit.Dp(wf*dpPrPx)), gtx.Px(unit.Dp(hf*dpPrPx)) } else { w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5) }