app,io/system,layout: [API] move FrameEvent and Insets to package app

In the early days of Gio, FrameEvent was part of package app. It was
moved to package system to enable layout.NewContext be a convenient
short-hand for constructing a layout.

However, it seems the better design to leave FrameEvent (and Insets) in
package app, and move layout.NewContext there as well. More importantly,
the move allows us to replace the event.Queue interface with a concrete
type.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2023-10-08 12:21:41 -05:00
parent 60bfb9e064
commit cb1e605203
16 changed files with 103 additions and 107 deletions
+80 -6
View File
@@ -3,9 +3,16 @@
package app package app
import ( import (
"image"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"gioui.org/io/event"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
) )
// extraArgs contains extra arguments to append to // extraArgs contains extra arguments to append to
@@ -30,13 +37,68 @@ var extraArgs string
// is not supported. Default value of ID is filepath.Base(os.Args[0]). // is not supported. Default value of ID is filepath.Base(os.Args[0]).
var ID = "" var ID = ""
func init() { // A FrameEvent requests a new frame in the form of a list of
if extraArgs != "" { // operations that describes what to display and how to handle
args := strings.Split(extraArgs, "|") // input.
os.Args = append(os.Args, args...) type FrameEvent struct {
// Now is the current animation. Use Now instead of time.Now to
// synchronize animation and to avoid the time.Now call overhead.
Now time.Time
// Metric converts device independent dp and sp to device pixels.
Metric unit.Metric
// Size is the dimensions of the window.
Size image.Point
// Insets represent the space occupied by system decorations and controls.
Insets Insets
// Frame completes the FrameEvent by drawing the graphical operations
// from ops into the window.
Frame func(frame *op.Ops)
// Queue supplies the events for event handlers.
Queue event.Queue
} }
if ID == "" {
ID = filepath.Base(os.Args[0]) // Insets is the space taken up by
// system decoration such as translucent
// system bars and software keyboards.
type Insets struct {
// Values are in pixels.
Top, Bottom, Left, Right unit.Dp
}
// NewContext is a shorthand for
//
// layout.Context{
// Ops: ops,
// Now: e.Now,
// Queue: e.Queue,
// Config: e.Config,
// Constraints: layout.Exact(e.Size),
// }
//
// NewContext calls ops.Reset and adjusts ops for e.Insets.
func NewContext(ops *op.Ops, e FrameEvent) layout.Context {
ops.Reset()
size := e.Size
if e.Insets != (Insets{}) {
left := e.Metric.Dp(e.Insets.Left)
top := e.Metric.Dp(e.Insets.Top)
op.Offset(image.Point{
X: left,
Y: top,
}).Add(ops)
size.X -= left + e.Metric.Dp(e.Insets.Right)
size.Y -= top + e.Metric.Dp(e.Insets.Bottom)
}
return layout.Context{
Ops: ops,
Now: e.Now,
Queue: e.Queue,
Metric: e.Metric,
Constraints: layout.Exact(size),
} }
} }
@@ -63,3 +125,15 @@ func DataDir() (string, error) {
func Main() { func Main() {
osMain() osMain()
} }
func (FrameEvent) ImplementsEvent() {}
func init() {
if extraArgs != "" {
args := strings.Split(extraArgs, "|")
os.Args = append(os.Args, args...)
}
if ID == "" {
ID = filepath.Base(os.Args[0])
}
}
+1 -3
View File
@@ -17,12 +17,10 @@ FrameEvent that prompts an update of the window contents.
For example: For example:
import "gioui.org/unit"
w := app.NewWindow() w := app.NewWindow()
for { for {
e := w.NextEvent() e := w.NextEvent()
if e, ok := e.(system.FrameEvent); ok { if e, ok := e.(app.FrameEvent); ok {
ops.Reset() ops.Reset()
// Add operations to ops. // Add operations to ops.
... ...
+1 -1
View File
@@ -132,7 +132,7 @@ func (o Orientation) String() string {
} }
type frameEvent struct { type frameEvent struct {
system.FrameEvent FrameEvent
Sync bool Sync bool
} }
+2 -2
View File
@@ -836,14 +836,14 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
const inchPrDp = 1.0 / 160 const inchPrDp = 1.0 / 160
ppdp := float32(w.dpi) * inchPrDp ppdp := float32(w.dpi) * inchPrDp
dppp := unit.Dp(1.0 / ppdp) dppp := unit.Dp(1.0 / ppdp)
insets := system.Insets{ insets := Insets{
Top: unit.Dp(w.insets.top) * dppp, Top: unit.Dp(w.insets.top) * dppp,
Bottom: unit.Dp(w.insets.bottom) * dppp, Bottom: unit.Dp(w.insets.bottom) * dppp,
Left: unit.Dp(w.insets.left) * dppp, Left: unit.Dp(w.insets.left) * dppp,
Right: unit.Dp(w.insets.right) * dppp, Right: unit.Dp(w.insets.right) * dppp,
} }
w.callbacks.Event(frameEvent{ w.callbacks.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Insets: insets, Insets: insets,
+2 -2
View File
@@ -156,13 +156,13 @@ func (w *window) draw(sync bool) {
} }
dppp := unit.Dp(1. / m.PxPerDp) dppp := unit.Dp(1. / m.PxPerDp)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: image.Point{
X: int(params.width + .5), X: int(params.width + .5),
Y: int(params.height + .5), Y: int(params.height + .5),
}, },
Insets: system.Insets{ Insets: Insets{
Top: unit.Dp(params.top) * dppp, Top: unit.Dp(params.top) * dppp,
Bottom: unit.Dp(params.bottom) * dppp, Bottom: unit.Dp(params.bottom) * dppp,
Left: unit.Dp(params.left) * dppp, Left: unit.Dp(params.left) * dppp,
+3 -3
View File
@@ -666,7 +666,7 @@ func (w *window) draw(sync bool) {
} }
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: size, Size: size,
Insets: insets, Insets: insets,
@@ -676,10 +676,10 @@ func (w *window) draw(sync bool) {
}) })
} }
func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) { func (w *window) getConfig() (image.Point, Insets, unit.Metric) {
invscale := unit.Dp(1. / w.scale) invscale := unit.Dp(1. / w.scale)
return image.Pt(w.config.Size.X, w.config.Size.Y), return image.Pt(w.config.Size.X, w.config.Size.Y),
system.Insets{ Insets{
Bottom: unit.Dp(w.inset.Y) * invscale, Bottom: unit.Dp(w.inset.Y) * invscale,
Right: unit.Dp(w.inset.X) * invscale, Right: unit.Dp(w.inset.X) * invscale,
}, unit.Metric{ }, unit.Metric{
+1 -1
View File
@@ -777,7 +777,7 @@ func (w *window) draw() {
cfg := configFor(w.scale) cfg := configFor(w.scale)
w.setStage(system.StageRunning) w.setStage(system.StageRunning)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: cfg, Metric: cfg,
+1 -1
View File
@@ -1712,7 +1712,7 @@ func (w *window) draw() {
C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf)) C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf))
} }
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: cfg, Metric: cfg,
+1 -1
View File
@@ -621,7 +621,7 @@ func (w *window) draw(sync bool) {
dpi := windows.GetWindowDPI(w.hwnd) dpi := windows.GetWindowDPI(w.hwnd)
cfg := configForDPI(dpi) cfg := configForDPI(dpi)
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: cfg, Metric: cfg,
+1 -1
View File
@@ -458,7 +458,7 @@ loop:
if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 { if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 {
w.w.Event(frameEvent{ w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: w.metric, Metric: w.metric,
+1 -1
View File
@@ -994,7 +994,7 @@ func (w *Window) fallbackDecorate() bool {
} }
// decorate the window if enabled and returns the corresponding Insets. // decorate the window if enabled and returns the corresponding Insets.
func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (size, offset image.Point) { func (w *Window) decorate(d driver, e FrameEvent, o *op.Ops) (size, offset image.Point) {
if !w.fallbackDecorate() { if !w.fallbackDecorate() {
return e.Size, image.Pt(0, 0) return e.Size, image.Pt(0, 0)
} }
+3 -41
View File
@@ -4,35 +4,6 @@
// program level. // program level.
package system package system
import (
"image"
"time"
"gioui.org/io/event"
"gioui.org/op"
"gioui.org/unit"
)
// A FrameEvent requests a new frame in the form of a list of
// operations that describes what to display and how to handle
// input.
type FrameEvent struct {
// Now is the current animation. Use Now instead of time.Now to
// synchronize animation and to avoid the time.Now call overhead.
Now time.Time
// Metric converts device independent dp and sp to device pixels.
Metric unit.Metric
// Size is the dimensions of the window.
Size image.Point
// Insets represent the space occupied by system decorations and controls.
Insets Insets
// Frame completes the FrameEvent by drawing the graphical operations
// from ops into the window.
Frame func(frame *op.Ops)
// Queue supplies the events for event handlers.
Queue event.Queue
}
// DestroyEvent is the last event sent through // DestroyEvent is the last event sent through
// a window event channel. // a window event channel.
type DestroyEvent struct { type DestroyEvent struct {
@@ -41,14 +12,6 @@ type DestroyEvent struct {
Err error Err error
} }
// Insets is the space taken up by
// system decoration such as translucent
// system bars and software keyboards.
type Insets struct {
// Values are in pixels.
Top, Bottom, Left, Right unit.Dp
}
// A StageEvent is generated whenever the stage of a // A StageEvent is generated whenever the stage of a
// Window changes. // Window changes.
type StageEvent struct { type StageEvent struct {
@@ -60,13 +23,13 @@ type Stage uint8
const ( const (
// StagePaused is the stage for windows that have no on-screen representation. // StagePaused is the stage for windows that have no on-screen representation.
// Paused windows don't receive FrameEvent. // Paused windows don't receive frames.
StagePaused Stage = iota StagePaused Stage = iota
// StageInactive is the stage for windows that are visible, but not active. // StageInactive is the stage for windows that are visible, but not active.
// Inactive windows receive FrameEvent. // Inactive windows receive frames.
StageInactive StageInactive
// StageRunning is for active and visible Windows. // StageRunning is for active and visible Windows.
// Running windows receive FrameEvent. // Running windows receive frames.
StageRunning StageRunning
) )
@@ -84,6 +47,5 @@ func (l Stage) String() string {
} }
} }
func (FrameEvent) ImplementsEvent() {}
func (StageEvent) ImplementsEvent() {} func (StageEvent) ImplementsEvent() {}
func (DestroyEvent) ImplementsEvent() {} func (DestroyEvent) ImplementsEvent() {}
-38
View File
@@ -3,7 +3,6 @@
package layout package layout
import ( import (
"image"
"time" "time"
"gioui.org/io/event" "gioui.org/io/event"
@@ -35,43 +34,6 @@ type Context struct {
*op.Ops *op.Ops
} }
// NewContext is a shorthand for
//
// Context{
// Ops: ops,
// Now: e.Now,
// Queue: e.Queue,
// Config: e.Config,
// Constraints: Exact(e.Size),
// }
//
// NewContext calls ops.Reset and adjusts ops for e.Insets.
func NewContext(ops *op.Ops, e system.FrameEvent) Context {
ops.Reset()
size := e.Size
if e.Insets != (system.Insets{}) {
left := e.Metric.Dp(e.Insets.Left)
top := e.Metric.Dp(e.Insets.Top)
op.Offset(image.Point{
X: left,
Y: top,
}).Add(ops)
size.X -= left + e.Metric.Dp(e.Insets.Right)
size.Y -= top + e.Metric.Dp(e.Insets.Bottom)
}
return Context{
Ops: ops,
Now: e.Now,
Queue: e.Queue,
Metric: e.Metric,
Constraints: Exact(size),
}
}
// Dp converts v to pixels. // Dp converts v to pixels.
func (c Context) Dp(v unit.Dp) int { func (c Context) Dp(v unit.Dp) int {
return c.Metric.Dp(v) return c.Metric.Dp(v)
+2 -2
View File
@@ -6,9 +6,9 @@ import (
"image" "image"
"testing" "testing"
"gioui.org/app"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/router" "gioui.org/io/router"
"gioui.org/io/system"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/widget" "gioui.org/widget"
@@ -21,7 +21,7 @@ func TestClickable(t *testing.T) {
b1 widget.Clickable b1 widget.Clickable
b2 widget.Clickable b2 widget.Clickable
) )
gtx := layout.NewContext(&ops, system.FrameEvent{Queue: &r}) gtx := app.NewContext(&ops, app.FrameEvent{Queue: &r})
layout := func() { layout := func() {
b1.Layout(gtx, func(gtx layout.Context) layout.Dimensions { b1.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Dimensions{Size: image.Pt(100, 100)} return layout.Dimensions{Size: image.Pt(100, 100)}
+2 -2
View File
@@ -5,7 +5,7 @@ import (
"testing" "testing"
"time" "time"
"gioui.org/io/system" "gioui.org/app"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/unit" "gioui.org/unit"
@@ -15,7 +15,7 @@ import (
func TestListAnchorStrategies(t *testing.T) { func TestListAnchorStrategies(t *testing.T) {
var ops op.Ops var ops op.Ops
gtx := layout.NewContext(&ops, system.FrameEvent{ gtx := app.NewContext(&ops, app.FrameEvent{
Metric: unit.Metric{ Metric: unit.Metric{
PxPerDp: 1, PxPerDp: 1,
PxPerSp: 1, PxPerSp: 1,
+2 -2
View File
@@ -6,11 +6,11 @@ import (
"image" "image"
"testing" "testing"
"gioui.org/app"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/router" "gioui.org/io/router"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/io/system"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/widget" "gioui.org/widget"
@@ -22,7 +22,7 @@ func TestBool(t *testing.T) {
r router.Router r router.Router
b widget.Bool b widget.Bool
) )
gtx := layout.NewContext(&ops, system.FrameEvent{Queue: &r}) gtx := app.NewContext(&ops, app.FrameEvent{Queue: &r})
layout := func() { layout := func() {
b.Layout(gtx, func(gtx layout.Context) layout.Dimensions { b.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.CheckBox.Add(gtx.Ops) semantic.CheckBox.Add(gtx.Ops)