From 8cf35a1f97ff455e4de964c544b753332c18d4c3 Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Mon, 30 Sep 2019 15:41:15 +0200 Subject: [PATCH] op: add package op for operations Extract operation types from package ui into package op. Signed-off-by: Elias Naur --- app/internal/gpu/gpu.go | 12 ++-- app/internal/input/key.go | 4 +- app/internal/input/pointer.go | 16 ++--- app/internal/input/router.go | 8 +-- app/window.go | 11 ++-- cmd/gogio/testdata/red.go | 3 +- gesture/gesture.go | 7 +- internal/ops/ops.go | 6 +- internal/ops/reader.go | 16 ++--- io/event/event.go | 2 +- io/key/key.go | 6 +- io/pointer/doc.go | 6 +- io/pointer/pointer.go | 14 ++-- io/profile/profile.go | 4 +- layout/flex.go | 10 +-- layout/layout.go | 15 +++-- layout/list.go | 12 ++-- layout/stack.go | 10 +-- measure/measure.go | 13 ++-- ui/ops.go => op/op.go | 116 +++++++++++++++++++++++++++++++++- paint/paint.go | 8 +-- paint/path.go | 8 +-- text/editor.go | 13 ++-- text/label.go | 8 +-- text/measure.go | 4 +- ui/doc.go | 58 ----------------- ui/ui.go | 62 ------------------ 27 files changed, 225 insertions(+), 227 deletions(-) rename ui/ops.go => op/op.go (59%) diff --git a/app/internal/gpu/gpu.go b/app/internal/gpu/gpu.go index 8e05a1ce..56f5eb21 100644 --- a/app/internal/gpu/gpu.go +++ b/app/internal/gpu/gpu.go @@ -12,11 +12,11 @@ import ( "strings" "time" - "gioui.org/ui" "gioui.org/app/internal/gl" "gioui.org/f32" "gioui.org/internal/opconst" "gioui.org/internal/ops" + "gioui.org/op" "gioui.org/paint" "golang.org/x/image/draw" ) @@ -74,7 +74,7 @@ type drawOps struct { type drawState struct { clip f32.Rectangle - t ui.TransformOp + t op.TransformOp cpath *pathOp rect bool z int @@ -398,7 +398,7 @@ func (g *GPU) Refresh() { g.setErr(<-g.refreshErr) } -func (g *GPU) Draw(profile bool, viewport image.Point, root *ui.Ops) { +func (g *GPU) Draw(profile bool, viewport image.Point, root *op.Ops) { if g.err != nil { return } @@ -678,7 +678,7 @@ func (d *drawOps) reset(cache *resourceCache, viewport image.Point) { d.pathOpCache = d.pathOpCache[:0] } -func (d *drawOps) collect(cache *resourceCache, root *ui.Ops, viewport image.Point) { +func (d *drawOps) collect(cache *resourceCache, root *op.Ops, viewport image.Point) { d.reset(cache, viewport) clip := f32.Rectangle{ Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)}, @@ -704,8 +704,8 @@ loop: for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { switch opconst.OpType(encOp.Data[0]) { case opconst.TypeTransform: - op := ops.DecodeTransformOp(encOp.Data) - state.t = state.t.Multiply(ui.TransformOp(op)) + dop := ops.DecodeTransformOp(encOp.Data) + state.t = state.t.Multiply(op.TransformOp(dop)) case opconst.TypeAux: aux = encOp.Data[opconst.TypeAuxLen:] auxKey = encOp.Key diff --git a/app/internal/input/key.go b/app/internal/input/key.go index 7e607862..2fee78e6 100644 --- a/app/internal/input/key.go +++ b/app/internal/input/key.go @@ -7,7 +7,7 @@ import ( "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/key" - "gioui.org/ui" + "gioui.org/op" ) type TextInputState uint8 @@ -44,7 +44,7 @@ func (q *keyQueue) InputState() TextInputState { return q.state } -func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) { +func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) { if q.handlers == nil { q.handlers = make(map[event.Key]*keyHandler) } diff --git a/app/internal/input/pointer.go b/app/internal/input/pointer.go index 6be1e87d..cdaf2fa8 100644 --- a/app/internal/input/pointer.go +++ b/app/internal/input/pointer.go @@ -11,7 +11,7 @@ import ( "gioui.org/internal/ops" "gioui.org/io/event" "gioui.org/io/pointer" - "gioui.org/ui" + "gioui.org/op" ) type pointerQueue struct { @@ -42,7 +42,7 @@ type pointerInfo struct { type pointerHandler struct { area int active bool - transform ui.TransformOp + transform op.TransformOp wantsGrab bool } @@ -52,7 +52,7 @@ type areaOp struct { } type areaNode struct { - trans ui.TransformOp + trans op.TransformOp next int area areaOp } @@ -64,7 +64,7 @@ const ( areaEllipse ) -func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t ui.TransformOp, area, node int, pass bool) { +func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t op.TransformOp, area, node int, pass bool) { for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { switch opconst.OpType(encOp.Data[0]) { case opconst.TypePush: @@ -86,8 +86,8 @@ func (q *pointerQueue) collectHandlers(r *ops.Reader, events *handlerEvents, t u }) node = len(q.hitTree) - 1 case opconst.TypeTransform: - op := ops.DecodeTransformOp(encOp.Data) - t = t.Multiply(ui.TransformOp(op)) + dop := ops.DecodeTransformOp(encOp.Data) + t = t.Multiply(op.TransformOp(dop)) case opconst.TypePointerInput: op := decodePointerInputOp(encOp.Data, encOp.Refs) q.hitTree = append(q.hitTree, hitNode{ @@ -158,7 +158,7 @@ func (q *pointerQueue) init() { } } -func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) { +func (q *pointerQueue) Frame(root *op.Ops, events *handlerEvents) { q.init() for _, h := range q.handlers { // Reset handler. @@ -167,7 +167,7 @@ func (q *pointerQueue) Frame(root *ui.Ops, events *handlerEvents) { q.hitTree = q.hitTree[:0] q.areas = q.areas[:0] q.reader.Reset(root) - q.collectHandlers(&q.reader, events, ui.TransformOp{}, -1, -1, false) + q.collectHandlers(&q.reader, events, op.TransformOp{}, -1, -1, false) for k, h := range q.handlers { if !h.active { q.dropHandler(k) diff --git a/app/internal/input/router.go b/app/internal/input/router.go index c24488bb..93c5262d 100644 --- a/app/internal/input/router.go +++ b/app/internal/input/router.go @@ -12,7 +12,7 @@ import ( "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/profile" - "gioui.org/ui" + "gioui.org/op" ) // Router is a Queue implementation that routes events from @@ -53,7 +53,7 @@ func (q *Router) Events(k event.Key) []event.Event { return events } -func (q *Router) Frame(ops *ui.Ops) { +func (q *Router) Frame(ops *op.Ops) { q.handlers.Clear() q.wakeup = false q.profHandlers = q.profHandlers[:0] @@ -160,12 +160,12 @@ func decodeProfileOp(d []byte, refs []interface{}) profile.Op { } } -func decodeInvalidateOp(d []byte) ui.InvalidateOp { +func decodeInvalidateOp(d []byte) op.InvalidateOp { bo := binary.LittleEndian if opconst.OpType(d[0]) != opconst.TypeInvalidate { panic("invalid op") } - var o ui.InvalidateOp + var o op.InvalidateOp if nanos := bo.Uint64(d[1:]); nanos > 0 { o.At = time.Unix(0, int64(nanos)) } diff --git a/app/window.go b/app/window.go index fac67c58..361f3b90 100644 --- a/app/window.go +++ b/app/window.go @@ -12,6 +12,7 @@ import ( "gioui.org/app/internal/input" "gioui.org/io/event" "gioui.org/io/profile" + "gioui.org/op" "gioui.org/ui" ) @@ -36,7 +37,7 @@ type Window struct { in chan event.Event ack chan struct{} invalidates chan struct{} - frames chan *ui.Ops + frames chan *op.Ops stage Stage animating bool @@ -99,7 +100,7 @@ func NewWindow(options ...WindowOption) *Window { out: make(chan event.Event), ack: make(chan struct{}), invalidates: make(chan struct{}, 1), - frames: make(chan *ui.Ops), + frames: make(chan *op.Ops), } go w.run(opts) return w @@ -120,11 +121,11 @@ func (w *Window) Queue() *Queue { // window contents, input operations declare input handlers, // and so on. The supplied operations list completely replaces // the window state from previous calls. -func (w *Window) Update(frame *ui.Ops) { +func (w *Window) Update(frame *op.Ops) { w.frames <- frame } -func (w *Window) draw(size image.Point, frame *ui.Ops) { +func (w *Window) draw(size image.Point, frame *op.Ops) { var drawDur time.Duration if !w.drawStart.IsZero() { drawDur = time.Since(w.drawStart) @@ -265,7 +266,7 @@ func (w *Window) run(opts *windowOptions) { w.drawStart = time.Now() w.hasNextFrame = false w.out <- e - var frame *ui.Ops + var frame *op.Ops // Wait for either a frame or the ack event, // which meant that the client didn't draw. select { diff --git a/cmd/gogio/testdata/red.go b/cmd/gogio/testdata/red.go index ed2d525b..ebacea17 100644 --- a/cmd/gogio/testdata/red.go +++ b/cmd/gogio/testdata/red.go @@ -7,7 +7,6 @@ import ( "image/color" "log" - "gioui.org/ui" "gioui.org/app" "gioui.org/f32" "gioui.org/paint" @@ -25,7 +24,7 @@ func main() { func loop(w *app.Window) error { background := color.RGBA{255, 0, 0, 255} - ops := new(ui.Ops) + ops := new(op.Ops) for { e := <-w.Events() switch e := e.(type) { diff --git a/gesture/gesture.go b/gesture/gesture.go index 69283859..d914ea91 100644 --- a/gesture/gesture.go +++ b/gesture/gesture.go @@ -16,6 +16,7 @@ import ( "gioui.org/internal/fling" "gioui.org/io/event" "gioui.org/io/pointer" + "gioui.org/op" "gioui.org/ui" ) @@ -95,7 +96,7 @@ const ( var touchSlop = ui.Dp(3) // Add the handler to the operation list to receive click events. -func (c *Click) Add(ops *ui.Ops) { +func (c *Click) Add(ops *op.Ops) { op := pointer.InputOp{Key: c} op.Add(ops) } @@ -140,11 +141,11 @@ func (c *Click) Events(q event.Queue) []ClickEvent { } // Add the handler to the operation list to receive scroll events. -func (s *Scroll) Add(ops *ui.Ops) { +func (s *Scroll) Add(ops *op.Ops) { oph := pointer.InputOp{Key: s, Grab: s.grab} oph.Add(ops) if s.flinger.Active() { - ui.InvalidateOp{}.Add(ops) + op.InvalidateOp{}.Add(ops) } } diff --git a/internal/ops/ops.go b/internal/ops/ops.go index 27e17084..c8a9f89d 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -6,17 +6,17 @@ import ( "encoding/binary" "math" - "gioui.org/ui" "gioui.org/f32" "gioui.org/internal/opconst" + "gioui.org/op" ) -func DecodeTransformOp(d []byte) ui.TransformOp { +func DecodeTransformOp(d []byte) op.TransformOp { bo := binary.LittleEndian if opconst.OpType(d[0]) != opconst.TypeTransform { panic("invalid op") } - return ui.TransformOp{}.Offset(f32.Point{ + return op.TransformOp{}.Offset(f32.Point{ X: math.Float32frombits(bo.Uint32(d[1:])), Y: math.Float32frombits(bo.Uint32(d[5:])), }) diff --git a/internal/ops/reader.go b/internal/ops/reader.go index 8cd60de5..25f30903 100644 --- a/internal/ops/reader.go +++ b/internal/ops/reader.go @@ -5,15 +5,15 @@ package ops import ( "encoding/binary" - "gioui.org/ui" "gioui.org/internal/opconst" + "gioui.org/op" ) // Reader parses an ops list. type Reader struct { pc pc stack []macro - ops *ui.Ops + ops *op.Ops } // EncodedOp represents an encoded op returned by @@ -26,14 +26,14 @@ type EncodedOp struct { // Key is a unique key for a given op. type Key struct { - ops *ui.Ops + ops *op.Ops pc int version int } -// Shadow of ui.MacroOp. +// Shadow of op.MacroOp. type macroOp struct { - ops *ui.Ops + ops *op.Ops version int pc pc } @@ -44,7 +44,7 @@ type pc struct { } type macro struct { - ops *ui.Ops + ops *op.Ops retPC pc endPC pc } @@ -58,7 +58,7 @@ type opAux struct { } // Reset start reading from the op list. -func (r *Reader) Reset(ops *ui.Ops) { +func (r *Reader) Reset(ops *op.Ops) { r.stack = r.stack[:0] r.pc = pc{} r.ops = ops @@ -170,7 +170,7 @@ func (m *macroOp) decode(data []byte, refs []interface{}) { refsIdx := int(int32(bo.Uint32(data[5:]))) version := int(int32(bo.Uint32(data[9:]))) *m = macroOp{ - ops: refs[0].(*ui.Ops), + ops: refs[0].(*op.Ops), pc: pc{ data: dataIdx, refs: refsIdx, diff --git a/io/event/event.go b/io/event/event.go index e91c2776..ecbdca88 100644 --- a/io/event/event.go +++ b/io/event/event.go @@ -23,7 +23,7 @@ The following example declares a handler ready for key input: import gioui.org/io/key - ops := new(ui.Ops) + ops := new(op.Ops) var h *Handler = ... key.InputOp{Key: h}.Add(ops) diff --git a/io/key/key.go b/io/key/key.go index 12ae46dd..e020d8c4 100644 --- a/io/key/key.go +++ b/io/key/key.go @@ -12,7 +12,7 @@ package key import ( "gioui.org/internal/opconst" "gioui.org/io/event" - "gioui.org/ui" + "gioui.org/op" ) // InputOp declares a handler ready for key events. @@ -86,7 +86,7 @@ func (m Modifiers) Contain(m2 Modifiers) bool { return m&m2 == m2 } -func (h InputOp) Add(o *ui.Ops) { +func (h InputOp) Add(o *op.Ops) { data := make([]byte, opconst.TypeKeyInputLen) data[0] = byte(opconst.TypeKeyInput) if h.Focus { @@ -95,7 +95,7 @@ func (h InputOp) Add(o *ui.Ops) { o.Write(data, h.Key) } -func (h HideInputOp) Add(o *ui.Ops) { +func (h HideInputOp) Add(o *op.Ops) { data := make([]byte, opconst.TypeHideInputLen) data[0] = byte(opconst.TypeHideInput) o.Write(data) diff --git a/io/pointer/doc.go b/io/pointer/doc.go index f023e74d..5fe2673b 100644 --- a/io/pointer/doc.go +++ b/io/pointer/doc.go @@ -15,7 +15,7 @@ subsequent InputOp are active. For example, to set up a rectangular hit area: - var ops ui.Ops + var ops op.Ops var h *Handler = ... r := image.Rectangle{...} @@ -33,8 +33,8 @@ with the most recent node. For example: - ops := new(ui.Ops) - var stack ui.StackOp + ops := new(op.Ops) + var stack op.StackOp var h1, h2 *Handler stack.Push(ops) diff --git a/io/pointer/pointer.go b/io/pointer/pointer.go index fb784adb..c8fd069d 100644 --- a/io/pointer/pointer.go +++ b/io/pointer/pointer.go @@ -10,7 +10,7 @@ import ( "gioui.org/f32" "gioui.org/internal/opconst" "gioui.org/io/event" - "gioui.org/ui" + "gioui.org/op" ) // Event is a pointer event. @@ -33,7 +33,7 @@ type Event struct { // outside it. Hit bool // Position is the position of the event, relative to - // the current transformation, as set by ui.TransformOp. + // the current transformation, as set by op.TransformOp. Position f32.Point // Scroll is the scroll amount, if any. Scroll f32.Point @@ -124,21 +124,21 @@ const ( areaEllipse ) -func (op RectAreaOp) Add(ops *ui.Ops) { +func (op RectAreaOp) Add(ops *op.Ops) { areaOp{ kind: areaRect, rect: op.Rect, }.add(ops) } -func (op EllipseAreaOp) Add(ops *ui.Ops) { +func (op EllipseAreaOp) Add(ops *op.Ops) { areaOp{ kind: areaEllipse, rect: op.Rect, }.add(ops) } -func (op areaOp) add(o *ui.Ops) { +func (op areaOp) add(o *op.Ops) { data := make([]byte, opconst.TypeAreaLen) data[0] = byte(opconst.TypeArea) data[1] = byte(op.kind) @@ -150,7 +150,7 @@ func (op areaOp) add(o *ui.Ops) { o.Write(data) } -func (h InputOp) Add(o *ui.Ops) { +func (h InputOp) Add(o *op.Ops) { data := make([]byte, opconst.TypePointerInputLen) data[0] = byte(opconst.TypePointerInput) if h.Grab { @@ -159,7 +159,7 @@ func (h InputOp) Add(o *ui.Ops) { o.Write(data, h.Key) } -func (op PassOp) Add(o *ui.Ops) { +func (op PassOp) Add(o *op.Ops) { data := make([]byte, opconst.TypePassLen) data[0] = byte(opconst.TypePass) if op.Pass { diff --git a/io/profile/profile.go b/io/profile/profile.go index d7ab2ce0..bdf774fe 100644 --- a/io/profile/profile.go +++ b/io/profile/profile.go @@ -7,7 +7,7 @@ package profile import ( "gioui.org/internal/opconst" "gioui.org/io/event" - "gioui.org/ui" + "gioui.org/op" ) // Op registers a handler for receiving @@ -23,7 +23,7 @@ type Event struct { Timings string } -func (p Op) Add(o *ui.Ops) { +func (p Op) Add(o *op.Ops) { data := make([]byte, opconst.TypeProfileLen) data[0] = byte(opconst.TypeProfile) o.Write(data, p.Key) diff --git a/layout/flex.go b/layout/flex.go index 5e6ac636..9957103c 100644 --- a/layout/flex.go +++ b/layout/flex.go @@ -6,7 +6,7 @@ import ( "image" "gioui.org/f32" - "gioui.org/ui" + "gioui.org/op" ) // Flex lays out child elements along an axis, @@ -21,7 +21,7 @@ type Flex struct { Alignment Alignment ctx *Context - macro ui.MacroOp + macro op.MacroOp mode flexMode size int rigidSize int @@ -33,7 +33,7 @@ type Flex struct { // FlexChild is the layout result of a call End. type FlexChild struct { - macro ui.MacroOp + macro op.MacroOp dims Dimensions } @@ -189,9 +189,9 @@ func (f *Flex) Layout(children ...FlexChild) { cross = f.maxBaseline - b } } - var stack ui.StackOp + var stack op.StackOp stack.Push(f.ctx.Ops) - ui.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ctx.Ops) + op.TransformOp{}.Offset(toPointF(axisPoint(f.Axis, mainSize, cross))).Add(f.ctx.Ops) child.macro.Add(f.ctx.Ops) stack.Pop() mainSize += axisMain(f.Axis, dims.Size) diff --git a/layout/layout.go b/layout/layout.go index e9048f04..7cb9715c 100644 --- a/layout/layout.go +++ b/layout/layout.go @@ -6,6 +6,7 @@ import ( "image" "gioui.org/io/event" + "gioui.org/op" "gioui.org/ui" ) @@ -53,7 +54,7 @@ type Context struct { ui.Config event.Queue - *ui.Ops + *op.Ops } const ( @@ -97,7 +98,7 @@ func (c *Context) Reset(cfg ui.Config, cs Constraints) { c.Dimensions = Dimensions{} c.Config = cfg if c.Ops == nil { - c.Ops = new(ui.Ops) + c.Ops = new(op.Ops) } c.Ops.Reset() } @@ -157,9 +158,9 @@ func (in Inset) Layout(gtx *Context, w Widget) { if mcs.Height.Max < mcs.Height.Min { mcs.Height.Max = mcs.Height.Min } - var stack ui.StackOp + var stack op.StackOp stack.Push(gtx.Ops) - ui.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(gtx.Ops) + op.TransformOp{}.Offset(toPointF(image.Point{X: left, Y: top})).Add(gtx.Ops) dims := gtx.Layout(mcs, w) stack.Pop() gtx.Dimensions = Dimensions{ @@ -176,7 +177,7 @@ func UniformInset(v ui.Value) Inset { // Layout a widget. func (a Align) Layout(gtx *Context, w Widget) { - var macro ui.MacroOp + var macro op.MacroOp macro.Record(gtx.Ops) cs := gtx.Constraints mcs := cs @@ -204,9 +205,9 @@ func (a Align) Layout(gtx *Context, w Widget) { case SW, S, SE: p.Y = sz.Y - dims.Size.Y } - var stack ui.StackOp + var stack op.StackOp stack.Push(gtx.Ops) - ui.TransformOp{}.Offset(toPointF(p)).Add(gtx.Ops) + op.TransformOp{}.Offset(toPointF(p)).Add(gtx.Ops) macro.Add(gtx.Ops) stack.Pop() gtx.Dimensions = Dimensions{ diff --git a/layout/list.go b/layout/list.go index 6cd7b804..08c41da7 100644 --- a/layout/list.go +++ b/layout/list.go @@ -7,13 +7,13 @@ import ( "gioui.org/gesture" "gioui.org/io/pointer" + "gioui.org/op" "gioui.org/paint" - "gioui.org/ui" ) type scrollChild struct { size image.Point - macro ui.MacroOp + macro op.MacroOp } // List displays a subsection of a potentially infinitely @@ -33,8 +33,8 @@ type List struct { beforeEnd bool ctx *Context - macro ui.MacroOp - child ui.MacroOp + macro op.MacroOp + child op.MacroOp scroll gesture.Scroll scrollDelta int @@ -246,10 +246,10 @@ func (l *List) layout() Dimensions { Min: axisPoint(l.Axis, min, -inf), Max: axisPoint(l.Axis, max, inf), } - var stack ui.StackOp + var stack op.StackOp stack.Push(ops) paint.RectClip(r).Add(ops) - ui.TransformOp{}.Offset(toPointF(axisPoint(l.Axis, pos, cross))).Add(ops) + op.TransformOp{}.Offset(toPointF(axisPoint(l.Axis, pos, cross))).Add(ops) child.macro.Add(ops) stack.Pop() pos += childSize diff --git a/layout/stack.go b/layout/stack.go index b816ce6f..c98ace4c 100644 --- a/layout/stack.go +++ b/layout/stack.go @@ -5,7 +5,7 @@ package layout import ( "image" - "gioui.org/ui" + "gioui.org/op" ) // Stack lays out child elements on top of each other, @@ -15,7 +15,7 @@ type Stack struct { // smaller than the available space. Alignment Direction - macro ui.MacroOp + macro op.MacroOp constrained bool ctx *Context maxSZ image.Point @@ -24,7 +24,7 @@ type Stack struct { // StackChild is the layout result of a call to End. type StackChild struct { - macro ui.MacroOp + macro op.MacroOp dims Dimensions } @@ -99,9 +99,9 @@ func (s *Stack) Layout(children ...StackChild) { case SW, S, SE: p.Y = s.maxSZ.Y - sz.Y } - var stack ui.StackOp + var stack op.StackOp stack.Push(s.ctx.Ops) - ui.TransformOp{}.Offset(toPointF(p)).Add(s.ctx.Ops) + op.TransformOp{}.Offset(toPointF(p)).Add(s.ctx.Ops) ch.macro.Add(s.ctx.Ops) stack.Pop() } diff --git a/measure/measure.go b/measure/measure.go index 1b7c99da..370c1457 100644 --- a/measure/measure.go +++ b/measure/measure.go @@ -10,10 +10,11 @@ import ( "unicode" "unicode/utf8" - "gioui.org/ui" "gioui.org/f32" + "gioui.org/op" "gioui.org/paint" "gioui.org/text" + "gioui.org/ui" "golang.org/x/image/font" "golang.org/x/image/font/sfnt" "golang.org/x/image/math/fixed" @@ -34,7 +35,7 @@ type cachedLayout struct { type cachedPath struct { active bool - path ui.MacroOp + path op.MacroOp } type layoutKey struct { @@ -128,7 +129,7 @@ func (f *Face) Layout(str string, opts text.LayoutOptions) *text.Layout { return l } -func (f *Face) Path(str text.String) ui.MacroOp { +func (f *Face) Path(str text.String) op.MacroOp { ppem := fixed.Int26_6(f.faces.config.Px(f.size) * 64) pk := pathKey{ f: f.font.Font, @@ -234,14 +235,14 @@ func layoutText(ppem fixed.Int26_6, str string, f *opentype, opts text.LayoutOpt return &text.Layout{Lines: lines} } -func textPath(ppem fixed.Int26_6, f *opentype, str text.String) ui.MacroOp { +func textPath(ppem fixed.Int26_6, f *opentype, str text.String) op.MacroOp { var lastPos f32.Point var builder paint.PathBuilder - ops := new(ui.Ops) + ops := new(op.Ops) builder.Init(ops) var x fixed.Int26_6 var advIdx int - var m ui.MacroOp + var m op.MacroOp m.Record(ops) for _, r := range str.String { if !unicode.IsSpace(r) { diff --git a/ui/ops.go b/op/op.go similarity index 59% rename from ui/ops.go rename to op/op.go index e667934a..24ce687c 100644 --- a/ui/ops.go +++ b/op/op.go @@ -1,10 +1,67 @@ // SPDX-License-Identifier: Unlicense OR MIT -package ui +/* + +Package op implements operations for updating a user interface. + +Gio programs use operations, or ops, for describing their user +interfaces. There are operations for drawing, defining input +handlers, changing window properties as well as operations for +controlling the execution of other operations. + +Ops represents a list of operations. The most important use +for an Ops list is to describe a complete user interface update +to a ui/app.Window's Update method. + +Drawing a colored square: + + import "gioui.org/ui" + import "gioui.org/app" + import "gioui.org/paint" + + var w app.Window + ops := new(op.Ops) + ... + ops.Reset() + paint.ColorOp{Color: ...}.Add(ops) + paint.PaintOp{Rect: ...}.Add(ops) + w.Update(ops) + +State + +An Ops list can be viewed as a very simple virtual machine: it has an implicit +mutable state stack and execution flow can be controlled with macros. + +The StackOp saves the current state to the state stack and restores it later: + + ops := new(op.Ops) + var stack op.StackOp + // Save the current state, in particular the transform. + stack.Push(ops) + // Apply a transform to subsequent operations. + op.TransformOp{}.Offset(...).Add(ops) + ... + // Restore the previous transform. + stack.Pop() + +The MacroOp records a list of operations to be executed later: + + ops := new(op.Ops) + var macro op.MacroOp + macro.Record() + // Record operations by adding them. + op.InvalidateOp{}.Add(ops) + ... + +*/ +package op import ( "encoding/binary" + "math" + "time" + "gioui.org/f32" "gioui.org/internal/opconst" ) @@ -45,6 +102,18 @@ type MacroOp struct { pc pc } +// InvalidateOp requests a redraw at the given time. Use +// the zero value to request an immediate redraw. +type InvalidateOp struct { + At time.Time +} + +// TransformOp applies a transform to the current transform. +type TransformOp struct { + // TODO: general transformations. + offset f32.Point +} + type pc struct { data int refs int @@ -208,3 +277,48 @@ func (m MacroOp) Add(o *Ops) { bo.PutUint32(data[9:], uint32(m.version)) o.Write(data, m.ops) } + +func (r InvalidateOp) Add(o *Ops) { + data := make([]byte, opconst.TypeRedrawLen) + data[0] = byte(opconst.TypeInvalidate) + bo := binary.LittleEndian + // UnixNano cannot represent the zero time. + if t := r.At; !t.IsZero() { + nanos := t.UnixNano() + if nanos > 0 { + bo.PutUint64(data[1:], uint64(nanos)) + } + } + o.Write(data) +} + +// Offset the transformation. +func (t TransformOp) Offset(o f32.Point) TransformOp { + return t.Multiply(TransformOp{o}) +} + +// Invert the transformation. +func (t TransformOp) Invert() TransformOp { + return TransformOp{offset: t.offset.Mul(-1)} +} + +// Transform a point. +func (t TransformOp) Transform(p f32.Point) f32.Point { + return p.Add(t.offset) +} + +// Multiply by a transformation. +func (t TransformOp) Multiply(t2 TransformOp) TransformOp { + return TransformOp{ + offset: t.offset.Add(t2.offset), + } +} + +func (t TransformOp) Add(o *Ops) { + data := make([]byte, opconst.TypeTransformLen) + data[0] = byte(opconst.TypeTransform) + bo := binary.LittleEndian + bo.PutUint32(data[1:], math.Float32bits(t.offset.X)) + bo.PutUint32(data[5:], math.Float32bits(t.offset.Y)) + o.Write(data) +} diff --git a/paint/paint.go b/paint/paint.go index 160f45bd..579fd34e 100644 --- a/paint/paint.go +++ b/paint/paint.go @@ -8,9 +8,9 @@ import ( "image/color" "math" - "gioui.org/ui" "gioui.org/f32" "gioui.org/internal/opconst" + "gioui.org/op" ) // ImageOp sets the material to a section of an @@ -33,7 +33,7 @@ type PaintOp struct { Rect f32.Rectangle } -func (i ImageOp) Add(o *ui.Ops) { +func (i ImageOp) Add(o *op.Ops) { data := make([]byte, opconst.TypeImageLen) data[0] = byte(opconst.TypeImage) bo := binary.LittleEndian @@ -44,7 +44,7 @@ func (i ImageOp) Add(o *ui.Ops) { o.Write(data, i.Src) } -func (c ColorOp) Add(o *ui.Ops) { +func (c ColorOp) Add(o *op.Ops) { data := make([]byte, opconst.TypeColorLen) data[0] = byte(opconst.TypeColor) data[1] = c.Color.R @@ -54,7 +54,7 @@ func (c ColorOp) Add(o *ui.Ops) { o.Write(data) } -func (d PaintOp) Add(o *ui.Ops) { +func (d PaintOp) Add(o *op.Ops) { data := make([]byte, opconst.TypePaintLen) data[0] = byte(opconst.TypePaint) bo := binary.LittleEndian diff --git a/paint/path.go b/paint/path.go index 89265361..56e4750b 100644 --- a/paint/path.go +++ b/paint/path.go @@ -7,10 +7,10 @@ import ( "math" "unsafe" - "gioui.org/ui" "gioui.org/f32" "gioui.org/internal/opconst" "gioui.org/internal/path" + "gioui.org/op" ) // PathBuilder builds and adds a general ClipOp clip path @@ -19,7 +19,7 @@ import ( // dynamic paths; path data is stored directly in the Ops // list supplied to Init. type PathBuilder struct { - ops *ui.Ops + ops *op.Ops firstVert int nverts int maxy float32 @@ -33,7 +33,7 @@ type ClipOp struct { bounds f32.Rectangle } -func (p ClipOp) Add(o *ui.Ops) { +func (p ClipOp) Add(o *op.Ops) { data := make([]byte, opconst.TypeClipLen) data[0] = byte(opconst.TypeClip) bo := binary.LittleEndian @@ -46,7 +46,7 @@ func (p ClipOp) Add(o *ui.Ops) { // Init the builder and specify the operations list for // storing the path data and final ClipOp. -func (p *PathBuilder) Init(ops *ui.Ops) { +func (p *PathBuilder) Init(ops *op.Ops) { p.ops = ops } diff --git a/text/editor.go b/text/editor.go index d0117027..da547b82 100644 --- a/text/editor.go +++ b/text/editor.go @@ -14,6 +14,7 @@ import ( "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/layout" + "gioui.org/op" "gioui.org/paint" "gioui.org/ui" @@ -33,12 +34,12 @@ type Editor struct { Submit bool // Material for drawing the text. - Material ui.MacroOp + Material op.MacroOp // Hint contains the text displayed to the user when the // Editor is empty. Hint string // Mmaterial is used to draw the hint. - HintMaterial ui.MacroOp + HintMaterial op.MacroOp oldScale int blinkStart time.Time @@ -218,7 +219,7 @@ func (e *Editor) Layout(gtx *layout.Context) { Width: e.viewWidth(), Offset: off, } - var stack ui.StackOp + var stack op.StackOp stack.Push(gtx.Ops) // Apply material. Set a default color in case the material is empty. if e.rr.len() > 0 { @@ -233,9 +234,9 @@ func (e *Editor) Layout(gtx *layout.Context) { if !ok { break } - var stack ui.StackOp + var stack op.StackOp stack.Push(gtx.Ops) - ui.TransformOp{}.Offset(lineOff).Add(gtx.Ops) + op.TransformOp{}.Offset(lineOff).Add(gtx.Ops) e.Face.Path(str).Add(gtx.Ops) paint.PaintOp{Rect: toRectF(clip).Sub(lineOff)}.Add(gtx.Ops) stack.Pop() @@ -267,7 +268,7 @@ func (e *Editor) Layout(gtx *layout.Context) { } } if blinking { - redraw := ui.InvalidateOp{At: nextBlink} + redraw := op.InvalidateOp{At: nextBlink} redraw.Add(gtx.Ops) } } diff --git a/text/label.go b/text/label.go index 423034a7..23a7a3fa 100644 --- a/text/label.go +++ b/text/label.go @@ -8,9 +8,9 @@ import ( "image/color" "unicode/utf8" - "gioui.org/ui" "gioui.org/f32" "gioui.org/layout" + "gioui.org/op" "gioui.org/paint" "golang.org/x/image/math/fixed" @@ -22,7 +22,7 @@ type Label struct { Face Face // Material is a macro recording the material to draw the // text. Use a ColorOp for colored text. - Material ui.MacroOp + Material op.MacroOp // Alignment specify the text alignment. Alignment Alignment // Text is the string to draw. @@ -118,9 +118,9 @@ func (l Label) Layout(gtx *layout.Context) { break } lclip := toRectF(clip).Sub(off) - var stack ui.StackOp + var stack op.StackOp stack.Push(gtx.Ops) - ui.TransformOp{}.Offset(off).Add(gtx.Ops) + op.TransformOp{}.Offset(off).Add(gtx.Ops) l.Face.Path(str).Add(gtx.Ops) // Set a default color in case the material is empty. paint.ColorOp{Color: color.RGBA{A: 0xff}}.Add(gtx.Ops) diff --git a/text/measure.go b/text/measure.go index 62865d41..a4388bb4 100644 --- a/text/measure.go +++ b/text/measure.go @@ -6,8 +6,8 @@ import ( "fmt" "image" - "gioui.org/ui" "gioui.org/layout" + "gioui.org/op" "golang.org/x/image/math/fixed" ) @@ -50,7 +50,7 @@ type Face interface { // options. Layout(s string, opts LayoutOptions) *Layout // Path returns the ClipOp outline of a text recorded in a macro. - Path(s String) ui.MacroOp + Path(s String) op.MacroOp } type Alignment uint8 diff --git a/ui/doc.go b/ui/doc.go index 79b78a05..65ee355d 100644 --- a/ui/doc.go +++ b/ui/doc.go @@ -4,64 +4,6 @@ Package ui defines operations buffers, units and common operations for GUI programs written with the Gio module. -Operations - -Gio programs use operations, or ops, for describing their user -interfaces. There are operations for drawing, defining input -handlers, changing window properties as well as operations for -controlling the execution of other operations. - -Ops represents a list of operations. The most important use -for an Ops list is to describe a complete user interface update -to a ui/app.Window's Update method. - -Drawing a colored square: - - import "gioui.org/ui" - import "gioui.org/app" - import "gioui.org/paint" - - var w app.Window - ops := new(ui.Ops) - ... - ops.Reset() - paint.ColorOp{Color: ...}.Add(ops) - paint.PaintOp{Rect: ...}.Add(ops) - w.Update(ops) - -State - -An Ops list can be viewed as a very simple virtual machine: it has an implicit -mutable state stack and execution flow can be controlled with macros. - -The StackOp saves the current state to the state stack and restores it later: - - ops := new(ui.Ops) - var stack ui.StackOp - // Save the current state, in particular the transform. - stack.Push(ops) - // Apply a transform to subsequent operations. - ui.TransformOp{}.Offset(...).Add(ops) - ... - // Restore the previous transform. - stack.Pop() - -The MacroOp records a list of operations to be executed later: - - ops := new(ui.Ops) - var macro ui.MacroOp - macro.Record() - // Record operations by adding them. - ui.InvalidateOp{}.Add(ops) - ... - macro.Stop() - ... - // Execute the recorded operations. - macro.Add(ops) - -Note that operations added between Record and Stop are not executed until -the macro is Added. - Units A Value is a value with a Unit attached. diff --git a/ui/ui.go b/ui/ui.go index 5ed1bd1e..8f083d4f 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -3,12 +3,7 @@ package ui import ( - "encoding/binary" - "math" "time" - - "gioui.org/f32" - "gioui.org/internal/opconst" ) // Config define the essential properties of @@ -19,60 +14,3 @@ type Config interface { // Px converts a Value to pixels. Px(v Value) int } - -// InvalidateOp requests a redraw at the given time. Use -// the zero value to request an immediate redraw. -type InvalidateOp struct { - At time.Time -} - -// TransformOp applies a transform to the current transform. -type TransformOp struct { - // TODO: general transformations. - offset f32.Point -} - -func (r InvalidateOp) Add(o *Ops) { - data := make([]byte, opconst.TypeRedrawLen) - data[0] = byte(opconst.TypeInvalidate) - bo := binary.LittleEndian - // UnixNano cannot represent the zero time. - if t := r.At; !t.IsZero() { - nanos := t.UnixNano() - if nanos > 0 { - bo.PutUint64(data[1:], uint64(nanos)) - } - } - o.Write(data) -} - -// Offset the transformation. -func (t TransformOp) Offset(o f32.Point) TransformOp { - return t.Multiply(TransformOp{o}) -} - -// Invert the transformation. -func (t TransformOp) Invert() TransformOp { - return TransformOp{offset: t.offset.Mul(-1)} -} - -// Transform a point. -func (t TransformOp) Transform(p f32.Point) f32.Point { - return p.Add(t.offset) -} - -// Multiply by a transformation. -func (t TransformOp) Multiply(t2 TransformOp) TransformOp { - return TransformOp{ - offset: t.offset.Add(t2.offset), - } -} - -func (t TransformOp) Add(o *Ops) { - data := make([]byte, opconst.TypeTransformLen) - data[0] = byte(opconst.TypeTransform) - bo := binary.LittleEndian - bo.PutUint32(data[1:], math.Float32bits(t.offset.X)) - bo.PutUint32(data[5:], math.Float32bits(t.offset.Y)) - o.Write(data) -}