I forgor to commit all this stuff
This commit is contained in:
parent
5e7c666d92
commit
208c50a66b
49
box.go
49
box.go
@ -10,7 +10,7 @@ import "git.tebibyte.media/tomo/tomo/canvas"
|
||||
|
||||
type box struct {
|
||||
backend *Backend
|
||||
window *window
|
||||
parent parent
|
||||
|
||||
bounds image.Rectangle
|
||||
minSize image.Point
|
||||
@ -24,9 +24,7 @@ type box struct {
|
||||
focused bool
|
||||
focusable bool
|
||||
|
||||
drawClean bool
|
||||
layoutClean bool
|
||||
|
||||
canvas canvas.Canvas
|
||||
drawer canvas.Drawer
|
||||
|
||||
on struct {
|
||||
@ -47,11 +45,9 @@ type box struct {
|
||||
}
|
||||
|
||||
func (backend *Backend) NewBox() tomo.Box {
|
||||
box := &box {
|
||||
return &box {
|
||||
backend: backend,
|
||||
}
|
||||
box.drawer = box
|
||||
return box
|
||||
}
|
||||
|
||||
func (this *box) Box () tomo.Box {
|
||||
@ -59,7 +55,7 @@ func (this *box) Box () tomo.Box {
|
||||
}
|
||||
|
||||
func (this *box) Window () tomo.Window {
|
||||
return this.window
|
||||
return this.parent.window()
|
||||
}
|
||||
|
||||
func (this *box) Bounds () image.Rectangle {
|
||||
@ -114,9 +110,9 @@ func (this *box) SetDNDAccept (types ...data.Mime) {
|
||||
|
||||
func (this *box) SetFocused (focused bool) {
|
||||
if this.Focused () && !focused {
|
||||
this.window.focus(nil)
|
||||
this.parent.window().focus(nil)
|
||||
} 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 {
|
||||
return this == this.window.focused
|
||||
return this == this.parent.window().focused
|
||||
}
|
||||
|
||||
func (this *box) Modifiers () input.Modifiers {
|
||||
return this.window.modifiers
|
||||
return this.parent.window().modifiers
|
||||
}
|
||||
|
||||
func (this *box) MousePosition () image.Point {
|
||||
return this.window.mousePosition
|
||||
return this.parent.window().mousePosition
|
||||
}
|
||||
|
||||
// ----- event handlers ----------------------------------------------------- //
|
||||
@ -218,11 +214,32 @@ func (this *box) Draw (can canvas.Canvas) {
|
||||
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 () {
|
||||
this.drawClean = false
|
||||
this.parent.window().invalidateDraw(this)
|
||||
}
|
||||
|
||||
func (this *box) invalidateLayout () {
|
||||
this.invalidateDraw()
|
||||
this.layoutClean = false
|
||||
this.parent.window().invalidateLayout(this)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package xcanvas
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "github.com/jezek/xgbutil"
|
||||
import "github.com/jezek/xgb/xproto"
|
||||
import "git.tebibyte.media/tomo/ggfx"
|
||||
import "github.com/jezek/xgbutil/xgraphics"
|
||||
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.
|
||||
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) }
|
||||
}
|
||||
|
||||
// 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:
|
||||
// - cap
|
||||
// - joint
|
||||
|
135
event.go
Normal file
135
event.go
Normal 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
|
||||
}
|
95
system.go
95
system.go
@ -1,20 +1,99 @@
|
||||
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 { }
|
||||
|
||||
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 {
|
||||
tomo.Box
|
||||
invalidateDraw ()
|
||||
invalidateLayout ()
|
||||
doDraw ()
|
||||
doLayout ()
|
||||
setParent (parent)
|
||||
recursiveRedo ()
|
||||
}
|
||||
|
||||
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) {
|
||||
if window.focused == bx { return }
|
||||
window.focused.invalidateDraw()
|
||||
window.focused = bx
|
||||
bx.invalidateDraw()
|
||||
func (window *window) window () *window {
|
||||
return window
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
52
window.go
52
window.go
@ -4,6 +4,7 @@ import "image"
|
||||
// import "errors"
|
||||
|
||||
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/input"
|
||||
import "git.tebibyte.media/tomo/tomo/event"
|
||||
@ -22,7 +23,8 @@ type mainWindow struct { *window }
|
||||
type window struct {
|
||||
backend *Backend
|
||||
xWindow *xwindow.Window
|
||||
xCanvas *xgraphics.Image
|
||||
xImage *xgraphics.Image
|
||||
xCanvas xcanvas.Canvas
|
||||
|
||||
title string
|
||||
|
||||
@ -41,6 +43,10 @@ type window struct {
|
||||
|
||||
root anyBox
|
||||
focused anyBox
|
||||
|
||||
needDraw boxSet
|
||||
needLayout boxSet
|
||||
needRedo bool
|
||||
}
|
||||
|
||||
func (backend *Backend) NewWindow (
|
||||
@ -124,7 +130,7 @@ func (backend *Backend) newWindow (
|
||||
window.metrics.bounds = bounds
|
||||
window.setMinimumSize(8, 8)
|
||||
|
||||
// window.reallocateCanvas()
|
||||
window.reallocateCanvas()
|
||||
|
||||
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 () {
|
||||
// if window.child == nil {
|
||||
// window.xCanvas.For (func (x, y int) xgraphics.BGRA {
|
||||
// return xgraphics.BGRA { }
|
||||
// })
|
||||
//
|
||||
// window.pushRegion(window.xCanvas.Bounds())
|
||||
// }
|
||||
// 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() }
|
||||
@ -265,7 +271,7 @@ func (window *window) Close () {
|
||||
window.modalParent.hasModal = false
|
||||
}
|
||||
window.Hide()
|
||||
// window.Adopt(nil)
|
||||
window.SetRoot(nil)
|
||||
delete(window.backend.windows, window.xWindow.Id)
|
||||
window.xWindow.Destroy()
|
||||
}
|
||||
@ -307,6 +313,32 @@ func (window *window) setClientLeader (leader *window) error {
|
||||
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) {
|
||||
if width < 8 { width = 8 }
|
||||
if height < 8 { height = 8 }
|
||||
|
Reference in New Issue
Block a user