I forgor to commit all this stuff

This commit is contained in:
Sasha Koshka 2023-07-04 00:04:00 -04:00
parent 5e7c666d92
commit 208c50a66b
5 changed files with 305 additions and 35 deletions

49
box.go
View File

@ -10,7 +10,7 @@ import "git.tebibyte.media/tomo/tomo/canvas"
type box struct { type box struct {
backend *Backend backend *Backend
window *window parent parent
bounds image.Rectangle bounds image.Rectangle
minSize image.Point minSize image.Point
@ -24,9 +24,7 @@ type box struct {
focused bool focused bool
focusable bool focusable bool
drawClean bool canvas canvas.Canvas
layoutClean bool
drawer canvas.Drawer drawer canvas.Drawer
on struct { on struct {
@ -47,11 +45,9 @@ type box struct {
} }
func (backend *Backend) NewBox() tomo.Box { func (backend *Backend) NewBox() tomo.Box {
box := &box { return &box {
backend: backend, backend: backend,
} }
box.drawer = box
return box
} }
func (this *box) Box () tomo.Box { func (this *box) Box () tomo.Box {
@ -59,7 +55,7 @@ func (this *box) Box () tomo.Box {
} }
func (this *box) Window () tomo.Window { func (this *box) Window () tomo.Window {
return this.window return this.parent.window()
} }
func (this *box) Bounds () image.Rectangle { func (this *box) Bounds () image.Rectangle {
@ -114,9 +110,9 @@ func (this *box) SetDNDAccept (types ...data.Mime) {
func (this *box) SetFocused (focused bool) { func (this *box) SetFocused (focused bool) {
if this.Focused () && !focused { if this.Focused () && !focused {
this.window.focus(nil) this.parent.window().focus(nil)
} else if !this.Focused() && focused { } else if !this.Focused() && focused {
this.window.focus(this) this.parent.window().focus(this)
} }
} }
@ -129,15 +125,15 @@ func (this *box) SetFocusable (focusable bool) {
} }
func (this *box) Focused () bool { func (this *box) Focused () bool {
return this == this.window.focused return this == this.parent.window().focused
} }
func (this *box) Modifiers () input.Modifiers { func (this *box) Modifiers () input.Modifiers {
return this.window.modifiers return this.parent.window().modifiers
} }
func (this *box) MousePosition () image.Point { func (this *box) MousePosition () image.Point {
return this.window.mousePosition return this.parent.window().mousePosition
} }
// ----- event handlers ----------------------------------------------------- // // ----- event handlers ----------------------------------------------------- //
@ -218,11 +214,32 @@ func (this *box) Draw (can canvas.Canvas) {
pen.Rectangle(bounds) pen.Rectangle(bounds)
} }
func (this *box) doDraw () {
if this.canvas == nil { return }
if this.drawer == nil {
this.Draw(this.canvas)
} else {
this.drawer.Draw(this.canvas)
}
}
func (this *box) doLayout () {
this.canvas = this.parent.canvas().Clip(this.bounds)
}
func (this *box) setParent (parent parent) {
this.parent = parent
}
func (this *box) recursiveRedo () {
this.doLayout()
this.doDraw()
}
func (this *box) invalidateDraw () { func (this *box) invalidateDraw () {
this.drawClean = false this.parent.window().invalidateDraw(this)
} }
func (this *box) invalidateLayout () { func (this *box) invalidateLayout () {
this.invalidateDraw() this.parent.window().invalidateLayout(this)
this.layoutClean = false
} }

View File

@ -3,6 +3,7 @@ package xcanvas
import "image" import "image"
import "image/color" import "image/color"
import "github.com/jezek/xgbutil" import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto"
import "git.tebibyte.media/tomo/ggfx" import "git.tebibyte.media/tomo/ggfx"
import "github.com/jezek/xgbutil/xgraphics" import "github.com/jezek/xgbutil/xgraphics"
import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/tomo/canvas"
@ -36,10 +37,16 @@ func (this Canvas) Pen () canvas.Pen {
} }
// Clip returns a sub-canvas of this canvas. // Clip returns a sub-canvas of this canvas.
func (this Canvas) Clip (bounds image.Rectangle) Canvas { func (this Canvas) Clip (bounds image.Rectangle) canvas.Canvas {
return Canvas { this.Image.SubImage(bounds).(*xgraphics.Image) } return Canvas { this.Image.SubImage(bounds).(*xgraphics.Image) }
} }
// Push pushes this canvas to the screen.
func (this Canvas) Push (window xproto.Window) {
this.XDraw()
this.XExpPaint(window, this.Bounds().Min.X, this.Bounds().Min.Y)
}
// TODO: we need to implement: // TODO: we need to implement:
// - cap // - cap
// - joint // - joint

135
event.go Normal file
View File

@ -0,0 +1,135 @@
package x
import "image"
import "github.com/jezek/xgbutil"
import "github.com/jezek/xgb/xproto"
import "github.com/jezek/xgbutil/xevent"
func (window *window) handleExpose (
connection *xgbutil.XUtil,
event xevent.ExposeEvent,
) {
window.compressExpose(*event.ExposeEvent)
window.xCanvas.Push(window.xWindow.Id)
}
func (window *window) updateBounds () {
// FIXME: some window managers parent windows more than once, we might
// need to sum up all their positions.
decorGeometry, _ := window.xWindow.DecorGeometry()
windowGeometry, _ := window.xWindow.Geometry()
origin := image.Pt(
windowGeometry.X() + decorGeometry.X(),
windowGeometry.Y() + decorGeometry.Y())
window.metrics.bounds = image.Rectangle {
Min: origin,
Max: origin.Add(image.Pt(windowGeometry.Width(), windowGeometry.Height())),
}
}
func (window *window) handleConfigureNotify (
connection *xgbutil.XUtil,
event xevent.ConfigureNotifyEvent,
) {
if window.root == nil { return }
configureEvent := *event.ConfigureNotifyEvent
newWidth := int(configureEvent.Width)
newHeight := int(configureEvent.Height)
sizeChanged :=
window.metrics.bounds.Dx() != newWidth ||
window.metrics.bounds.Dy() != newHeight
window.updateBounds()
if sizeChanged {
configureEvent = window.compressConfigureNotify(configureEvent)
window.reallocateCanvas()
window.root.SetBounds(window.metrics.bounds)
// TODO figure out what to do with this
// if !window.exposeEventFollows(configureEvent) {
// }
}
}
func (window *window) exposeEventFollows (event xproto.ConfigureNotifyEvent) (found bool) {
nextEvents := xevent.Peek(window.backend.x)
if len(nextEvents) > 0 {
untypedEvent := nextEvents[0]
if untypedEvent.Err == nil {
typedEvent, ok :=
untypedEvent.Event.(xproto.ConfigureNotifyEvent)
if ok && typedEvent.Window == event.Window {
return true
}
}
}
return false
}
func (window *window) compressExpose (
firstEvent xproto.ExposeEvent,
) (
lastEvent xproto.ExposeEvent,
region image.Rectangle,
) {
region = image.Rect (
int(firstEvent.X), int(firstEvent.Y),
int(firstEvent.X + firstEvent.Width),
int(firstEvent.Y + firstEvent.Height))
window.backend.x.Sync()
xevent.Read(window.backend.x, false)
lastEvent = firstEvent
for index, untypedEvent := range xevent.Peek(window.backend.x) {
if untypedEvent.Err != nil { continue }
typedEvent, ok := untypedEvent.Event.(xproto.ExposeEvent)
if !ok { continue }
if firstEvent.Window == typedEvent.Window {
region = region.Union (image.Rect (
int(typedEvent.X), int(typedEvent.Y),
int(typedEvent.X + typedEvent.Width),
int(typedEvent.Y + typedEvent.Height)))
lastEvent = typedEvent
defer func (index int) {
xevent.DequeueAt(window.backend.x, index)
} (index)
}
}
return
}
func (window *window) compressConfigureNotify (
firstEvent xproto.ConfigureNotifyEvent,
) (
lastEvent xproto.ConfigureNotifyEvent,
) {
window.backend.x.Sync()
xevent.Read(window.backend.x, false)
lastEvent = firstEvent
for index, untypedEvent := range xevent.Peek(window.backend.x) {
if untypedEvent.Err != nil { continue }
typedEvent, ok := untypedEvent.Event.(xproto.ConfigureNotifyEvent)
if !ok { continue }
if firstEvent.Event == typedEvent.Event &&
firstEvent.Window == typedEvent.Window {
lastEvent = typedEvent
defer func (index int) {
xevent.DequeueAt(window.backend.x, index)
} (index)
}
}
return
}

View File

@ -1,20 +1,99 @@
package x package x
import "image"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/x/canvas"
import "git.tebibyte.media/tomo/tomo/canvas"
type boxSet map[anyBox] struct { }
func (set boxSet) Empty () bool {
return set == nil || len(set) == 0
}
func (set boxSet) Has (box anyBox) bool {
if set == nil { return false }
_, ok := set[box]
return ok
}
func (set *boxSet) Add (box anyBox) {
if *set == nil {
*set = make(boxSet)
}
(*set)[box] = struct { } { }
}
func (set *boxSet) Pop () anyBox {
for box := range *set {
delete(*set, box)
return box
}
return nil
}
type parent interface {
window () *window
canvas () canvas.Canvas
}
type anyBox interface { type anyBox interface {
tomo.Box tomo.Box
invalidateDraw () doDraw ()
invalidateLayout () doLayout ()
setParent (parent)
recursiveRedo ()
} }
func (window *window) SetRoot (root tomo.Object) { func (window *window) SetRoot (root tomo.Object) {
box := root.Box().(anyBox)
window.root.setParent(nil)
box.setParent(window)
window.invalidateLayout(box)
window.root = box
} }
func (window *window) focus (bx anyBox) { func (window *window) window () *window {
if window.focused == bx { return } return window
window.focused.invalidateDraw() }
window.focused = bx
bx.invalidateDraw() func (window *window) canvas () canvas.Canvas {
return window.xCanvas
}
func (window *window) invalidateDraw (box anyBox) {
window.needDraw.Add(box)
}
func (window *window) invalidateLayout (box anyBox) {
window.needLayout.Add(box)
window.invalidateDraw(box)
}
func (window *window) focus (box anyBox) {
if window.focused == box { return }
window.invalidateDraw(window.focused)
window.focused = box
window.invalidateDraw(box)
}
func (window *window) afterEvent () {
if window.needRedo {
if window.root != nil {
window.root.recursiveRedo()
}
window.xCanvas.Push(window.xWindow.Id)
return
}
for len(window.needLayout) > 0 {
window.needLayout.Pop().doLayout()
}
var toPush image.Rectangle
for len(window.needDraw) > 0 {
box := window.needDraw.Pop()
box.doDraw()
toPush = toPush.Union(box.Bounds())
}
window.xCanvas.Clip(toPush).(xcanvas.Canvas).Push(window.xWindow.Id)
} }

View File

@ -4,6 +4,7 @@ import "image"
// import "errors" // import "errors"
import "git.tebibyte.media/tomo/tomo" import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/x/canvas"
import "git.tebibyte.media/tomo/tomo/data" import "git.tebibyte.media/tomo/tomo/data"
import "git.tebibyte.media/tomo/tomo/input" import "git.tebibyte.media/tomo/tomo/input"
import "git.tebibyte.media/tomo/tomo/event" import "git.tebibyte.media/tomo/tomo/event"
@ -22,7 +23,8 @@ type mainWindow struct { *window }
type window struct { type window struct {
backend *Backend backend *Backend
xWindow *xwindow.Window xWindow *xwindow.Window
xCanvas *xgraphics.Image xImage *xgraphics.Image
xCanvas xcanvas.Canvas
title string title string
@ -41,6 +43,10 @@ type window struct {
root anyBox root anyBox
focused anyBox focused anyBox
needDraw boxSet
needLayout boxSet
needRedo bool
} }
func (backend *Backend) NewWindow ( func (backend *Backend) NewWindow (
@ -124,7 +130,7 @@ func (backend *Backend) newWindow (
window.metrics.bounds = bounds window.metrics.bounds = bounds
window.setMinimumSize(8, 8) window.setMinimumSize(8, 8)
// window.reallocateCanvas() window.reallocateCanvas()
backend.windows[window.xWindow.Id] = window backend.windows[window.xWindow.Id] = window
@ -237,13 +243,13 @@ func (window *window) Paste (callback func (data.Data, error), accept ...data.Mi
} }
func (window *window) Show () { func (window *window) Show () {
// if window.child == nil { // fill the screen with black if there is no active child
// window.xCanvas.For (func (x, y int) xgraphics.BGRA { if window.root == nil {
// return xgraphics.BGRA { } window.xCanvas.For (func (x, y int) xgraphics.BGRA {
// }) return xgraphics.BGRA { }
// })
// window.pushRegion(window.xCanvas.Bounds()) window.xCanvas.Push(window.xWindow.Id)
// } }
window.xWindow.Map() window.xWindow.Map()
if window.shy { window.grabInput() } if window.shy { window.grabInput() }
@ -265,7 +271,7 @@ func (window *window) Close () {
window.modalParent.hasModal = false window.modalParent.hasModal = false
} }
window.Hide() window.Hide()
// window.Adopt(nil) window.SetRoot(nil)
delete(window.backend.windows, window.xWindow.Id) delete(window.backend.windows, window.xWindow.Id)
window.xWindow.Destroy() window.xWindow.Destroy()
} }
@ -307,6 +313,32 @@ func (window *window) setClientLeader (leader *window) error {
hints) hints)
} }
func (window *window) reallocateCanvas () {
previousWidth := window.xCanvas.Bounds().Dx()
previousHeight := window.xCanvas.Bounds().Dy()
newWidth := window.metrics.bounds.Dx()
newHeight := window.metrics.bounds.Dy()
larger := newWidth > previousWidth || newHeight > previousHeight
smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2
allocStep := 128
if larger || smaller {
if window.xCanvas.Image != nil {
window.xCanvas.Destroy()
}
window.xCanvas = xcanvas.NewFrom(xgraphics.New (
window.backend.x,
image.Rect (
0, 0,
(newWidth / allocStep + 1) * allocStep,
(newHeight / allocStep + 1) * allocStep)))
window.xCanvas.CreatePixmap()
}
window.needRedo = true
}
func (window *window) setMinimumSize (width, height int) { func (window *window) setMinimumSize (width, height int) {
if width < 8 { width = 8 } if width < 8 { width = 8 }
if height < 8 { height = 8 } if height < 8 { height = 8 }