Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Implement basic platform view. #90

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/internal/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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))
}
Expand Down
12 changes: 12 additions & 0 deletions app/linked/linked.go
Original file line number Diff line number Diff line change
@@ -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()
}
37 changes: 37 additions & 0 deletions app/linked/linked_windows.go
Original file line number Diff line number Diff line change
@@ -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)
}
23 changes: 23 additions & 0 deletions gpu/gpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"time"
"unsafe"

"gioui.org/app/linked"
"gioui.org/gpu/internal/driver"
"gioui.org/internal/byteslice"
"gioui.org/internal/f32"
Expand Down Expand Up @@ -101,6 +102,8 @@ type drawState struct {
stop2 f32.Point
color1 color.NRGBA
color2 color.NRGBA

handle *linked.LinkedView
}

type pathOp struct {
Expand Down Expand Up @@ -159,6 +162,8 @@ type material struct {
// For materialTypeTexture.
data imageOpData
uvTrans f32.Affine2D

handle *linked.LinkedView
}

// imageOpData is the shadow of paint.ImageOp.
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -296,6 +308,7 @@ const (
materialColor materialType = iota
materialLinearGradient
materialTexture
materialLinkedView
)

// New creates a GPU for the given API.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions internal/ops/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ const (
TypeSnippet
TypeSelection
TypeActionInput
TypeLinkedView
)

type StackID struct {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
19 changes: 19 additions & 0 deletions op/paint/paint.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"image/draw"
"math"

"gioui.org/app/linked"
"gioui.org/f32"
"gioui.org/internal/ops"
"gioui.org/op"
Expand Down Expand Up @@ -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
}
40 changes: 40 additions & 0 deletions widget/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}