ui,ui/app: convert Config to an interface

To keep the interface slim, remove the helper methods and shorten
the essential method, Pixels, to Px.

Add and use unexported Config implementation in the app package.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2019-07-10 16:40:53 +02:00
parent 624ef78e1c
commit 32aae18293
14 changed files with 104 additions and 104 deletions
+34 -1
View File
@@ -4,8 +4,10 @@ package app
import (
"image"
"math"
"os"
"strings"
"time"
"gioui.org/ui"
)
@@ -15,7 +17,7 @@ type Event interface {
}
type DrawEvent struct {
Config ui.Config
Config Config
Size image.Point
// Insets is the window space taken up by
// system decoration such as translucent
@@ -131,3 +133,34 @@ func init() {
func DataDir() (string, error) {
return dataDir()
}
// Config implements the ui.Config interface.
type Config struct {
// Device pixels per dp.
pxPerDp float32
// Device pixels per sp.
pxPerSp float32
now time.Time
}
func (c *Config) Now() time.Time {
return c.now
}
func (c *Config) Px(v ui.Value) int {
var r float32
switch v.U {
case ui.UnitPx:
r = v.V
case ui.UnitDp:
r = c.pxPerDp * v.V
case ui.UnitSp:
r = c.pxPerSp * v.V
default:
panic("unknown unit")
}
if math.IsInf(float64(r), +1) {
return ui.Inf
}
return int(math.Round(float64(r)))
}
+4 -5
View File
@@ -24,7 +24,6 @@ import (
"time"
"unsafe"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -289,10 +288,10 @@ func (w *window) draw(sync bool) {
Y: int(height),
},
Insets: w.insets,
Config: ui.Config{
PxPerDp: ppdp,
PxPerSp: w.fontScale * ppdp,
Now: time.Now(),
Config: Config{
pxPerDp: ppdp,
pxPerSp: w.fontScale * ppdp,
now: time.Now(),
},
sync: sync,
})
+4 -5
View File
@@ -22,7 +22,6 @@ import (
"sync/atomic"
"time"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -87,10 +86,10 @@ func onDraw(view C.CFTypeRef, dpi, sdpi, width, height C.CGFloat, sync C.int, to
Min: image.Point{X: int(left + .5), Y: int(top + .5)},
Max: image.Point{X: int(right + .5), Y: int(bottom + .5)},
},
Config: ui.Config{
PxPerDp: float32(dpi) * inchPrDp,
PxPerSp: float32(sdpi) * inchPrDp,
Now: time.Now(),
Config: Config{
pxPerDp: float32(dpi) * inchPrDp,
pxPerSp: float32(sdpi) * inchPrDp,
now: time.Now(),
},
sync: isSync,
})
+6 -7
View File
@@ -7,7 +7,6 @@ import (
"syscall/js"
"time"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -329,13 +328,13 @@ func (w *window) setTextInput(s key.TextInputState) {
func (w *window) draw(sync bool) {
width, height, scale, cfg := w.config()
if cfg == (ui.Config{}) {
if cfg == (Config{}) {
return
}
w.mu.Lock()
w.scale = float32(scale)
w.mu.Unlock()
cfg.Now = time.Now()
cfg.now = time.Now()
w.w.event(DrawEvent{
Size: image.Point{
X: width,
@@ -346,7 +345,7 @@ func (w *window) draw(sync bool) {
})
}
func (w *window) config() (int, int, float32, ui.Config) {
func (w *window) config() (int, int, float32, Config) {
rect := w.cnv.Call("getBoundingClientRect")
width, height := rect.Get("width").Float(), rect.Get("height").Float()
scale := w.window.Get("devicePixelRatio").Float()
@@ -359,9 +358,9 @@ func (w *window) config() (int, int, float32, ui.Config) {
w.cnv.Set("height", ih)
}
const ppdp = 96 * inchPrDp * monitorScale
return iw, ih, float32(scale), ui.Config{
PxPerDp: ppdp * float32(scale),
PxPerSp: ppdp * float32(scale),
return iw, ih, float32(scale), Config{
pxPerDp: ppdp * float32(scale),
pxPerSp: ppdp * float32(scale),
}
}
+7 -8
View File
@@ -19,7 +19,6 @@ import (
"time"
"unsafe"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -142,7 +141,7 @@ func (w *window) draw(sync bool) {
return
}
cfg := getConfig()
cfg.Now = time.Now()
cfg.now = time.Now()
w.setStage(StageRunning)
w.w.event(DrawEvent{
Size: image.Point{
@@ -154,15 +153,15 @@ func (w *window) draw(sync bool) {
})
}
func getConfig() ui.Config {
func getConfig() Config {
ppdp := float32(C.gio_getPixelsPerDP())
ppdp *= monitorScale
if ppdp < minDensity {
ppdp = minDensity
}
return ui.Config{
PxPerDp: ppdp,
PxPerSp: ppdp,
return Config{
pxPerDp: ppdp,
pxPerSp: ppdp,
}
}
@@ -216,8 +215,8 @@ func Main() {
}
cfg := getConfig()
opts := singleWindow.opts
w := cfg.Pixels(opts.Width)
h := cfg.Pixels(opts.Height)
w := cfg.Px(opts.Width)
h := cfg.Px(opts.Height)
title := C.CString(opts.Title)
defer C.free(unsafe.Pointer(title))
C.gio_main(view, title, C.CGFloat(w), C.CGFloat(h))
+8 -9
View File
@@ -19,7 +19,6 @@ import (
"unicode/utf8"
"unsafe"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -237,8 +236,8 @@ func createNativeWindow(opts *WindowOptions) (*window, error) {
C.free(unsafe.Pointer(title))
_, _, cfg := w.config()
w.width = cfg.Pixels(opts.Width)
w.height = cfg.Pixels(opts.Height)
w.width = cfg.Px(opts.Width)
w.height = cfg.Px(opts.Height)
if conn.decor != nil {
// Request server side decorations.
w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(conn.decor, w.topLvl)
@@ -1020,11 +1019,11 @@ func (w *window) updateOutputs() {
}
}
func (w *window) config() (int, int, ui.Config) {
func (w *window) config() (int, int, Config) {
width, height := w.width*w.scale, w.height*w.scale
return width, height, ui.Config{
PxPerDp: w.ppdp * float32(w.scale),
PxPerSp: w.ppsp * float32(w.scale),
return width, height, Config{
pxPerDp: w.ppdp * float32(w.scale),
pxPerSp: w.ppsp * float32(w.scale),
}
}
@@ -1036,7 +1035,7 @@ func (w *window) draw(sync bool) {
return
}
width, height, cfg := w.config()
if cfg == (ui.Config{}) {
if cfg == (Config{}) {
return
}
if animating && w.lastFrameCallback == nil {
@@ -1044,7 +1043,7 @@ func (w *window) draw(sync bool) {
// Use the surface as listener data for gio_onFrameDone.
C.gio_wl_callback_add_listener(w.lastFrameCallback, unsafe.Pointer(w.surf))
}
cfg.Now = time.Now()
cfg.now = time.Now()
w.w.event(DrawEvent{
Size: image.Point{
X: width,
+7 -8
View File
@@ -13,7 +13,6 @@ import (
syscall "golang.org/x/sys/windows"
"gioui.org/ui"
"gioui.org/ui/f32"
"gioui.org/ui/key"
"gioui.org/ui/pointer"
@@ -220,8 +219,8 @@ func createNativeWindow(opts *WindowOptions) (*window, error) {
}
defer unregisterClass(cls, hInst)
wr := rect{
right: int32(cfg.Pixels(opts.Width)),
bottom: int32(cfg.Pixels(opts.Height)),
right: int32(cfg.Px(opts.Width)),
bottom: int32(cfg.Px(opts.Height)),
}
dwStyle := uint32(_WS_OVERLAPPEDWINDOW)
dwExStyle := uint32(_WS_EX_APPWINDOW | _WS_EX_WINDOWEDGE)
@@ -419,7 +418,7 @@ func (w *window) draw(sync bool) {
w.width = int(r.right - r.left)
w.height = int(r.bottom - r.top)
cfg := configForDC(w.hdc)
cfg.Now = time.Now()
cfg.now = time.Now()
w.w.event(DrawEvent{
Size: image.Point{
X: w.width,
@@ -487,16 +486,16 @@ func convertKeyCode(code uintptr) (rune, bool) {
return r, true
}
func configForDC(hdc syscall.Handle) ui.Config {
func configForDC(hdc syscall.Handle) Config {
dpi := getDeviceCaps(hdc, _LOGPIXELSX)
ppdp := float32(dpi) * inchPrDp * monitorScale
// Force a minimum density to keep text legible and to handle bogus output geometry.
if ppdp < minDensity {
ppdp = minDensity
}
return ui.Config{
PxPerDp: ppdp,
PxPerSp: ppdp,
return Config{
pxPerDp: ppdp,
pxPerSp: ppdp,
}
}
+7 -7
View File
@@ -133,7 +133,7 @@ func (s *Scroll) Dragging() bool {
return s.dragging
}
func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
func (s *Scroll) Scroll(cfg ui.Config, q input.Events, axis Axis) int {
if s.axis != axis {
s.axis = axis
return 0
@@ -165,15 +165,15 @@ func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
break
}
fling := s.estimator.Estimate()
if slop, d := float32(cfg.Pixels(touchSlop)), fling.Distance; d >= slop || -slop >= d {
if min, v := float32(cfg.Pixels(minFlingVelocity)), fling.Velocity; v >= min || -min >= v {
max := float32(cfg.Pixels(maxFlingVelocity))
if slop, d := float32(cfg.Px(touchSlop)), fling.Distance; d >= slop || -slop >= d {
if min, v := float32(cfg.Px(minFlingVelocity)), fling.Velocity; v >= min || -min >= v {
max := float32(cfg.Px(maxFlingVelocity))
if v > max {
v = max
} else if v < -max {
v = -max
}
s.flinger.Init(cfg.Now, v)
s.flinger.Init(cfg.Now(), v)
}
}
fallthrough
@@ -200,7 +200,7 @@ func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
v := int(math.Round(float64(val)))
dist := s.last - v
if e.Priority < pointer.Grabbed {
slop := cfg.Pixels(touchSlop)
slop := cfg.Px(touchSlop)
if dist := dist; dist >= slop || -slop >= dist {
s.grab = true
}
@@ -210,7 +210,7 @@ func (s *Scroll) Scroll(cfg *ui.Config, q input.Events, axis Axis) int {
}
}
}
total += s.flinger.Tick(cfg.Now)
total += s.flinger.Tick(cfg.Now())
return total
}
+5 -5
View File
@@ -96,14 +96,14 @@ type Insets struct {
cs Constraints
}
func (in *Insets) Begin(c *ui.Config, ops *ui.Ops, cs Constraints) Constraints {
func (in *Insets) Begin(c ui.Config, ops *ui.Ops, cs Constraints) Constraints {
if in.begun {
panic("must End before Begin")
}
in.top = c.Pixels(in.Top)
in.right = c.Pixels(in.Right)
in.bottom = c.Pixels(in.Bottom)
in.left = c.Pixels(in.Left)
in.top = c.Px(in.Top)
in.right = c.Px(in.Right)
in.bottom = c.Px(in.Bottom)
in.left = c.Px(in.Left)
in.begun = true
in.ops = ops
in.cs = cs
+1 -1
View File
@@ -18,7 +18,7 @@ type scrollChild struct {
}
type List struct {
Config *ui.Config
Config ui.Config
Inputs input.Events
Axis Axis
Invert bool
+3 -3
View File
@@ -17,7 +17,7 @@ import (
)
type Faces struct {
Config *ui.Config
Config ui.Config
faceCache map[faceKey]*textFace
layoutCache map[layoutKey]cachedLayout
pathCache map[pathKey]cachedPath
@@ -102,7 +102,7 @@ func (f *Faces) init() {
}
func (f *textFace) Layout(str string, opts text.LayoutOptions) *text.Layout {
ppem := fixed.Int26_6(f.faces.Config.Pixels(f.size) * 64)
ppem := fixed.Int26_6(f.faces.Config.Px(f.size) * 64)
lk := layoutKey{
f: f.font.Font,
ppem: ppem,
@@ -120,7 +120,7 @@ func (f *textFace) Layout(str string, opts text.LayoutOptions) *text.Layout {
}
func (f *textFace) Path(str text.String) ui.BlockOp {
ppem := fixed.Int26_6(f.faces.Config.Pixels(f.size) * 64)
ppem := fixed.Int26_6(f.faces.Config.Px(f.size) * 64)
pk := pathKey{
f: f.font.Font,
ppem: ppem,
+10 -9
View File
@@ -21,7 +21,7 @@ import (
)
type Editor struct {
Config *ui.Config
Config ui.Config
Inputs input.Events
Face Face
Alignment Alignment
@@ -32,7 +32,7 @@ type Editor struct {
Hint string
HintMaterial ui.BlockOp
oldCfg ui.Config
oldScale int
blinkStart time.Time
focused bool
rr editBuffer
@@ -73,9 +73,10 @@ func (s ChangeEvent) isEditorEvent() {}
func (s SubmitEvent) isEditorEvent() {}
func (e *Editor) Next() (EditorEvent, bool) {
if cfg := *e.Config; cfg != e.oldCfg {
// Crude configuration change detection.
if scale := e.Config.Px(ui.Sp(100)); scale != e.oldScale {
e.invalidate()
e.oldCfg = cfg
e.oldScale = scale
}
sbounds := e.scrollBounds()
var smin, smax int
@@ -104,7 +105,7 @@ func (e *Editor) Next() (EditorEvent, bool) {
switch {
case evt.Type == gesture.TypePress && evt.Source == pointer.Mouse,
evt.Type == gesture.TypeClick && evt.Source == pointer.Touch:
e.blinkStart = e.Config.Now
e.blinkStart = e.Config.Now()
e.moveCoord(image.Point{
X: int(math.Round(float64(evt.Position.X))),
Y: int(math.Round(float64(evt.Position.Y))),
@@ -123,7 +124,7 @@ func (e *Editor) Next() (EditorEvent, bool) {
if !ok {
break
}
e.blinkStart = e.Config.Now
e.blinkStart = e.Config.Now()
switch ke := ke.(type) {
case key.FocusEvent:
e.focused = ke.Focus
@@ -153,7 +154,7 @@ func (e *Editor) Next() (EditorEvent, bool) {
}
func (e *Editor) caretWidth() fixed.Int26_6 {
oneDp := e.Config.Dp(1)
oneDp := e.Config.Px(ui.Dp(1))
return fixed.Int26_6(oneDp * 64)
}
@@ -167,7 +168,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
break
}
}
twoDp := e.Config.Dp(2)
twoDp := e.Config.Px(ui.Dp(2))
e.padLeft, e.padRight = twoDp, twoDp
maxWidth := cs.Width.Max
if e.SingleLine {
@@ -225,7 +226,7 @@ func (e *Editor) Layout(ops *ui.Ops, cs layout.Constraints) layout.Dimens {
ui.PopOp{}.Add(ops)
}
if e.focused {
now := e.Config.Now
now := e.Config.Now()
dt := now.Sub(e.blinkStart)
blinking := dt < maxBlinkDuration
const timePerBlink = time.Second / blinksPerSecond
+6 -34
View File
@@ -11,41 +11,13 @@ import (
"gioui.org/ui/internal/ops"
)
// Config contains the essential configuration for
// Config represents the essential configuration for
// updating and drawing a user interface.
type Config struct {
// Device pixels per dp.
PxPerDp float32
// Device pixels per sp.
PxPerSp float32
// The current time for animation.
Now time.Time
}
// Dp converts a value in dp units to pixels.
func (c *Config) Dp(dp float32) int {
return c.Pixels(Dp(dp))
}
// Sp converts a value in sp units to pixels.
func (c *Config) Sp(sp float32) int {
return c.Pixels(Sp(sp))
}
// Pixels converts a value to pixels.
func (c *Config) Pixels(v Value) int {
var r float32
switch v.U {
case UnitPx:
r = v.V
case UnitDp:
r = c.PxPerDp * v.V
case UnitSp:
r = c.PxPerSp * v.V
default:
panic("unknown unit")
}
return int(math.Round(float64(r)))
type Config interface {
// Now returns the current animation time.
Now() time.Time
// Px converts a Value to pixels.
Px(v Value) int
}
// LayerOp represents a semantic layer of UI.
+2 -2
View File
@@ -23,13 +23,13 @@ type Image struct {
Scale float32
}
func (im Image) Layout(c *ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimens {
func (im Image) Layout(c ui.Config, ops *ui.Ops, cs layout.Constraints) layout.Dimens {
size := im.Src.Bounds()
wf, hf := float32(size.Dx()), float32(size.Dy())
var w, h int
if im.Scale == 0 {
const dpPrPx = 160 / 72
w, h = c.Dp(wf*dpPrPx), c.Dp(hf*dpPrPx)
w, h = c.Px(ui.Dp(wf*dpPrPx)), c.Px(ui.Dp(hf*dpPrPx))
} else {
w, h = int(wf*im.Scale+.5), int(hf*im.Scale+.5)
}