diff --git a/app/os_wayland.go b/app/os_wayland.go index cb22d7db..c71ba914 100644 --- a/app/os_wayland.go +++ b/app/os_wayland.go @@ -1068,11 +1068,11 @@ func (w *window) resize(a system.Action) { case system.ActionResizeNorthWest: edge = C.XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT case system.ActionResizeNorthEast: - edge = C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT + edge = C.XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT case system.ActionResizeSouthEast: edge = C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT case system.ActionResizeSouthWest: - edge = C.XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT + edge = C.XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT default: return } @@ -1115,9 +1115,9 @@ func (w *window) SetCursor(name pointer.CursorName) { case pointer.CursorTopLeftResize: name = "top_left_corner" case pointer.CursorTopRightResize: - name = "bottom_left_corner" - case pointer.CursorBottomLeftResize: name = "top_right_corner" + case pointer.CursorBottomLeftResize: + name = "bottom_left_corner" case pointer.CursorBottomRightResize: name = "bottom_right_corner" case pointer.CursorLeftResize: diff --git a/app/window.go b/app/window.go index ebb3a3fa..56c0b1f6 100644 --- a/app/window.go +++ b/app/window.go @@ -68,7 +68,7 @@ type Window struct { decorations struct { op.Ops Config - *material.Decorations + *material.DecorationsStyle } callbacks callbacks @@ -688,7 +688,7 @@ 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 + deco := w.decorations.DecorationsStyle if deco == nil { theme := material.NewTheme(gofont.Collection()) allActions := system.ActionMinimize | system.ActionMaximize | system.ActionUnmaximize | @@ -697,10 +697,9 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) image.Point system.ActionResizeWest | system.ActionResizeEast | system.ActionResizeNorthWest | system.ActionResizeSouthWest | system.ActionResizeNorthEast | system.ActionResizeSouthEast - deco = &material.Decorations{ - DecorationsStyle: material.Decorate(theme, allActions), - } - w.decorations.Decorations = deco + style := material.Decorate(theme, allActions) + deco = &style + w.decorations.DecorationsStyle = &style } // Update the decorations based on the current window mode. var actions system.Action @@ -716,9 +715,9 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) image.Point default: panic(fmt.Errorf("unknown WindowMode %v", m)) } - deco.Perform(actions) + deco.Decorations.Perform(actions) // Update the window based on the actions on the decorations. - d.Perform(deco.Actions()) + d.Perform(deco.Decorations.Actions()) gtx := layout.Context{ Ops: o, @@ -728,7 +727,7 @@ func (w *Window) decorate(d driver, e system.FrameEvent, o *op.Ops) image.Point Constraints: layout.Exact(e.Size), } rec := op.Record(o) - dims := deco.Decorate(gtx, w.decorations.Config.Title) + dims := deco.Layout(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} @@ -837,8 +836,9 @@ func CustomRenderer(custom bool) Option { } } -// Decorated controls whether automatic window decorations -// are enabled. +// Decorated controls whether Gio and/or the platform are responsible +// for drawing window decorations. Providing false indicates that +// the application will either be undecorated or will draw its own decorations. func Decorated(enabled bool) Option { return func(_ unit.Metric, cnf *Config) { cnf.Decorated = enabled diff --git a/widget/material/decorations.go b/widget/material/decorations.go index edf393b4..a8cea82d 100644 --- a/widget/material/decorations.go +++ b/widget/material/decorations.go @@ -19,6 +19,7 @@ import ( // DecorationsStyle provides the style elements for Decorations. type DecorationsStyle struct { + Decorations Actions system.Action Title LabelStyle Background color.NRGBA @@ -39,7 +40,6 @@ func Decorate(th *Theme, actions system.Action) DecorationsStyle { // Decorations provides window decorations. type Decorations struct { - DecorationsStyle actions struct { layout.List clicks []widget.Clickable @@ -54,22 +54,23 @@ type Decorations struct { maximized bool } -// Decorate 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.Dimensions { +// Layout a window with the title and actions defined in DecorationsStyle. +func (d *DecorationsStyle) Layout(gtx layout.Context, title string) layout.Dimensions { rec := op.Record(gtx.Ops) - dims := d.layoutDecorations(gtx, title) + label := d.Title + label.Text = title + paint.ColorOp{Color: d.Foreground}.Add(gtx.Ops) + dims := d.Decorations.layoutDecorations(gtx, d.Actions, label.Layout) decos := rec.Stop() r := clip.Rect{Max: dims.Size} - paint.FillShape(gtx.Ops, d.DecorationsStyle.Background, r.Op()) + paint.FillShape(gtx.Ops, d.Background, r.Op()) decos.Add(gtx.Ops) - d.layoutResizing(gtx) + d.Decorations.layoutResizing(gtx, d.Actions) return dims } -func (d *Decorations) layoutResizing(gtx layout.Context) { - cs := gtx.Constraints.Min +func (d *Decorations) layoutResizing(gtx layout.Context, actions system.Action) { + cs := gtx.Constraints.Max wh := gtx.Px(unit.Dp(10)) s := []struct { system.Action @@ -80,13 +81,13 @@ func (d *Decorations) layoutResizing(gtx layout.Context) { {system.ActionResizeWest, image.Rect(cs.X-wh, 0, cs.X, cs.Y)}, {system.ActionResizeEast, image.Rect(0, 0, wh, cs.Y)}, {system.ActionResizeNorthWest, image.Rect(0, 0, wh, wh)}, - {system.ActionResizeSouthWest, image.Rect(cs.X-wh, 0, cs.X, wh)}, - {system.ActionResizeNorthEast, image.Rect(0, cs.Y-wh, wh, cs.Y)}, + {system.ActionResizeNorthEast, image.Rect(cs.X-wh, 0, cs.X, wh)}, + {system.ActionResizeSouthWest, image.Rect(0, cs.Y-wh, wh, cs.Y)}, {system.ActionResizeSouthEast, image.Rect(cs.X-wh, cs.Y-wh, cs.X, cs.Y)}, } for i, data := range s { action := data.Action - if d.DecorationsStyle.Actions&action == 0 { + if actions&action == 0 { continue } rsz := &d.actions.resize[i] @@ -106,7 +107,7 @@ func (d *Decorations) layoutResizing(gtx layout.Context) { } } -func (d *Decorations) layoutDecorations(gtx layout.Context, title string) layout.Dimensions { +func (d *Decorations) layoutDecorations(gtx layout.Context, actions system.Action, title layout.Widget) layout.Dimensions { gtx.Constraints.Min.Y = 0 inset := layout.UniformInset(unit.Dp(10)) return layout.Flex{ @@ -115,9 +116,8 @@ func (d *Decorations) layoutDecorations(gtx layout.Context, title string) layout Spacing: layout.SpaceBetween, }.Layout(gtx, layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { - d.DecorationsStyle.Title.Text = title - dims := inset.Layout(gtx, d.DecorationsStyle.Title.Layout) - if d.DecorationsStyle.Actions&system.ActionMove != 0 { + dims := inset.Layout(gtx, title) + if actions&system.ActionMove != 0 { d.actions.move.Events(gtx.Metric, gtx, gesture.Both) st := clip.Rect{Max: dims.Size}.Push(gtx.Ops) @@ -131,27 +131,16 @@ func (d *Decorations) layoutDecorations(gtx layout.Context, title string) layout }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { // Remove the unmaximize action as it is taken care of by maximize. - actions := d.DecorationsStyle.Actions &^ system.ActionUnmaximize + actions := actions &^ system.ActionUnmaximize an := bits.OnesCount(uint(actions)) if n := len(d.actions.clicks); n < an { d.actions.clicks = append(d.actions.clicks, make([]widget.Clickable, an-n)...) } + action := system.Action(1) return d.actions.Layout(gtx, an, func(gtx layout.Context, idx int) layout.Dimensions { - action := system.Action(1 << idx) - var w layout.Widget - switch actions & action { - case system.ActionMinimize: - w = d.minimizeWindow - case system.ActionMaximize: - if d.maximized { - w = d.maximizedWindow - } else { - w = d.maximizeWindow - } - case system.ActionClose: - w = d.closeWindow - default: - return layout.Dimensions{} + defer func() { action <<= 1 }() + for actions&action == 0 { + action <<= 1 } click := &d.actions.clicks[idx] if click.Clicked() { @@ -168,6 +157,21 @@ func (d *Decorations) layoutDecorations(gtx layout.Context, title string) layout } } return Clickable(gtx, click, func(gtx layout.Context) layout.Dimensions { + var w layout.Widget + switch actions & action { + case system.ActionMinimize: + w = minimizeWindow + case system.ActionMaximize: + if d.maximized { + w = maximizedWindow + } else { + w = maximizeWindow + } + case system.ActionClose: + w = closeWindow + default: + return layout.Dimensions{} + } return inset.Layout(gtx, w) }) }) @@ -200,13 +204,12 @@ var ( ) // minimizeWindows draws a line icon representing the minimize action. -func (d *Decorations) minimizeWindow(gtx layout.Context) layout.Dimensions { - paint.ColorOp{Color: d.DecorationsStyle.Foreground}.Add(gtx.Ops) +func minimizeWindow(gtx layout.Context) layout.Dimensions { size := gtx.Px(winIconSize) size32 := float32(size) margin := float32(gtx.Px(winIconMargin)) width := float32(gtx.Px(winIconStroke)) - p := &d.path + var p clip.Path p.Begin(gtx.Ops) p.MoveTo(f32.Point{X: margin, Y: size32 - margin}) p.LineTo(f32.Point{X: size32 - 2*margin, Y: size32 - margin}) @@ -220,8 +223,7 @@ func (d *Decorations) minimizeWindow(gtx layout.Context) layout.Dimensions { } // maximizeWindow draws a rectangle representing the maximize action. -func (d *Decorations) maximizeWindow(gtx layout.Context) layout.Dimensions { - paint.ColorOp{Color: d.DecorationsStyle.Foreground}.Add(gtx.Ops) +func maximizeWindow(gtx layout.Context) layout.Dimensions { size := gtx.Px(winIconSize) size32 := float32(size) margin := float32(gtx.Px(winIconMargin)) @@ -245,8 +247,7 @@ func (d *Decorations) maximizeWindow(gtx layout.Context) layout.Dimensions { } // maximizedWindow draws interleaved rectangles representing the un-maximize action. -func (d *Decorations) maximizedWindow(gtx layout.Context) layout.Dimensions { - paint.ColorOp{Color: d.DecorationsStyle.Foreground}.Add(gtx.Ops) +func maximizedWindow(gtx layout.Context) layout.Dimensions { size := gtx.Px(winIconSize) size32 := float32(size) margin := float32(gtx.Px(winIconMargin)) @@ -273,13 +274,12 @@ func (d *Decorations) maximizedWindow(gtx layout.Context) layout.Dimensions { } // closeWindow draws a cross representing the close action. -func (d *Decorations) closeWindow(gtx layout.Context) layout.Dimensions { - paint.ColorOp{Color: d.DecorationsStyle.Foreground}.Add(gtx.Ops) +func closeWindow(gtx layout.Context) layout.Dimensions { size := gtx.Px(winIconSize) size32 := float32(size) margin := float32(gtx.Px(winIconMargin)) width := float32(gtx.Px(winIconStroke)) - p := &d.path + var p clip.Path p.Begin(gtx.Ops) p.MoveTo(f32.Point{X: margin, Y: margin}) p.LineTo(f32.Point{X: size32 - margin, Y: size32 - margin})