~eliasnaur/gio-patches

gio: app: decorations WIP v1 PROPOSED

Elias Naur: 1
 app: decorations WIP
~pierrec: 1
 app: decorations WIP

 5 files changed, 116 insertions(+), 26 deletions(-)
#680704 apple.yml failed
#680705 freebsd.yml failed
#680706 linux.yml failed
#680707 openbsd.yml failed
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio-patches/patches/28715/mbox | git am -3
Learn more about email & git

[PATCH gio] app: decorations WIP Export this patch

From: Pierre Curto <pierre.curto@gmail.com>

In app/window.go at L732, the modified size
interferes with the content display.

cmd/main.go is the program I use for testing.
Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
---
 app/os_ios.go                  |  4 +-
 app/window.go                  | 25 ++++-----
 cmd/windeco/main.go            | 94 ++++++++++++++++++++++++++++++++++
 widget/material/decorations.go |  8 ++-
 4 files changed, 109 insertions(+), 22 deletions(-)
 create mode 100644 cmd/windeco/main.go

diff --git a/app/os_ios.go b/app/os_ios.go
index d7f3a6e3..f6fc6147 100644
--- a/app/os_ios.go
+++ b/app/os_ios.go
@@ -98,6 +98,7 @@ type window struct {

	visible bool
	cursor  pointer.CursorName
	config  Config

	pointerMap []C.CFTypeRef
}
@@ -277,9 +278,6 @@ func (w *window) Configure([]Option) {
	prev := w.config
	// Decorations are never disabled.
	w.config.Decorated = true
	if cnf.Decorated != prev.Decorated {
		w.config.Decorated = cnf.Decorated
	}
	if w.config != prev {
		w.w.Event(ConfigEvent{Config: w.config})
	}
diff --git a/app/window.go b/app/window.go
index dc1576ee..e46c6510 100644
--- a/app/window.go
+++ b/app/window.go
@@ -584,13 +584,10 @@ func (w *Window) processEvent(d driver, e event.Event) {
		e2.Frame = w.update
		e2.Queue = &w.queue

		// Prepare the decorations and update the frame insets.
		// Prepare the decorations and update the frame size.
		wrapper := &w.decorations.Ops
		wrapper.Reset()
		decoRec := op.Record(wrapper)
		e2.FrameEvent.Insets = w.decorate(d, e2.FrameEvent, wrapper)
		op.Defer(wrapper, decoRec.Stop())

		e2.FrameEvent.Size = w.decorate(d, e2.FrameEvent, wrapper)
		w.out <- e2.FrameEvent
		frame, gotFrame := w.waitFrame()
		ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))
@@ -683,9 +680,9 @@ func (w *Window) updateCursor(d driver) {
}

// decorate the window if enabled and returns the corresponding Insets.
func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) system.Insets {
func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (appSize image.Point) {
	if w.decorations.Config.Decorated || w.decorations.Config.Mode == Fullscreen {
		return e.Insets
		return e.Size
	}
	deco := w.decorations.Decorations
	if deco == nil {
@@ -726,13 +723,13 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) system.Inset
		Metric:      e.Metric,
		Constraints: layout.Exact(e.Size),
	}
	insets := deco.Decorate(gtx, w.decorations.Config.Title)
	return system.Insets{
		Top:    unit.Add(e.Metric, e.Insets.Top, insets.Top),
		Bottom: unit.Add(e.Metric, e.Insets.Bottom, insets.Bottom),
		Left:   unit.Add(e.Metric, e.Insets.Left, insets.Left),
		Right:  unit.Add(e.Metric, e.Insets.Right, insets.Right),
	}
	decoRec := op.Record(o)
	dims := deco.Layout(gtx, w.decorations.Config.Title)
	decoCall := decoRec.Stop()
	_ = decoCall
	size := image.Point{Y: dims.Size.Y}
	//op.Offset(f32.Point{Y: float32(size.Y)}).Add(o)
	return e.Size.Sub(size)
}

// Raise requests that the platform bring this window to the top of all open windows.
diff --git a/cmd/windeco/main.go b/cmd/windeco/main.go
new file mode 100644
index 00000000..28f72af9
--- /dev/null
+++ b/cmd/windeco/main.go
@@ -0,0 +1,94 @@
package main

import (
	"fmt"
	"image/color"
	"log"
	"os"

	"gioui.org/app"
	"gioui.org/font/gofont"
	"gioui.org/io/system"
	"gioui.org/layout"
	"gioui.org/op"
	"gioui.org/op/paint"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
)

func main() {
	go func() {
		theme := material.NewTheme(gofont.Collection())
		w := app.NewWindow(
			app.Title("MyWindow"),
			app.Size(unit.Dp(800), unit.Dp(600)),
		)
		ui := new(UI)
		*ui = UI{
			th:  theme,
			win: w,
		}
		//time.AfterFunc(4*time.Second, func() {
		//	w.Option(app.Fullscreen.Option())
		//	w.Invalidate()
		//	fmt.Println("fullscreen")
		//})
		//time.AfterFunc(8*time.Second, func() {
		//	w.Option(app.Windowed.Option())
		//	w.Invalidate()
		//	fmt.Println("windowed")
		//})
		ops := new(op.Ops)
		for ev := range w.Events() {
			switch ev := ev.(type) {
			case system.DestroyEvent:
				var code int
				if ev.Err != nil {
					code = 1
					log.Println(ev.Err)
				}
				os.Exit(code)
			case system.FrameEvent:
				gtx := layout.NewContext(ops, ev)
				ui.Layout(gtx)
				ev.Frame(gtx.Ops)
			case app.ConfigEvent:
				fmt.Println(ev.Config)
				ui.config = ev.Config
			}
		}
	}()
	app.Main()
}

type UI struct {
	th     *material.Theme
	win    *app.Window
	config app.Config
	btn    widget.Bool
	close  widget.Clickable
}

func (ui *UI) Layout(gtx layout.Context) layout.Dimensions {
	col := color.NRGBA{R: 200, G: 200, B: 200, A: 255}
	if ui.btn.Value {
		col = color.NRGBA{R: 100, G: 100, B: 100, A: 255}
	}
	paint.Fill(gtx.Ops, col)
	layout.N.Layout(gtx, material.Body1(ui.th, "Hello World").Layout)
	layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		return ui.btn.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
			return material.Body1(ui.th, "Toggle color").Layout(gtx)
		})
	})
	layout.SE.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
		if ui.close.Clicked() {
			ui.win.Close()
		}
		return ui.close.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
			return material.Body1(ui.th, "Close").Layout(gtx)
		})
	})
	return layout.Dimensions{}
}
diff --git a/widget/material/decorations.go b/widget/material/decorations.go
index 92f0861a..d6120392 100644
--- a/widget/material/decorations.go
+++ b/widget/material/decorations.go
@@ -54,10 +54,10 @@ type Decorations struct {
	maximized bool
}

// Decorate a window with the title and actions defined in DecorationsStyle.
// Layout a window with the title and actions defined in DecorationsStyle.
// The space used by the decorations is returned as an inset for the window
// content.
func (d *Decorations) Decorate(gtx layout.Context, title string) layout.Inset {
func (d *Decorations) Layout(gtx layout.Context, title string) layout.Dimensions {
	rec := op.Record(gtx.Ops)
	dims := d.layoutDecorations(gtx, title)
	decos := rec.Stop()
@@ -65,9 +65,7 @@ func (d *Decorations) Decorate(gtx layout.Context, title string) layout.Inset {
	paint.FillShape(gtx.Ops, d.DecorationsStyle.Background, r.Op())
	decos.Add(gtx.Ops)
	d.layoutResizing(gtx)
	return layout.Inset{
		Top: unit.Px(float32(dims.Size.Y)),
	}
	return dims
}

func (d *Decorations) layoutResizing(gtx layout.Context) {
-- 
2.32.0
gio/patches: FAILED in 2m13s

[app: decorations WIP][0] from [~pierrec][1]

[0]: https://lists.sr.ht/~eliasnaur/gio-patches/patches/28715
[1]: mailto:pierre.curto@gmail.com

✗ #680706 FAILED gio/patches/linux.yml   https://builds.sr.ht/~eliasnaur/job/680706
✗ #680707 FAILED gio/patches/openbsd.yml https://builds.sr.ht/~eliasnaur/job/680707
✗ #680704 FAILED gio/patches/apple.yml   https://builds.sr.ht/~eliasnaur/job/680704
✗ #680705 FAILED gio/patches/freebsd.yml https://builds.sr.ht/~eliasnaur/job/680705

Re: [PATCH gio] app: decorations WIP Export this patch

On Tue Jan 25, 2022 at 16:18, ~pierrec wrote:
> From: Pierre Curto <pierre.curto@gmail.com>
>
> In app/window.go at L732, the modified size
> interferes with the content display.
>
> cmd/main.go is the program I use for testing.
>


Here's the change that seems to fix the issue for me:

diff --git app/window.go app/window.go
index e46c6510..12cbff07 100644
--- app/window.go
@@ -587,12 +587,13 @@ func (w *Window) processEvent(d driver, e event.Event) {
		// Prepare the decorations and update the frame size.
		wrapper := &w.decorations.Ops
		wrapper.Reset()
		orig := e2.Size
		e2.FrameEvent.Size = w.decorate(d, e2.FrameEvent, wrapper)
		w.out <- e2.FrameEvent
		frame, gotFrame := w.waitFrame()
		ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))

		err := w.validateAndProcess(d, e2.Size, e2.Sync, wrapper)
		err := w.validateAndProcess(d, orig, e2.Sync, wrapper)
		if gotFrame {
			// We're done with frame, let the client continue.
			w.frameAck <- struct{}{}
@@ -680,7 +681,8 @@ func (w *Window) updateCursor(d driver) {
}

// decorate the window if enabled and returns the corresponding Insets.
func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (appSize image.Point) {
func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (contentSize image.Point) {
	fmt.Println("here?", w.decorations.Config.Decorated)
	if w.decorations.Config.Decorated || w.decorations.Config.Mode == Fullscreen {
		return e.Size
	}
@@ -710,7 +712,7 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (appSize ima
	case Fullscreen:
		actions |= system.ActionFullscreen
	default:
		panic(fmt.Sprintf("unknown WindowMode %v", m))
		panic(fmt.Errorf("unknown WindowMode %v", m))
	}
	deco.Perform(actions)
	// Update the window based on the actions on the decorations.
@@ -726,9 +728,10 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (appSize ima
	decoRec := op.Record(o)
	dims := deco.Layout(gtx, w.decorations.Config.Title)
	decoCall := decoRec.Stop()
	op.Defer(o, decoCall)
	_ = decoCall
	size := image.Point{Y: dims.Size.Y}
	//op.Offset(f32.Point{Y: float32(size.Y)}).Add(o)
	op.Offset(f32.Point{Y: float32(size.Y)}).Add(o)
	return e.Size.Sub(size)
}


I also fixed two nits.

Elias