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 {
|
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
|
|
||||||
}
|
}
|
||||||
|
@ -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
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
|
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)
|
||||||
}
|
}
|
||||||
|
52
window.go
52
window.go
@ -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 }
|
||||||
|
Reference in New Issue
Block a user