From 4c79d6772c87fe3e6de89aefc804a550c07cf409 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Wed, 5 Jul 2023 00:44:56 -0400 Subject: [PATCH] It displays things on a screen --- box.go | 2 ++ canvas/canvas.go | 48 ++++++++++++++++++++++++++++------------------ event.go | 9 ++++++--- system.go | 32 +++++++++++++++++++++++-------- window.go | 50 ++++++++++++++++++++++++++++++------------------ 5 files changed, 92 insertions(+), 49 deletions(-) diff --git a/box.go b/box.go index 7948f25..11e9b5a 100644 --- a/box.go +++ b/box.go @@ -237,9 +237,11 @@ func (this *box) recursiveRedo () { } func (this *box) invalidateDraw () { + if this.parent == nil { return } this.parent.window().invalidateDraw(this) } func (this *box) invalidateLayout () { + if this.parent == nil { return } this.parent.window().invalidateLayout(this) } diff --git a/canvas/canvas.go b/canvas/canvas.go index 7bb8d4c..534838f 100644 --- a/canvas/canvas.go +++ b/canvas/canvas.go @@ -14,18 +14,19 @@ type Canvas struct { } // New creates a new canvas from a bounding rectangle. -func New (x *xgbutil.XUtil, bounds image.Rectangle) Canvas { - return Canvas { xgraphics.New(x, bounds) } +func New (x *xgbutil.XUtil, bounds image.Rectangle) *Canvas { + return NewFrom(xgraphics.New(x, bounds)) } // NewFrom creates a new canvas from an existing xgraphics.Image. -func NewFrom (image *xgraphics.Image) Canvas { - return Canvas { image } +func NewFrom (image *xgraphics.Image) *Canvas { + if image == nil { return nil } + return &Canvas { image } } // Pen returns a new drawing context. -func (this Canvas) Pen () canvas.Pen { - return pen { +func (this *Canvas) Pen () canvas.Pen { + return &pen { image: this.Image, gfx: ggfx.Image[uint8] { Pix: this.Image.Pix, @@ -37,16 +38,25 @@ func (this Canvas) Pen () canvas.Pen { } // Clip returns a sub-canvas of this canvas. -func (this Canvas) Clip (bounds image.Rectangle) canvas.Canvas { - return Canvas { this.Image.SubImage(bounds).(*xgraphics.Image) } +func (this *Canvas) Clip (bounds image.Rectangle) canvas.Canvas { + this.assert() + subImage := this.Image.SubImage(bounds) + if subImage == nil { return nil } + xImage := subImage.(*xgraphics.Image) + return &Canvas { xImage } } // Push pushes this canvas to the screen. -func (this Canvas) Push (window xproto.Window) { +func (this *Canvas) Push (window xproto.Window) { + this.assert() this.XDraw() this.XExpPaint(window, this.Bounds().Min.X, this.Bounds().Min.Y) } +func (this *Canvas) assert () { + if this == nil { panic("nil canvas") } +} + // TODO: we need to implement: // - cap // - joint @@ -65,7 +75,7 @@ type pen struct { fill [4]uint8 } -func (this pen) Rectangle (bounds image.Rectangle) { +func (this *pen) Rectangle (bounds image.Rectangle) { if this.weight == 0 { this.gfx.FillRectangle(this.fill[:], bounds) } else { @@ -73,7 +83,7 @@ func (this pen) Rectangle (bounds image.Rectangle) { } } -func (this pen) Path (points ...image.Point) { +func (this *pen) Path (points ...image.Point) { if this.weight == 0 { this.gfx.FillPolygon(this.fill[:], points...) } else if this.closed { @@ -83,21 +93,21 @@ func (this pen) Path (points ...image.Point) { } } -func (this pen) Closed (closed bool) { this.closed = closed } -func (this pen) Cap (endCap canvas.Cap) { this.endCap = endCap } -func (this pen) Joint (joint canvas.Joint) { this.joint = joint } -func (this pen) StrokeWeight (weight int) { this.weight = weight } -func (this pen) StrokeAlign (align canvas.StrokeAlign) { this.align = align } +func (this *pen) Closed (closed bool) { this.closed = closed } +func (this *pen) Cap (endCap canvas.Cap) { this.endCap = endCap } +func (this *pen) Joint (joint canvas.Joint) { this.joint = joint } +func (this *pen) StrokeWeight (weight int) { this.weight = weight } +func (this *pen) StrokeAlign (align canvas.StrokeAlign) { this.align = align } -func (this pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) } -func (this pen) Fill (fill color.Color) { this.fill = convertColor(fill) } +func (this *pen) Stroke (stroke color.Color) { this.stroke = convertColor(stroke) } +func (this *pen) Fill (fill color.Color) { this.fill = convertColor(fill) } func convertColor (c color.Color) [4]uint8 { r, g, b, a := c.RGBA() return [4]uint8 { uint8(b >> 8), uint8(g >> 8), - uint8(a >> 8), uint8(r >> 8), + uint8(a >> 8), } } diff --git a/event.go b/event.go index 6faa15f..69b93a2 100644 --- a/event.go +++ b/event.go @@ -10,8 +10,11 @@ func (window *window) handleExpose ( connection *xgbutil.XUtil, event xevent.ExposeEvent, ) { + if window.xCanvas == nil { + window.reallocateCanvas() + } window.compressExpose(*event.ExposeEvent) - window.xCanvas.Push(window.xWindow.Id) + window.pushAll() } func (window *window) updateBounds () { @@ -33,7 +36,7 @@ func (window *window) handleConfigureNotify ( event xevent.ConfigureNotifyEvent, ) { if window.root == nil { return } - + configureEvent := *event.ConfigureNotifyEvent newWidth := int(configureEvent.Width) @@ -46,7 +49,7 @@ func (window *window) handleConfigureNotify ( 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) { // } diff --git a/system.go b/system.go index 1cb96ab..9d1aef6 100644 --- a/system.go +++ b/system.go @@ -2,7 +2,6 @@ package x import "image" 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 { } @@ -46,11 +45,17 @@ type anyBox interface { } func (window *window) SetRoot (root tomo.Object) { - box := root.Box().(anyBox) - window.root.setParent(nil) - box.setParent(window) - window.invalidateLayout(box) - window.root = box + if window.root != nil { + window.root.setParent(nil) + } + if root == nil { + window.root = nil + } else { + box := root.Box().(anyBox) + box.setParent(window) + window.invalidateLayout(box) + window.root = box + } } func (window *window) window () *window { @@ -78,11 +83,20 @@ func (window *window) focus (box anyBox) { } func (window *window) afterEvent () { + if window.xCanvas == nil { return } + if window.needRedo { + // set child bounds + childBounds := window.metrics.bounds + childBounds = childBounds.Sub(childBounds.Min) + window.root.SetBounds(childBounds) + + // full relayout/redraw if window.root != nil { window.root.recursiveRedo() } - window.xCanvas.Push(window.xWindow.Id) + window.pushAll() + window.needRedo = false return } @@ -95,5 +109,7 @@ func (window *window) afterEvent () { box.doDraw() toPush = toPush.Union(box.Bounds()) } - window.xCanvas.Clip(toPush).(xcanvas.Canvas).Push(window.xWindow.Id) + if !toPush.Empty() { + window.pushRegion(toPush) + } } diff --git a/window.go b/window.go index 0f59b2a..56fad2b 100644 --- a/window.go +++ b/window.go @@ -24,7 +24,7 @@ type window struct { backend *Backend xWindow *xwindow.Window xImage *xgraphics.Image - xCanvas xcanvas.Canvas + xCanvas *xcanvas.Canvas title string @@ -55,7 +55,7 @@ func (backend *Backend) NewWindow ( output tomo.MainWindow, err error, ) { - if backend == nil { panic("nil backend") } + backend.assert() window, err := backend.newWindow(bounds, false) output = mainWindow { window: window } @@ -104,10 +104,10 @@ func (backend *Backend) newWindow ( window.Close() }) - // xevent.ExposeFun(window.handleExpose). - // Connect(backend.x, window.xWindow.Id) - // xevent.ConfigureNotifyFun(window.handleConfigureNotify). - // Connect(backend.x, window.xWindow.Id) + xevent.ExposeFun(window.handleExpose). + Connect(backend.x, window.xWindow.Id) + xevent.ConfigureNotifyFun(window.handleConfigureNotify). + Connect(backend.x, window.xWindow.Id) // xevent.KeyPressFun(window.handleKeyPress). // Connect(backend.x, window.xWindow.Id) // xevent.KeyReleaseFun(window.handleKeyRelease). @@ -130,8 +130,6 @@ func (backend *Backend) newWindow ( window.metrics.bounds = bounds window.setMinimumSize(8, 8) - window.reallocateCanvas() - backend.windows[window.xWindow.Id] = window output = window @@ -243,14 +241,6 @@ func (window *window) Paste (callback func (data.Data, error), accept ...data.Mi } func (window *window) Show () { - // fill the screen with black if there is no active child - if window.root == nil { - window.xCanvas.For (func (x, y int) xgraphics.BGRA { - return xgraphics.BGRA { } - }) - window.xCanvas.Push(window.xWindow.Id) - } - window.xWindow.Map() if window.shy { window.grabInput() } } @@ -314,8 +304,11 @@ func (window *window) setClientLeader (leader *window) error { } func (window *window) reallocateCanvas () { - previousWidth := window.xCanvas.Bounds().Dx() - previousHeight := window.xCanvas.Bounds().Dy() + var previousWidth, previousHeight int + if window.xCanvas != nil { + previousWidth = window.xCanvas.Bounds().Dx() + previousHeight = window.xCanvas.Bounds().Dy() + } newWidth := window.metrics.bounds.Dx() newHeight := window.metrics.bounds.Dy() @@ -325,7 +318,7 @@ func (window *window) reallocateCanvas () { allocStep := 128 if larger || smaller { - if window.xCanvas.Image != nil { + if window.xCanvas != nil { window.xCanvas.Destroy() } window.xCanvas = xcanvas.NewFrom(xgraphics.New ( @@ -336,9 +329,28 @@ func (window *window) reallocateCanvas () { (newHeight / allocStep + 1) * allocStep))) window.xCanvas.CreatePixmap() } + window.needRedo = true } +func (window *window) pushAll () { + if window.xCanvas != nil { + window.xCanvas.Push(window.xWindow.Id) + } +} + +func (window *window) pushRegion (region image.Rectangle) { + if window.xCanvas == nil { + return + } + + subCanvas := window.xCanvas.Clip(region) + if subCanvas == nil { + return + } + subCanvas.(*xcanvas.Canvas).Push(window.xWindow.Id) +} + func (window *window) setMinimumSize (width, height int) { if width < 8 { width = 8 } if height < 8 { height = 8 }