diff --git a/app/internal/windows/windows.go b/app/internal/windows/windows.go index d79a650d6..d357f6307 100644 --- a/app/internal/windows/windows.go +++ b/app/internal/windows/windows.go @@ -349,6 +349,8 @@ var ( _ReleaseDC = user32.NewProc("ReleaseDC") _ScreenToClient = user32.NewProc("ScreenToClient") _ShowWindow = user32.NewProc("ShowWindow") + _CloseWindow = user32.NewProc("CloseWindow") + _SetParent = user32.NewProc("SetParent") _SetCapture = user32.NewProc("SetCapture") _SetCursor = user32.NewProc("SetCursor") _SetClipboardData = user32.NewProc("SetClipboardData") @@ -742,6 +744,14 @@ func SetForegroundWindow(hwnd syscall.Handle) { _SetForegroundWindow.Call(uintptr(hwnd)) } +func SetParent(hwndc, hwndp syscall.Handle) { + _SetParent.Call(uintptr(hwndc), uintptr(hwndp)) +} + +func CloseWindow(hwnd syscall.Handle) { + _CloseWindow.Call(uintptr(hwnd)) +} + func SetFocus(hwnd syscall.Handle) { _SetFocus.Call(uintptr(hwnd)) } diff --git a/app/linked/linked.go b/app/linked/linked.go new file mode 100644 index 000000000..fc3797c94 --- /dev/null +++ b/app/linked/linked.go @@ -0,0 +1,12 @@ +package linked + +import "image" + +type LinkedView interface { + // Using app.ViewEvent to Parent() a native window + Parent(interface{}) + Move(image.Rectangle) + Show() + Hide() + Destroy() +} diff --git a/app/linked/linked_windows.go b/app/linked/linked_windows.go new file mode 100644 index 000000000..3e67cee92 --- /dev/null +++ b/app/linked/linked_windows.go @@ -0,0 +1,37 @@ +package linked + +import ( + "image" + + "gioui.org/app/internal/windows" + + syscall "golang.org/x/sys/windows" +) + +type Link struct { + Handle syscall.Handle +} + +func New(v uintptr) Link { + return Link{Handle: syscall.Handle(v)} +} + +func (W Link) Parent(P interface{}) { + windows.SetParent(W.Handle, syscall.Handle(P.(uintptr))) +} + +func (W Link) Move(r image.Rectangle) { + windows.MoveWindow(W.Handle, int32(r.Min.X), int32(r.Min.Y), int32(r.Dx()), int32(r.Dy()), false) +} + +func (W Link) Show() { + windows.ShowWindow(W.Handle, 1) +} + +func (W Link) Hide() { + windows.ShowWindow(W.Handle, 0) +} + +func (W Link) Destroy() { + windows.CloseWindow(W.Handle) +} diff --git a/gpu/gpu.go b/gpu/gpu.go index cb566edb9..b5a90da80 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -18,6 +18,7 @@ import ( "time" "unsafe" + "gioui.org/app/linked" "gioui.org/gpu/internal/driver" "gioui.org/internal/byteslice" "gioui.org/internal/f32" @@ -101,6 +102,8 @@ type drawState struct { stop2 f32.Point color1 color.NRGBA color2 color.NRGBA + + handle *linked.LinkedView } type pathOp struct { @@ -159,6 +162,8 @@ type material struct { // For materialTypeTexture. data imageOpData uvTrans f32.Affine2D + + handle *linked.LinkedView } // imageOpData is the shadow of paint.ImageOp. @@ -222,6 +227,13 @@ func decodeLinearGradientOp(data []byte) linearGradientOpData { } } +func decodeLinkedViewOp(data []byte, refs []interface{}) *linked.LinkedView { + if len(refs) > 0 { + return refs[0].(*linked.LinkedView) + } + return new(linked.LinkedView) +} + type clipType uint8 type resource interface { @@ -296,6 +308,7 @@ const ( materialColor materialType = iota materialLinearGradient materialTexture + materialLinkedView ) // New creates a GPU for the given API. @@ -924,6 +937,9 @@ loop: case ops.TypeImage: state.matType = materialTexture state.image = decodeImageOp(encOp.Data, encOp.Refs) + case ops.TypeLinkedView: + state.matType = materialLinkedView + state.handle = decodeLinkedViewOp(encOp.Data, encOp.Refs) case ops.TypePaint: // Transform (if needed) the painting rectangle and if so generate a clip path, // for those cases also compute a partialTrans that maps texture coordinates between @@ -1035,6 +1051,9 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32 uvScale, uvOffset := texSpaceTransform(sr, sz) m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset)) m.data = d.image + case materialLinkedView: + m.material = materialLinkedView + m.handle = d.handle } return m } @@ -1076,6 +1095,10 @@ func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) { switch m.material { case materialTexture: r.ctx.BindTexture(0, r.texHandle(cache, m.data)) + + case materialLinkedView: + (*m.handle).Move(img.clip) + continue } drc := img.clip diff --git a/internal/ops/ops.go b/internal/ops/ops.go index fff43ad35..f9c52df31 100644 --- a/internal/ops/ops.go +++ b/internal/ops/ops.go @@ -76,6 +76,7 @@ const ( TypeSnippet TypeSelection TypeActionInput + TypeLinkedView ) type StackID struct { @@ -160,6 +161,7 @@ const ( TypeSnippetLen = 1 + 4 + 4 TypeSelectionLen = 1 + 2*4 + 2*4 + 4 + 4 TypeActionInputLen = 1 + 1 + TypeLinkedViewLen = 1 ) func (op *ClipOp) Decode(data []byte) { @@ -417,6 +419,7 @@ var opProps = [0x100]opProp{ TypeSnippet: {Size: TypeSnippetLen, NumRefs: 2}, TypeSelection: {Size: TypeSelectionLen, NumRefs: 1}, TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0}, + TypeLinkedView: {Size: TypeLinkedViewLen, NumRefs: 1}, } func (t OpType) props() (size, numRefs int) { diff --git a/op/paint/paint.go b/op/paint/paint.go index 1c992973a..e0cf06b2e 100644 --- a/op/paint/paint.go +++ b/op/paint/paint.go @@ -9,6 +9,7 @@ import ( "image/draw" "math" + "gioui.org/app/linked" "gioui.org/f32" "gioui.org/internal/ops" "gioui.org/op" @@ -145,3 +146,21 @@ func Fill(ops *op.Ops, c color.NRGBA) { ColorOp{Color: c}.Add(ops) PaintOp{}.Add(ops) } + +type LinkedViewOp struct { + address *linked.LinkedView + area image.Point +} + +func NewLinkedViewOp(src linked.LinkedView, area image.Point) LinkedViewOp { + return LinkedViewOp{address: &src, area: area} +} + +func (i LinkedViewOp) Add(o *op.Ops) { + data := ops.Write1(&o.Internal, ops.TypeLinkedViewLen, i.address) + data[0] = byte(ops.TypeLinkedView) +} + +func (i LinkedViewOp) Size() image.Point { + return i.area +} diff --git a/widget/image.go b/widget/image.go index c30941d9b..ef6470ac4 100644 --- a/widget/image.go +++ b/widget/image.go @@ -53,3 +53,43 @@ func (im Image) Layout(gtx layout.Context) layout.Dimensions { return dims } + +// Native is a widget that displays an native window. +// Basically mimicking behaviour of Image widget. +type Native struct { + // Src is the link to display with. + Src paint.LinkedViewOp + // Fit specifies how to scale the view to the constraints. + // By default it does not do any scaling. + Fit Fit + // Position specifies where to position the view within + // the constraints. + Position layout.Direction + // Scale is the ratio of image pixels to + // dps. If Scale is zero Image falls back to + // a scale that match a standard 72 DPI. + Scale float32 +} + +func (im Native) Layout(gtx layout.Context) layout.Dimensions { + scale := im.Scale + if scale == 0 { + scale = defaultScale + } + + size := im.Src.Size() + wf, hf := float32(size.X), float32(size.Y) + w, h := gtx.Dp(unit.Dp(wf*scale)), gtx.Dp(unit.Dp(hf*scale)) + + dims, trans := im.Fit.scale(gtx.Constraints, im.Position, layout.Dimensions{Size: image.Pt(w, h)}) + defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop() + + pixelScale := scale * gtx.Metric.PxPerDp + trans = trans.Mul(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(pixelScale, pixelScale))) + defer op.Affine(trans).Push(gtx.Ops).Pop() + + im.Src.Add(gtx.Ops) + paint.PaintOp{}.Add(gtx.Ops) + + return dims +}