mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +00:00
app: use material.Decorations on undecorated platforms
This patch implements a mechanism for customizing window decorations. If a window is configured with app.Decorated(true), then the widget/material.Decorations are applied. On Wayland, the option is automatically set when the server does not provide window decorations. Server side decorations are no longer requested. The Decorated flag is set according to the server's requests. Wayland is now the default driver for UNIX platforms. References: https://todo.sr.ht/~eliasnaur/gio/318 Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
This commit is contained in:
+83
-3
@@ -11,14 +11,18 @@ import (
|
||||
"time"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/font/gofont"
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/ops"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/profile"
|
||||
"gioui.org/io/router"
|
||||
"gioui.org/io/system"
|
||||
"gioui.org/layout"
|
||||
"gioui.org/op"
|
||||
"gioui.org/unit"
|
||||
"gioui.org/widget/material"
|
||||
|
||||
_ "gioui.org/app/internal/log"
|
||||
)
|
||||
@@ -59,8 +63,13 @@ type Window struct {
|
||||
nextFrame time.Time
|
||||
delayedDraw *time.Timer
|
||||
|
||||
queue queue
|
||||
cursor pointer.CursorName
|
||||
queue queue
|
||||
cursor pointer.CursorName
|
||||
decorations struct {
|
||||
op.Ops
|
||||
Config
|
||||
*material.Decorations
|
||||
}
|
||||
|
||||
callbacks callbacks
|
||||
|
||||
@@ -578,9 +587,16 @@ func (w *Window) processEvent(d driver, e event.Event) {
|
||||
w.hasNextFrame = false
|
||||
e2.Frame = w.update
|
||||
e2.Queue = &w.queue
|
||||
|
||||
// Prepare the decorations and update the frame insets.
|
||||
wrapper := &w.decorations.Ops
|
||||
wrapper.Reset()
|
||||
size := e2.Size // save the initial window size as the decorations will change it.
|
||||
e2.FrameEvent.Size = w.decorate(d, e2.FrameEvent, wrapper)
|
||||
w.out <- e2.FrameEvent
|
||||
frame, gotFrame := w.waitFrame()
|
||||
err := w.validateAndProcess(d, e2.Size, e2.Sync, frame)
|
||||
ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))
|
||||
err := w.validateAndProcess(d, size, e2.Sync, wrapper)
|
||||
if gotFrame {
|
||||
// We're done with frame, let the client continue.
|
||||
w.frameAck <- struct{}{}
|
||||
@@ -606,6 +622,9 @@ func (w *Window) processEvent(d driver, e event.Event) {
|
||||
w.out <- e2
|
||||
w.waitAck()
|
||||
case wakeupEvent:
|
||||
case ConfigEvent:
|
||||
w.decorations.Config = e2.Config
|
||||
w.out <- e
|
||||
case event.Event:
|
||||
if w.queue.q.Queue(e2) {
|
||||
w.setNextFrame(time.Time{})
|
||||
@@ -664,6 +683,59 @@ 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) image.Point {
|
||||
if w.decorations.Config.Decorated || w.decorations.Config.Mode == Fullscreen {
|
||||
return e.Size
|
||||
}
|
||||
deco := w.decorations.Decorations
|
||||
if deco == nil {
|
||||
theme := material.NewTheme(gofont.Collection())
|
||||
allActions := system.ActionMinimize | system.ActionMaximize | system.ActionUnmaximize |
|
||||
system.ActionClose | system.ActionMove |
|
||||
system.ActionResizeNorth | system.ActionResizeSouth |
|
||||
system.ActionResizeWest | system.ActionResizeEast |
|
||||
system.ActionResizeNorthWest | system.ActionResizeSouthWest |
|
||||
system.ActionResizeNorthEast | system.ActionResizeSouthEast
|
||||
deco = &material.Decorations{
|
||||
DecorationsStyle: material.Decorate(theme, allActions),
|
||||
}
|
||||
w.decorations.Decorations = deco
|
||||
}
|
||||
// Update the decorations based on the current window mode.
|
||||
var actions system.Action
|
||||
switch m := w.decorations.Config.Mode; m {
|
||||
case Windowed:
|
||||
actions |= system.ActionUnmaximize
|
||||
case Minimized:
|
||||
actions |= system.ActionMinimize
|
||||
case Maximized:
|
||||
actions |= system.ActionMaximize
|
||||
case Fullscreen:
|
||||
actions |= system.ActionFullscreen
|
||||
default:
|
||||
panic(fmt.Errorf("unknown WindowMode %v", m))
|
||||
}
|
||||
deco.Perform(actions)
|
||||
// Update the window based on the actions on the decorations.
|
||||
d.Perform(deco.Actions())
|
||||
|
||||
gtx := layout.Context{
|
||||
Ops: o,
|
||||
Now: e.Now,
|
||||
Queue: e.Queue,
|
||||
Metric: e.Metric,
|
||||
Constraints: layout.Exact(e.Size),
|
||||
}
|
||||
rec := op.Record(o)
|
||||
dims := deco.Decorate(gtx, w.decorations.Config.Title)
|
||||
op.Defer(o, rec.Stop())
|
||||
// Offset to place the frame content below the decorations.
|
||||
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.
|
||||
// Some platforms do not allow this except under certain circumstances, such as when
|
||||
// a window from the same application already has focus. If the platform does not
|
||||
@@ -764,3 +836,11 @@ func CustomRenderer(custom bool) Option {
|
||||
cnf.CustomRenderer = custom
|
||||
}
|
||||
}
|
||||
|
||||
// Decorated controls whether automatic window decorations
|
||||
// are enabled.
|
||||
func Decorated(enabled bool) Option {
|
||||
return func(_ unit.Metric, cnf *Config) {
|
||||
cnf.Decorated = enabled
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user