mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
3d37491342
The unit.Value is a struct and thus more inconvenient to use than its
underlying float32 type. In addition, most uses don't need a general
value, but rather a specific unit given by the context. This change
replaces unit.Value with two float32 units, Dp and Sp. It also changes
variables and parameters of unit.Value to a specific unit type matching
the context. That is, unit.Dp everywhere except for text sizes which are
in Sp.
Switching to typed float32s has multiple advantages
- They can be constants:
const touchSlop = unit.Dp(16)
- Casting untyped constants is no longer necessary:
insets := layout.UniformInset(16)
- Calculation with values is natural:
func (s ScrollbarStyle) Width() unit.Dp {
return s.Indicator.MinorWidth + s.Track.MinorPadding + s.Track.MinorPadding
}
The main API change is that calls to gtx.Px must be replaced with either
gtx.Dp or gtx.Sp depending on the unit.
Idea by Christophe Meessen.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package widget
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"math/bits"
|
|
|
|
"gioui.org/gesture"
|
|
"gioui.org/io/pointer"
|
|
"gioui.org/io/system"
|
|
"gioui.org/layout"
|
|
"gioui.org/op/clip"
|
|
)
|
|
|
|
// Decorations handles the states of window decorations.
|
|
type Decorations struct {
|
|
move gesture.Drag
|
|
clicks []Clickable
|
|
resize [8]struct {
|
|
gesture.Hover
|
|
gesture.Drag
|
|
}
|
|
actions system.Action
|
|
maximized bool
|
|
}
|
|
|
|
// LayoutMove lays out the widget that makes a window movable.
|
|
func (d *Decorations) LayoutMove(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
|
dims := w(gtx)
|
|
d.move.Events(gtx.Metric, gtx, gesture.Both)
|
|
st := clip.Rect{Max: dims.Size}.Push(gtx.Ops)
|
|
d.move.Add(gtx.Ops)
|
|
if d.move.Pressed() {
|
|
d.actions |= system.ActionMove
|
|
}
|
|
st.Pop()
|
|
return dims
|
|
}
|
|
|
|
// Clickable returns the clickable for the given single action.
|
|
func (d *Decorations) Clickable(action system.Action) *Clickable {
|
|
if bits.OnesCount(uint(action)) != 1 {
|
|
panic(fmt.Errorf("not a single action"))
|
|
}
|
|
idx := bits.TrailingZeros(uint(action))
|
|
if n := idx - len(d.clicks); n >= 0 {
|
|
d.clicks = append(d.clicks, make([]Clickable, n+1)...)
|
|
}
|
|
click := &d.clicks[idx]
|
|
if click.Clicked() {
|
|
if action == system.ActionMaximize {
|
|
if d.maximized {
|
|
d.maximized = false
|
|
d.actions |= system.ActionUnmaximize
|
|
} else {
|
|
d.maximized = true
|
|
d.actions |= system.ActionMaximize
|
|
}
|
|
} else {
|
|
d.actions |= action
|
|
}
|
|
}
|
|
return click
|
|
}
|
|
|
|
// LayoutResize lays out the resize actions.
|
|
func (d *Decorations) LayoutResize(gtx layout.Context, actions system.Action) {
|
|
cs := gtx.Constraints.Max
|
|
wh := gtx.Dp(10)
|
|
s := []struct {
|
|
system.Action
|
|
image.Rectangle
|
|
}{
|
|
{system.ActionResizeNorth, image.Rect(0, 0, cs.X, wh)},
|
|
{system.ActionResizeSouth, image.Rect(0, cs.Y-wh, cs.X, cs.Y)},
|
|
{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.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 actions&action == 0 {
|
|
continue
|
|
}
|
|
rsz := &d.resize[i]
|
|
rsz.Events(gtx.Metric, gtx, gesture.Both)
|
|
if rsz.Drag.Dragging() {
|
|
d.actions |= action
|
|
}
|
|
st := clip.Rect(data.Rectangle).Push(gtx.Ops)
|
|
if rsz.Hover.Hovered(gtx) {
|
|
action.Cursor().Add(gtx.Ops)
|
|
}
|
|
rsz.Drag.Add(gtx.Ops)
|
|
pass := pointer.PassOp{}.Push(gtx.Ops)
|
|
rsz.Hover.Add(gtx.Ops)
|
|
pass.Pop()
|
|
st.Pop()
|
|
}
|
|
}
|
|
|
|
// Perform updates the decorations as if the specified actions were
|
|
// performed by the user.
|
|
func (d *Decorations) Perform(actions system.Action) {
|
|
if actions&system.ActionMaximize != 0 {
|
|
d.maximized = true
|
|
}
|
|
if actions&(system.ActionUnmaximize|system.ActionMinimize|system.ActionFullscreen) != 0 {
|
|
d.maximized = false
|
|
}
|
|
}
|
|
|
|
// Actions returns the set of actions activated by the user.
|
|
func (d *Decorations) Actions() system.Action {
|
|
a := d.actions
|
|
d.actions = 0
|
|
return a
|
|
}
|
|
|
|
// Maximized returns whether the window is maximized.
|
|
func (d *Decorations) Maximized() bool {
|
|
return d.maximized
|
|
}
|