mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 18:35:34 +00:00
app: [Wayland] account for fallback decoration height in window sizes
Pass through a fallback window decoration height to the Wayland backend, so that it can account for it when determining surface size. Fixes: https://todo.sr.ht/~eliasnaur/gio/435 Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -43,6 +43,9 @@ type Config struct {
|
|||||||
CustomRenderer bool
|
CustomRenderer bool
|
||||||
// Decorated reports whether window decorations are provided automatically.
|
// Decorated reports whether window decorations are provided automatically.
|
||||||
Decorated bool
|
Decorated bool
|
||||||
|
// decoHeight is the height of the fallback decoration for platforms such
|
||||||
|
// as Wayland that may need fallback client-side decorations.
|
||||||
|
decoHeight unit.Dp
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigEvent is sent whenever the configuration of a Window changes.
|
// ConfigEvent is sent whenever the configuration of a Window changes.
|
||||||
|
|||||||
+31
-11
@@ -576,6 +576,12 @@ func gio_onToplevelDecorationConfigure(data unsafe.Pointer, deco *C.struct_zxdg_
|
|||||||
w.config.Decorated = true
|
w.config.Decorated = true
|
||||||
}
|
}
|
||||||
if decorated != w.config.Decorated {
|
if decorated != w.config.Decorated {
|
||||||
|
w.setWindowConstraints()
|
||||||
|
if w.config.Decorated {
|
||||||
|
w.size.Y -= int(w.config.decoHeight)
|
||||||
|
} else {
|
||||||
|
w.size.Y += int(w.config.decoHeight)
|
||||||
|
}
|
||||||
w.w.Event(ConfigEvent{Config: w.config})
|
w.w.Event(ConfigEvent{Config: w.config})
|
||||||
w.redraw = true
|
w.redraw = true
|
||||||
}
|
}
|
||||||
@@ -1022,6 +1028,7 @@ func (w *window) Configure(options []Option) {
|
|||||||
prev := w.config
|
prev := w.config
|
||||||
cnf := w.config
|
cnf := w.config
|
||||||
cnf.apply(cfg, options)
|
cnf.apply(cfg, options)
|
||||||
|
w.config.decoHeight = cnf.decoHeight
|
||||||
|
|
||||||
switch cnf.Mode {
|
switch cnf.Mode {
|
||||||
case Fullscreen:
|
case Fullscreen:
|
||||||
@@ -1064,22 +1071,35 @@ func (w *window) Configure(options []Option) {
|
|||||||
w.setTitle(prev, cnf)
|
w.setTitle(prev, cnf)
|
||||||
if prev.Size != cnf.Size {
|
if prev.Size != cnf.Size {
|
||||||
w.config.Size = cnf.Size
|
w.config.Size = cnf.Size
|
||||||
w.size = cnf.Size.Div(w.scale)
|
w.config.Size.Y += int(w.decoHeight()) * w.scale
|
||||||
}
|
w.size = w.config.Size.Div(w.scale)
|
||||||
if prev.MinSize != cnf.MinSize {
|
|
||||||
w.config.MinSize = cnf.MinSize
|
|
||||||
scaled := cnf.MinSize.Div(w.scale)
|
|
||||||
C.xdg_toplevel_set_min_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y))
|
|
||||||
}
|
|
||||||
if prev.MaxSize != cnf.MaxSize {
|
|
||||||
w.config.MaxSize = cnf.MaxSize
|
|
||||||
scaled := cnf.MaxSize.Div(w.scale)
|
|
||||||
C.xdg_toplevel_set_max_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y))
|
|
||||||
}
|
}
|
||||||
|
w.config.MinSize = cnf.MinSize
|
||||||
|
w.config.MaxSize = cnf.MaxSize
|
||||||
|
w.setWindowConstraints()
|
||||||
}
|
}
|
||||||
w.w.Event(ConfigEvent{Config: w.config})
|
w.w.Event(ConfigEvent{Config: w.config})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *window) setWindowConstraints() {
|
||||||
|
decoHeight := w.decoHeight()
|
||||||
|
if scaled := w.config.MinSize.Div(w.scale); scaled != (image.Point{}) {
|
||||||
|
C.xdg_toplevel_set_min_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y+decoHeight))
|
||||||
|
}
|
||||||
|
if scaled := w.config.MaxSize.Div(w.scale); scaled != (image.Point{}) {
|
||||||
|
C.xdg_toplevel_set_max_size(w.topLvl, C.int32_t(scaled.X), C.int32_t(scaled.Y+decoHeight))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoHeight returns the adjustment for client-side decorations, if applicable.
|
||||||
|
// The unit is in surface-local coordinates.
|
||||||
|
func (w *window) decoHeight() int {
|
||||||
|
if !w.config.Decorated {
|
||||||
|
return int(w.config.decoHeight)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (w *window) setTitle(prev, cnf Config) {
|
func (w *window) setTitle(prev, cnf Config) {
|
||||||
if prev.Title != cnf.Title {
|
if prev.Title != cnf.Title {
|
||||||
w.config.Title = cnf.Title
|
w.config.Title = cnf.Title
|
||||||
|
|||||||
+38
-21
@@ -85,9 +85,10 @@ type Window struct {
|
|||||||
// capability.
|
// capability.
|
||||||
enabled bool
|
enabled bool
|
||||||
Config
|
Config
|
||||||
|
height unit.Dp
|
||||||
|
currentHeight int
|
||||||
*material.Theme
|
*material.Theme
|
||||||
*widget.Decorations
|
*widget.Decorations
|
||||||
size image.Point // decorations size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callbacks callbacks
|
callbacks callbacks
|
||||||
@@ -137,10 +138,24 @@ type queue struct {
|
|||||||
// Calling NewWindow more than once is not supported on
|
// Calling NewWindow more than once is not supported on
|
||||||
// iOS, Android, WebAssembly.
|
// iOS, Android, WebAssembly.
|
||||||
func NewWindow(options ...Option) *Window {
|
func NewWindow(options ...Option) *Window {
|
||||||
|
// Measure decoration height.
|
||||||
|
deco := new(widget.Decorations)
|
||||||
|
theme := material.NewTheme(gofont.Collection())
|
||||||
|
decoStyle := material.Decorations(theme, deco, 0, "")
|
||||||
|
gtx := layout.Context{
|
||||||
|
Ops: new(op.Ops),
|
||||||
|
// Measure in Dp.
|
||||||
|
Metric: unit.Metric{},
|
||||||
|
}
|
||||||
|
// Allow plenty of space.
|
||||||
|
gtx.Constraints.Max.Y = 200
|
||||||
|
dims := decoStyle.Layout(gtx)
|
||||||
|
decoHeight := unit.Dp(dims.Size.Y)
|
||||||
defaultOptions := []Option{
|
defaultOptions := []Option{
|
||||||
Size(800, 600),
|
Size(800, 600),
|
||||||
Title("Gio"),
|
Title("Gio"),
|
||||||
Decorated(true),
|
Decorated(true),
|
||||||
|
decoHeightOpt(decoHeight),
|
||||||
}
|
}
|
||||||
options = append(defaultOptions, options...)
|
options = append(defaultOptions, options...)
|
||||||
var cnf Config
|
var cnf Config
|
||||||
@@ -162,6 +177,8 @@ func NewWindow(options ...Option) *Window {
|
|||||||
actions: make(chan system.Action, 1),
|
actions: make(chan system.Action, 1),
|
||||||
nocontext: cnf.CustomRenderer,
|
nocontext: cnf.CustomRenderer,
|
||||||
}
|
}
|
||||||
|
w.decorations.Theme = theme
|
||||||
|
w.decorations.Decorations = deco
|
||||||
w.decorations.enabled = cnf.Decorated
|
w.decorations.enabled = cnf.Decorated
|
||||||
w.imeState.compose = key.Range{Start: -1, End: -1}
|
w.imeState.compose = key.Range{Start: -1, End: -1}
|
||||||
w.semantic.ids = make(map[router.SemanticID]router.SemanticNode)
|
w.semantic.ids = make(map[router.SemanticID]router.SemanticNode)
|
||||||
@@ -170,6 +187,12 @@ func NewWindow(options ...Option) *Window {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func decoHeightOpt(h unit.Dp) Option {
|
||||||
|
return func(m unit.Metric, c *Config) {
|
||||||
|
c.decoHeight = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Events returns the channel where events are delivered.
|
// Events returns the channel where events are delivered.
|
||||||
func (w *Window) Events() <-chan event.Event {
|
func (w *Window) Events() <-chan event.Event {
|
||||||
return w.out
|
return w.out
|
||||||
@@ -474,6 +497,11 @@ func (c *callbacks) Event(e event.Event) bool {
|
|||||||
opt(c.w.metric, &cnf)
|
opt(c.w.metric, &cnf)
|
||||||
}
|
}
|
||||||
c.w.decorations.enabled = cnf.Decorated
|
c.w.decorations.enabled = cnf.Decorated
|
||||||
|
decoHeight := c.w.decorations.height
|
||||||
|
if !c.w.decorations.enabled {
|
||||||
|
decoHeight = 0
|
||||||
|
}
|
||||||
|
opts = append(opts, decoHeightOpt(decoHeight))
|
||||||
c.d.Configure(opts)
|
c.d.Configure(opts)
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
@@ -879,9 +907,7 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
|
|||||||
case ConfigEvent:
|
case ConfigEvent:
|
||||||
w.decorations.Config = e2.Config
|
w.decorations.Config = e2.Config
|
||||||
if !w.fallbackDecorate() {
|
if !w.fallbackDecorate() {
|
||||||
// Decorations are no longer applied.
|
w.decorations.height = 0
|
||||||
w.decorations.Decorations = nil
|
|
||||||
w.decorations.size = image.Point{}
|
|
||||||
}
|
}
|
||||||
e2.Config = w.effectiveConfig()
|
e2.Config = w.effectiveConfig()
|
||||||
w.out <- e2
|
w.out <- e2
|
||||||
@@ -975,19 +1001,10 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (size, offse
|
|||||||
if !w.fallbackDecorate() {
|
if !w.fallbackDecorate() {
|
||||||
return e.Size, image.Pt(0, 0)
|
return e.Size, image.Pt(0, 0)
|
||||||
}
|
}
|
||||||
theme := w.decorations.Theme
|
|
||||||
if theme == nil {
|
|
||||||
theme = material.NewTheme(gofont.Collection())
|
|
||||||
w.decorations.Theme = theme
|
|
||||||
}
|
|
||||||
deco := w.decorations.Decorations
|
deco := w.decorations.Decorations
|
||||||
if deco == nil {
|
|
||||||
deco = new(widget.Decorations)
|
|
||||||
w.decorations.Decorations = deco
|
|
||||||
}
|
|
||||||
allActions := system.ActionMinimize | system.ActionMaximize | system.ActionUnmaximize |
|
allActions := system.ActionMinimize | system.ActionMaximize | system.ActionUnmaximize |
|
||||||
system.ActionClose | system.ActionMove
|
system.ActionClose | system.ActionMove
|
||||||
style := material.Decorations(theme, deco, allActions, w.decorations.Config.Title)
|
style := material.Decorations(w.decorations.Theme, deco, allActions, w.decorations.Config.Title)
|
||||||
// Update the decorations based on the current window mode.
|
// Update the decorations based on the current window mode.
|
||||||
var actions system.Action
|
var actions system.Action
|
||||||
switch m := w.decorations.Config.Mode; m {
|
switch m := w.decorations.Config.Mode; m {
|
||||||
@@ -1010,22 +1027,22 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) (size, offse
|
|||||||
Metric: e.Metric,
|
Metric: e.Metric,
|
||||||
Constraints: layout.Exact(e.Size),
|
Constraints: layout.Exact(e.Size),
|
||||||
}
|
}
|
||||||
dims := style.Layout(gtx)
|
style.Layout(gtx)
|
||||||
// Update the window based on the actions on the decorations.
|
// Update the window based on the actions on the decorations.
|
||||||
w.Perform(deco.Actions())
|
w.Perform(deco.Actions())
|
||||||
// Offset to place the frame content below the decorations.
|
// Offset to place the frame content below the decorations.
|
||||||
decoSize := image.Point{Y: dims.Size.Y}
|
decoHeight := gtx.Dp(w.decorations.Config.decoHeight)
|
||||||
appSize := e.Size.Sub(decoSize)
|
if w.decorations.currentHeight != decoHeight {
|
||||||
if w.decorations.size != decoSize {
|
w.decorations.currentHeight = decoHeight
|
||||||
w.decorations.size = decoSize
|
|
||||||
w.out <- ConfigEvent{Config: w.effectiveConfig()}
|
w.out <- ConfigEvent{Config: w.effectiveConfig()}
|
||||||
}
|
}
|
||||||
return appSize, image.Pt(0, decoSize.Y)
|
e.Size.Y -= w.decorations.currentHeight
|
||||||
|
return e.Size, image.Pt(0, decoHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) effectiveConfig() Config {
|
func (w *Window) effectiveConfig() Config {
|
||||||
cnf := w.decorations.Config
|
cnf := w.decorations.Config
|
||||||
cnf.Size = cnf.Size.Sub(w.decorations.size)
|
cnf.Size.Y -= w.decorations.currentHeight
|
||||||
cnf.Decorated = w.decorations.enabled || cnf.Decorated
|
cnf.Decorated = w.decorations.enabled || cnf.Decorated
|
||||||
return cnf
|
return cnf
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user