robust-parenting #12
@ -14,7 +14,7 @@ type scrollSum struct {
|
|||||||
|
|
||||||
const scrollDistance = 16
|
const scrollDistance = 16
|
||||||
|
|
||||||
func (sum *scrollSum) add (button xproto.Button, window *Window, state uint16) {
|
func (sum *scrollSum) add (button xproto.Button, window *window, state uint16) {
|
||||||
shift :=
|
shift :=
|
||||||
(state & xproto.ModMaskShift) > 0 ||
|
(state & xproto.ModMaskShift) > 0 ||
|
||||||
(state & window.backend.modifierMasks.shiftLock) > 0
|
(state & window.backend.modifierMasks.shiftLock) > 0
|
||||||
@ -44,7 +44,7 @@ func (sum *scrollSum) add (button xproto.Button, window *Window, state uint16) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleExpose (
|
func (window *window) handleExpose (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.ExposeEvent,
|
event xevent.ExposeEvent,
|
||||||
) {
|
) {
|
||||||
@ -52,7 +52,7 @@ func (window *Window) handleExpose (
|
|||||||
window.pushRegion(region)
|
window.pushRegion(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleConfigureNotify (
|
func (window *window) handleConfigureNotify (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.ConfigureNotifyEvent,
|
event xevent.ConfigureNotifyEvent,
|
||||||
) {
|
) {
|
||||||
@ -81,7 +81,7 @@ func (window *Window) handleConfigureNotify (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (found bool) {
|
func (window *window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (found bool) {
|
||||||
nextEvents := xevent.Peek(window.backend.connection)
|
nextEvents := xevent.Peek(window.backend.connection)
|
||||||
if len(nextEvents) > 0 {
|
if len(nextEvents) > 0 {
|
||||||
untypedEvent := nextEvents[0]
|
untypedEvent := nextEvents[0]
|
||||||
@ -97,7 +97,7 @@ func (window *Window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (fo
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) modifiersFromState (
|
func (window *window) modifiersFromState (
|
||||||
state uint16,
|
state uint16,
|
||||||
) (
|
) (
|
||||||
modifiers input.Modifiers,
|
modifiers input.Modifiers,
|
||||||
@ -114,7 +114,7 @@ func (window *Window) modifiersFromState (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleKeyPress (
|
func (window *window) handleKeyPress (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.KeyPressEvent,
|
event xevent.KeyPressEvent,
|
||||||
) {
|
) {
|
||||||
@ -141,7 +141,7 @@ func (window *Window) handleKeyPress (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleKeyRelease (
|
func (window *window) handleKeyRelease (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.KeyReleaseEvent,
|
event xevent.KeyReleaseEvent,
|
||||||
) {
|
) {
|
||||||
@ -175,23 +175,25 @@ func (window *Window) handleKeyRelease (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleButtonPress (
|
func (window *window) handleButtonPress (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.ButtonPressEvent,
|
event xevent.ButtonPressEvent,
|
||||||
) {
|
) {
|
||||||
if window.child == nil { return }
|
if window.child == nil { return }
|
||||||
|
|
||||||
if child, ok := window.child.(elements.MouseTarget); ok {
|
buttonEvent := *event.ButtonPressEvent
|
||||||
buttonEvent := *event.ButtonPressEvent
|
if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 {
|
||||||
if buttonEvent.Detail >= 4 && buttonEvent.Detail <= 7 {
|
if child, ok := window.child.(elements.ScrollTarget); ok {
|
||||||
sum := scrollSum { }
|
sum := scrollSum { }
|
||||||
sum.add(buttonEvent.Detail, window, buttonEvent.State)
|
sum.add(buttonEvent.Detail, window, buttonEvent.State)
|
||||||
window.compressScrollSum(buttonEvent, &sum)
|
window.compressScrollSum(buttonEvent, &sum)
|
||||||
child.HandleMouseScroll (
|
child.HandleScroll (
|
||||||
int(buttonEvent.EventX),
|
int(buttonEvent.EventX),
|
||||||
int(buttonEvent.EventY),
|
int(buttonEvent.EventY),
|
||||||
float64(sum.x), float64(sum.y))
|
float64(sum.x), float64(sum.y))
|
||||||
} else {
|
}
|
||||||
|
} else {
|
||||||
|
if child, ok := window.child.(elements.MouseTarget); ok {
|
||||||
child.HandleMouseDown (
|
child.HandleMouseDown (
|
||||||
int(buttonEvent.EventX),
|
int(buttonEvent.EventX),
|
||||||
int(buttonEvent.EventY),
|
int(buttonEvent.EventY),
|
||||||
@ -201,7 +203,7 @@ func (window *Window) handleButtonPress (
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleButtonRelease (
|
func (window *window) handleButtonRelease (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.ButtonReleaseEvent,
|
event xevent.ButtonReleaseEvent,
|
||||||
) {
|
) {
|
||||||
@ -217,21 +219,21 @@ func (window *Window) handleButtonRelease (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) handleMotionNotify (
|
func (window *window) handleMotionNotify (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.MotionNotifyEvent,
|
event xevent.MotionNotifyEvent,
|
||||||
) {
|
) {
|
||||||
if window.child == nil { return }
|
if window.child == nil { return }
|
||||||
|
|
||||||
if child, ok := window.child.(elements.MouseTarget); ok {
|
if child, ok := window.child.(elements.MotionTarget); ok {
|
||||||
motionEvent := window.compressMotionNotify(*event.MotionNotifyEvent)
|
motionEvent := window.compressMotionNotify(*event.MotionNotifyEvent)
|
||||||
child.HandleMouseMove (
|
child.HandleMotion (
|
||||||
int(motionEvent.EventX),
|
int(motionEvent.EventX),
|
||||||
int(motionEvent.EventY))
|
int(motionEvent.EventY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) compressExpose (
|
func (window *window) compressExpose (
|
||||||
firstEvent xproto.ExposeEvent,
|
firstEvent xproto.ExposeEvent,
|
||||||
) (
|
) (
|
||||||
lastEvent xproto.ExposeEvent,
|
lastEvent xproto.ExposeEvent,
|
||||||
@ -268,7 +270,7 @@ func (window *Window) compressExpose (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) compressConfigureNotify (
|
func (window *window) compressConfigureNotify (
|
||||||
firstEvent xproto.ConfigureNotifyEvent,
|
firstEvent xproto.ConfigureNotifyEvent,
|
||||||
) (
|
) (
|
||||||
lastEvent xproto.ConfigureNotifyEvent,
|
lastEvent xproto.ConfigureNotifyEvent,
|
||||||
@ -296,7 +298,7 @@ func (window *Window) compressConfigureNotify (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) compressScrollSum (
|
func (window *window) compressScrollSum (
|
||||||
firstEvent xproto.ButtonPressEvent,
|
firstEvent xproto.ButtonPressEvent,
|
||||||
sum *scrollSum,
|
sum *scrollSum,
|
||||||
) {
|
) {
|
||||||
@ -323,7 +325,7 @@ func (window *Window) compressScrollSum (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) compressMotionNotify (
|
func (window *window) compressMotionNotify (
|
||||||
firstEvent xproto.MotionNotifyEvent,
|
firstEvent xproto.MotionNotifyEvent,
|
||||||
) (
|
) (
|
||||||
lastEvent xproto.MotionNotifyEvent,
|
lastEvent xproto.MotionNotifyEvent,
|
||||||
|
@ -14,7 +14,7 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
|||||||
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
import "git.tebibyte.media/sashakoshka/tomo/elements"
|
||||||
// import "runtime/debug"
|
// import "runtime/debug"
|
||||||
|
|
||||||
type Window struct {
|
type window struct {
|
||||||
backend *Backend
|
backend *Backend
|
||||||
xWindow *xwindow.Window
|
xWindow *xwindow.Window
|
||||||
xCanvas *xgraphics.Image
|
xCanvas *xgraphics.Image
|
||||||
@ -40,7 +40,7 @@ func (backend *Backend) NewWindow (
|
|||||||
) {
|
) {
|
||||||
if backend == nil { panic("nil backend") }
|
if backend == nil { panic("nil backend") }
|
||||||
|
|
||||||
window := &Window { backend: backend }
|
window := &window { backend: backend }
|
||||||
|
|
||||||
window.xWindow, err = xwindow.Generate(backend.connection)
|
window.xWindow, err = xwindow.Generate(backend.connection)
|
||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
@ -90,18 +90,15 @@ func (backend *Backend) NewWindow (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) Adopt (child elements.Element) {
|
func (window *window) NotifyMinimumSizeChange (child elements.Element) {
|
||||||
|
window.childMinimumSizeChangeCallback(child.MinimumSize())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (window *window) Adopt (child elements.Element) {
|
||||||
// disown previous child
|
// disown previous child
|
||||||
if window.child != nil {
|
if window.child != nil {
|
||||||
window.child.OnDamage(nil)
|
window.child.SetParent(nil)
|
||||||
window.child.OnMinimumSizeChange(nil)
|
window.child.DrawTo(nil, image.Rectangle { }, nil)
|
||||||
}
|
|
||||||
if previousChild, ok := window.child.(elements.Focusable); ok {
|
|
||||||
previousChild.OnFocusRequest(nil)
|
|
||||||
previousChild.OnFocusMotionRequest(nil)
|
|
||||||
if previousChild.Focused() {
|
|
||||||
previousChild.HandleUnfocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// adopt new child
|
// adopt new child
|
||||||
@ -112,15 +109,7 @@ func (window *Window) Adopt (child elements.Element) {
|
|||||||
if newChild, ok := child.(elements.Configurable); ok {
|
if newChild, ok := child.(elements.Configurable); ok {
|
||||||
newChild.SetConfig(window.config)
|
newChild.SetConfig(window.config)
|
||||||
}
|
}
|
||||||
if newChild, ok := child.(elements.Focusable); ok {
|
|
||||||
newChild.OnFocusRequest(window.childSelectionRequestCallback)
|
|
||||||
}
|
|
||||||
if child != nil {
|
if child != nil {
|
||||||
child.OnDamage(window.childDrawCallback)
|
|
||||||
child.OnMinimumSizeChange (func () {
|
|
||||||
window.childMinimumSizeChangeCallback (
|
|
||||||
child.MinimumSize())
|
|
||||||
})
|
|
||||||
if !window.childMinimumSizeChangeCallback(child.MinimumSize()) {
|
if !window.childMinimumSizeChangeCallback(child.MinimumSize()) {
|
||||||
window.resizeChildToFit()
|
window.resizeChildToFit()
|
||||||
window.redrawChildEntirely()
|
window.redrawChildEntirely()
|
||||||
@ -128,19 +117,19 @@ func (window *Window) Adopt (child elements.Element) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) Child () (child elements.Element) {
|
func (window *window) Child () (child elements.Element) {
|
||||||
child = window.child
|
child = window.child
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) SetTitle (title string) {
|
func (window *window) SetTitle (title string) {
|
||||||
ewmh.WmNameSet (
|
ewmh.WmNameSet (
|
||||||
window.backend.connection,
|
window.backend.connection,
|
||||||
window.xWindow.Id,
|
window.xWindow.Id,
|
||||||
title)
|
title)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) SetIcon (sizes []image.Image) {
|
func (window *window) SetIcon (sizes []image.Image) {
|
||||||
wmIcons := []ewmh.WmIcon { }
|
wmIcons := []ewmh.WmIcon { }
|
||||||
|
|
||||||
for _, icon := range sizes {
|
for _, icon := range sizes {
|
||||||
@ -179,7 +168,7 @@ func (window *Window) SetIcon (sizes []image.Image) {
|
|||||||
wmIcons)
|
wmIcons)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) Show () {
|
func (window *window) Show () {
|
||||||
if window.child == nil {
|
if window.child == nil {
|
||||||
window.xCanvas.For (func (x, y int) xgraphics.BGRA {
|
window.xCanvas.For (func (x, y int) xgraphics.BGRA {
|
||||||
return xgraphics.BGRA { }
|
return xgraphics.BGRA { }
|
||||||
@ -191,35 +180,35 @@ func (window *Window) Show () {
|
|||||||
window.xWindow.Map()
|
window.xWindow.Map()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) Hide () {
|
func (window *window) Hide () {
|
||||||
window.xWindow.Unmap()
|
window.xWindow.Unmap()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) Close () {
|
func (window *window) Close () {
|
||||||
if window.onClose != nil { window.onClose() }
|
if window.onClose != nil { window.onClose() }
|
||||||
delete(window.backend.windows, window.xWindow.Id)
|
delete(window.backend.windows, window.xWindow.Id)
|
||||||
window.xWindow.Destroy()
|
window.xWindow.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) OnClose (callback func ()) {
|
func (window *window) OnClose (callback func ()) {
|
||||||
window.onClose = callback
|
window.onClose = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) SetTheme (theme theme.Theme) {
|
func (window *window) SetTheme (theme theme.Theme) {
|
||||||
window.theme = theme
|
window.theme = theme
|
||||||
if child, ok := window.child.(elements.Themeable); ok {
|
if child, ok := window.child.(elements.Themeable); ok {
|
||||||
child.SetTheme(theme)
|
child.SetTheme(theme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) SetConfig (config config.Config) {
|
func (window *window) SetConfig (config config.Config) {
|
||||||
window.config = config
|
window.config = config
|
||||||
if child, ok := window.child.(elements.Configurable); ok {
|
if child, ok := window.child.(elements.Configurable); ok {
|
||||||
child.SetConfig(config)
|
child.SetConfig(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) reallocateCanvas () {
|
func (window *window) reallocateCanvas () {
|
||||||
window.canvas.Reallocate(window.metrics.width, window.metrics.height)
|
window.canvas.Reallocate(window.metrics.width, window.metrics.height)
|
||||||
|
|
||||||
previousWidth, previousHeight := 0, 0
|
previousWidth, previousHeight := 0, 0
|
||||||
@ -250,23 +239,28 @@ func (window *Window) reallocateCanvas () {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) redrawChildEntirely () {
|
func (window *window) redrawChildEntirely () {
|
||||||
window.pushRegion(window.paste(window.canvas))
|
window.paste(window.canvas.Bounds())
|
||||||
|
window.pushRegion(window.canvas.Bounds())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) resizeChildToFit () {
|
func (window *window) resizeChildToFit () {
|
||||||
window.skipChildDrawCallback = true
|
window.skipChildDrawCallback = true
|
||||||
window.child.DrawTo(window.canvas, window.canvas.Bounds())
|
window.child.DrawTo (
|
||||||
|
window.canvas,
|
||||||
|
window.canvas.Bounds(),
|
||||||
|
window.childDrawCallback)
|
||||||
window.skipChildDrawCallback = false
|
window.skipChildDrawCallback = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) childDrawCallback (region canvas.Canvas) {
|
func (window *window) childDrawCallback (region image.Rectangle) {
|
||||||
if window.skipChildDrawCallback { return }
|
if window.skipChildDrawCallback { return }
|
||||||
window.pushRegion(window.paste(region))
|
window.paste(region)
|
||||||
|
window.pushRegion(region)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) paste (canvas canvas.Canvas) (updatedRegion image.Rectangle) {
|
func (window *window) paste (region image.Rectangle) {
|
||||||
|
canvas := canvas.Cut(window.canvas, region)
|
||||||
data, stride := canvas.Buffer()
|
data, stride := canvas.Buffer()
|
||||||
bounds := canvas.Bounds().Intersect(window.xCanvas.Bounds())
|
bounds := canvas.Bounds().Intersect(window.xCanvas.Bounds())
|
||||||
|
|
||||||
@ -286,11 +280,9 @@ func (window *Window) paste (canvas canvas.Canvas) (updatedRegion image.Rectangl
|
|||||||
dstData[index + 3] = rgba.A
|
dstData[index + 3] = rgba.A
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return bounds
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) childMinimumSizeChangeCallback (width, height int) (resized bool) {
|
func (window *window) childMinimumSizeChangeCallback (width, height int) (resized bool) {
|
||||||
icccm.WmNormalHintsSet (
|
icccm.WmNormalHintsSet (
|
||||||
window.backend.connection,
|
window.backend.connection,
|
||||||
window.xWindow.Id,
|
window.xWindow.Id,
|
||||||
@ -312,14 +304,14 @@ func (window *Window) childMinimumSizeChangeCallback (width, height int) (resize
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) childSelectionRequestCallback () (granted bool) {
|
func (window *window) childSelectionRequestCallback () (granted bool) {
|
||||||
if _, ok := window.child.(elements.Focusable); ok {
|
if _, ok := window.child.(elements.Focusable); ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) childSelectionMotionRequestCallback (
|
func (window *window) childSelectionMotionRequestCallback (
|
||||||
direction input.KeynavDirection,
|
direction input.KeynavDirection,
|
||||||
) (
|
) (
|
||||||
granted bool,
|
granted bool,
|
||||||
@ -333,7 +325,7 @@ func (window *Window) childSelectionMotionRequestCallback (
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *Window) pushRegion (region image.Rectangle) {
|
func (window *window) pushRegion (region image.Rectangle) {
|
||||||
if window.xCanvas == nil { panic("whoopsie!!!!!!!!!!!!!!") }
|
if window.xCanvas == nil { panic("whoopsie!!!!!!!!!!!!!!") }
|
||||||
image, ok := window.xCanvas.SubImage(region).(*xgraphics.Image)
|
image, ok := window.xCanvas.SubImage(region).(*xgraphics.Image)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -30,7 +30,7 @@ type Backend struct {
|
|||||||
theme theme.Theme
|
theme theme.Theme
|
||||||
config config.Config
|
config config.Config
|
||||||
|
|
||||||
windows map[xproto.Window] *Window
|
windows map[xproto.Window] *window
|
||||||
|
|
||||||
open bool
|
open bool
|
||||||
}
|
}
|
||||||
@ -38,7 +38,7 @@ type Backend struct {
|
|||||||
// NewBackend instantiates an X backend.
|
// NewBackend instantiates an X backend.
|
||||||
func NewBackend () (output tomo.Backend, err error) {
|
func NewBackend () (output tomo.Backend, err error) {
|
||||||
backend := &Backend {
|
backend := &Backend {
|
||||||
windows: map[xproto.Window] *Window { },
|
windows: map[xproto.Window] *window { },
|
||||||
doChannel: make(chan func (), 0),
|
doChannel: make(chan func (), 0),
|
||||||
theme: theme.Default { },
|
theme: theme.Default { },
|
||||||
config: config.Default { },
|
config: config.Default { },
|
||||||
@ -79,7 +79,7 @@ func (backend *Backend) Stop () {
|
|||||||
if !backend.open { return }
|
if !backend.open { return }
|
||||||
backend.open = false
|
backend.open = false
|
||||||
|
|
||||||
toClose := []*Window { }
|
toClose := []*window { }
|
||||||
for _, window := range backend.windows {
|
for _, window := range backend.windows {
|
||||||
toClose = append(toClose, window)
|
toClose = append(toClose, window)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user