forked from joejulian/gio
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:
+34
-1
@@ -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)))
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -18,7 +18,7 @@ type scrollChild struct {
|
||||
}
|
||||
|
||||
type List struct {
|
||||
Config *ui.Config
|
||||
Config ui.Config
|
||||
Inputs input.Events
|
||||
Axis Axis
|
||||
Invert bool
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user