18 Commits

Author SHA1 Message Date
39a591e732 Fix boundary detection for polygons 2024-06-24 19:11:21 -04:00
07865dc85f Polygon boundary detection now rounds properly 2024-06-24 19:04:08 -04:00
868b6fdfe9 Fix crash when CanvasBox draws with nil canvas 2024-06-24 18:43:54 -04:00
1c803ff9c1 Fix out of bounds panic with polygon filling 2024-06-24 18:42:43 -04:00
fdcf254891 Box re-applies theme on role change 2024-06-20 16:44:24 -04:00
e23e794730 CanvasBox no longer crashes on nil drawer 2024-06-19 12:13:31 -04:00
caa261665f Remove obsolete TODO 2024-06-15 23:35:44 -04:00
e21b57a915 X backend properly converts image data 2024-06-15 23:33:59 -04:00
727a801243 Attempt to fix strange issue with overflowing 2024-06-14 02:30:59 -04:00
76701d4383 Fix style application part 2 2024-06-12 02:12:24 -04:00
6619987b5a Fixed style application 2024-06-12 00:39:00 -04:00
02de78c997 Window is resizable by default (lol) 2024-06-12 00:29:07 -04:00
95b1d033a9 SetResizable has been implemented 2024-06-11 23:58:19 -04:00
1951b6e408 Partially implement new stuff for X 2024-06-11 23:45:49 -04:00
c7f09c7894 Add recommended sizes and all that jazz 2024-06-11 22:45:40 -04:00
80f60b42de I lied 2024-06-11 18:35:40 -04:00
995e6fd624 Add theme setting nonsense 2024-06-11 18:12:47 -04:00
26b69d3e21 Update Tomo API 2024-06-11 17:18:30 -04:00
12 changed files with 284 additions and 65 deletions

2
go.mod
View File

@@ -3,7 +3,7 @@ module git.tebibyte.media/tomo/backend
go 1.20 go 1.20
require ( require (
git.tebibyte.media/tomo/tomo v0.36.0 git.tebibyte.media/tomo/tomo v0.38.0
git.tebibyte.media/tomo/typeset v0.7.1 git.tebibyte.media/tomo/typeset v0.7.1
git.tebibyte.media/tomo/xgbkb v1.0.1 git.tebibyte.media/tomo/xgbkb v1.0.1
github.com/jezek/xgb v1.1.1 github.com/jezek/xgb v1.1.1

4
go.sum
View File

@@ -1,6 +1,6 @@
git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q= git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q=
git.tebibyte.media/tomo/tomo v0.36.0 h1:V9vyPYb4kpUceBhcDF/XyLDACzE5lY8kYEGHAkIsqs0= git.tebibyte.media/tomo/tomo v0.38.0 h1:K5TP67RxnszudeNfmGZiU5cFTRjFueXiI3NCsgw+05U=
git.tebibyte.media/tomo/tomo v0.36.0/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps= git.tebibyte.media/tomo/tomo v0.38.0/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps=
git.tebibyte.media/tomo/typeset v0.7.1 h1:aZrsHwCG5ZB4f5CruRFsxLv5ezJUCFUFsQJJso2sXQ8= git.tebibyte.media/tomo/typeset v0.7.1 h1:aZrsHwCG5ZB4f5CruRFsxLv5ezJUCFUFsQJJso2sXQ8=
git.tebibyte.media/tomo/typeset v0.7.1/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g= git.tebibyte.media/tomo/typeset v0.7.1/go.mod h1:PwDpSdBF3l/EzoIsa2ME7QffVVajnTHZN6l3MHEGe1g=
git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE= git.tebibyte.media/tomo/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE=

View File

@@ -19,7 +19,10 @@ type box struct {
parent parent parent parent
outer anyBox outer anyBox
role tomo.Role role tomo.Role
styleCookie event.Cookie
lastStyleNonce int
lastIconsNonce int
bounds image.Rectangle bounds image.Rectangle
minSize image.Point minSize image.Point
@@ -44,19 +47,21 @@ type box struct {
drawer canvas.Drawer drawer canvas.Drawer
on struct { on struct {
focusEnter event.FuncBroadcaster focusEnter event.FuncBroadcaster
focusLeave event.FuncBroadcaster focusLeave event.FuncBroadcaster
dndEnter event.FuncBroadcaster dndEnter event.FuncBroadcaster
dndLeave event.FuncBroadcaster dndLeave event.FuncBroadcaster
dndDrop event.Broadcaster[func (data.Data)] dndDrop event.Broadcaster[func (data.Data)]
mouseEnter event.FuncBroadcaster mouseEnter event.FuncBroadcaster
mouseLeave event.FuncBroadcaster mouseLeave event.FuncBroadcaster
mouseMove event.FuncBroadcaster mouseMove event.FuncBroadcaster
mouseDown event.Broadcaster[func (input.Button)] mouseDown event.Broadcaster[func (input.Button)]
mouseUp event.Broadcaster[func (input.Button)] mouseUp event.Broadcaster[func (input.Button)]
scroll event.Broadcaster[func (float64, float64)] scroll event.Broadcaster[func (float64, float64)]
keyDown event.Broadcaster[func (input.Key, bool)] keyDown event.Broadcaster[func (input.Key, bool)]
keyUp event.Broadcaster[func (input.Key, bool)] keyUp event.Broadcaster[func (input.Key, bool)]
styleChange event.FuncBroadcaster
iconsChange event.FuncBroadcaster
} }
} }
@@ -123,6 +128,15 @@ func (this *box) borderSum () tomo.Inset {
return sum return sum
} }
func (this *box) borderAndPaddingSum () tomo.Inset {
sum := this.borderSum()
sum[0] += this.padding[0]
sum[1] += this.padding[1]
sum[2] += this.padding[2]
sum[3] += this.padding[3]
return sum
}
func (this *box) SetBounds (bounds image.Rectangle) { func (this *box) SetBounds (bounds image.Rectangle) {
if this.bounds == bounds { return } if this.bounds == bounds { return }
this.bounds = bounds this.bounds = bounds
@@ -189,7 +203,10 @@ func (this *box) SetPadding (padding tomo.Inset) {
} }
func (this *box) SetRole (role tomo.Role) { func (this *box) SetRole (role tomo.Role) {
if this.role == role { return }
this.role = role this.role = role
this.lastStyleNonce = -1
this.outer.recursiveReApply()
} }
func (this *box) SetDNDData (dat data.Data) { func (this *box) SetDNDData (dat data.Data) {
@@ -282,6 +299,12 @@ func (this *box) OnKeyDown (callback func(key input.Key, numberPad bool)) event.
func (this *box) OnKeyUp (callback func(key input.Key, numberPad bool)) event.Cookie { func (this *box) OnKeyUp (callback func(key input.Key, numberPad bool)) event.Cookie {
return this.on.keyUp.Connect(callback) return this.on.keyUp.Connect(callback)
} }
func (this *box) OnStyleChange (callback func()) event.Cookie {
return this.on.styleChange.Connect(callback)
}
func (this *box) OnIconsChange (callback func()) event.Cookie {
return this.on.iconsChange.Connect(callback)
}
func (this *box) handleFocusEnter () { func (this *box) handleFocusEnter () {
this.on.focusEnter.Broadcast() this.on.focusEnter.Broadcast()
} }
@@ -451,7 +474,7 @@ func (this *box) doLayout () {
// laycnt ++ // laycnt ++
this.innerClippingBounds = this.borderSum().Apply(this.bounds) this.innerClippingBounds = this.borderSum().Apply(this.bounds)
this.loseCanvas() this.outer.recursiveLoseCanvas()
} }
func (this *box) setParent (parent parent) { func (this *box) setParent (parent parent) {
@@ -459,6 +482,7 @@ func (this *box) setParent (parent parent) {
this.SetFocused(false) this.SetFocused(false)
} }
this.parent = parent this.parent = parent
this.outer.recursiveReApply()
} }
func (this *box) getParent () parent { func (this *box) getParent () parent {
@@ -481,7 +505,7 @@ func (this *box) recursiveRedo () {
this.doDraw() this.doDraw()
} }
func (this *box) loseCanvas () { func (this *box) recursiveLoseCanvas () {
this.canvas.InvalidateTo(nil) this.canvas.InvalidateTo(nil)
} }
@@ -506,6 +530,36 @@ func (this *box) invalidateMinimum () {
} }
} }
func (this *box) recursiveReApply () {
if this.getHierarchy() == nil { return }
// re-apply styling, icons *if needed*
// style
hierarchyStyleNonce := this.getStyleNonce()
if this.lastStyleNonce != hierarchyStyleNonce {
// remove old style
this.lastStyleNonce = hierarchyStyleNonce
if this.styleCookie != nil {
this.styleCookie.Close()
this.styleCookie = nil
}
// apply new one
if style := this.getStyle(); style != nil {
this.styleCookie = style.Apply(this.outer)
}
this.on.styleChange.Broadcast()
}
// icons
hierarchyIconsNonce := this.getIconsNonce()
if this.lastIconsNonce != hierarchyIconsNonce {
this.lastIconsNonce = hierarchyIconsNonce
this.on.iconsChange.Broadcast()
}
}
func (this *box) canBeFocused () bool { func (this *box) canBeFocused () bool {
return this.focusable return this.focusable
} }
@@ -539,7 +593,23 @@ func (this *box) getWindow () tomo.Window {
return hierarchy.getWindow() return hierarchy.getWindow()
} }
func (this *box) getStyle () tomo.Style {
hierarchy := this.getHierarchy()
if hierarchy == nil { return nil }
return hierarchy.getStyle()
}
func (this *box) getHierarchy () *Hierarchy { func (this *box) getHierarchy () *Hierarchy {
if this.parent == nil { return nil } if this.parent == nil { return nil }
return this.parent.getHierarchy() return this.parent.getHierarchy()
} }
func (this *box) getStyleNonce () int {
// should panic if not in the tree
return this.getHierarchy().getStyleNonce()
}
func (this *box) getIconsNonce () int {
// should panic if not in the tree
return this.getHierarchy().getIconsNonce()
}

View File

@@ -29,7 +29,10 @@ func (this *canvasBox) Invalidate () {
} }
func (this *canvasBox) Draw (can canvas.Canvas) { func (this *canvasBox) Draw (can canvas.Canvas) {
if can == nil { return }
this.box.Draw(can) this.box.Draw(can)
this.userDrawer.Draw ( if this.userDrawer != nil {
can.SubCanvas(this.padding.Apply(this.innerClippingBounds))) this.userDrawer.Draw (
can.SubCanvas(this.padding.Apply(this.innerClippingBounds)))
}
} }

View File

@@ -73,6 +73,24 @@ func (this *containerBox) ScrollTo (point image.Point) {
this.invalidateLayout() this.invalidateLayout()
} }
func (this *containerBox) RecommendedHeight (width int) int {
if this.layout == nil || this.vOverflow {
return this.MinimumSize().Y
} else {
return this.layout.RecommendedHeight(this.layoutHints(), this.children, width) +
this.borderAndPaddingSum().Vertical()
}
}
func (this *containerBox) RecommendedWidth (height int) int {
if this.layout == nil || this.hOverflow {
return this.MinimumSize().X
} else {
return this.layout.RecommendedWidth(this.layoutHints(), this.children, height) +
this.borderAndPaddingSum().Horizontal()
}
}
func (this *containerBox) OnContentBoundsChange (callback func()) event.Cookie { func (this *containerBox) OnContentBoundsChange (callback func()) event.Cookie {
return this.on.contentBoundsChange.Connect(callback) return this.on.contentBoundsChange.Connect(callback)
} }
@@ -335,6 +353,20 @@ func (this *containerBox) recursiveRedo () {
} }
} }
func (this *containerBox) recursiveLoseCanvas () {
this.box.recursiveLoseCanvas()
for _, child := range this.children {
child.(anyBox).recursiveLoseCanvas()
}
}
func (this *containerBox) recursiveReApply () {
this.box.recursiveReApply()
for _, child := range this.children {
child.(anyBox).recursiveReApply()
}
}
func (this *containerBox) boxUnder (point image.Point, category eventCategory) anyBox { func (this *containerBox) boxUnder (point image.Point, category eventCategory) anyBox {
if !point.In(this.bounds) { return nil } if !point.In(this.bounds) { return nil }

View File

@@ -56,6 +56,7 @@ func (this *System) NewHierarchy (link WindowLink) *Hierarchy {
needLayout: make(util.Set[anyBox]), needLayout: make(util.Set[anyBox]),
needDraw: make(util.Set[anyBox]), needDraw: make(util.Set[anyBox]),
} }
this.hierarchies.Add(hierarchy)
return hierarchy return hierarchy
} }
@@ -85,7 +86,7 @@ func (this *Hierarchy) Empty () bool {
// draw to. The Hierarchy will use the canvas.Canvas's bounds to lay itself out. // draw to. The Hierarchy will use the canvas.Canvas's bounds to lay itself out.
func (this *Hierarchy) SetCanvas (can canvas.Canvas) { func (this *Hierarchy) SetCanvas (can canvas.Canvas) {
this.canvas = can this.canvas = can
if this.root != nil { this.root.loseCanvas() } if this.root != nil { this.root.recursiveLoseCanvas() }
this.needRedo = true this.needRedo = true
} }
@@ -135,6 +136,20 @@ func (this *Hierarchy) AfterEvent () {
} }
} }
// Close closes the Hierarchy. This should be called when the Window that
// contains it has been closed.
func (this *Hierarchy) Close () {
this.system.removeHierarchy(this)
}
func (this *Hierarchy) setStyle () {
if this.root != nil { this.root.recursiveReApply() }
}
func (this *Hierarchy) setIcons () {
if this.root != nil { this.root.recursiveReApply() }
}
func (this *Hierarchy) getHierarchy () *Hierarchy { func (this *Hierarchy) getHierarchy () *Hierarchy {
return this return this
} }
@@ -143,6 +158,18 @@ func (this *Hierarchy) getWindow () tomo.Window {
return this.link.GetWindow() return this.link.GetWindow()
} }
func (this *Hierarchy) getStyle () tomo.Style {
return this.system.style
}
func (this *Hierarchy) getStyleNonce () int {
return this.system.styleNonce
}
func (this *Hierarchy) getIconsNonce () int {
return this.system.iconsNonce
}
func (this *Hierarchy) getCanvas () canvas.Canvas { func (this *Hierarchy) getCanvas () canvas.Canvas {
return this.canvas return this.canvas
} }

View File

@@ -51,13 +51,17 @@ type anyBox interface {
// flushActionQueue performs any queued actions, like invalidating the // flushActionQueue performs any queued actions, like invalidating the
// minimum size or grabbing input focus. // minimum size or grabbing input focus.
flushActionQueue () flushActionQueue ()
// recursiveRedo recursively recalculates the minimum size, layout, and // recursiveRedo recursively recalculates the minimum size, layout, and
// re-paints this anyBox and all of its children. // re-paints this anyBox and all of its children.
recursiveRedo () recursiveRedo ()
// loseCanvas causes this anyBox and its children (if applicable) to // loseCanvas causes this anyBox and its children (if applicable) to
// lose their canvases and re-cut them as needed. // lose their canvases and re-cut them as needed.
loseCanvas () recursiveLoseCanvas ()
// recursiveReAppply causes this anyBox and its children (if applicable)
// to check whether they have an outdated style or icon set, and if so,
// update it and trigger the appropriate event broadcasters.
recursiveReApply ()
// contentMinimum returns the minimum dimensions of this box's content // contentMinimum returns the minimum dimensions of this box's content
contentMinimum () image.Point contentMinimum () image.Point
@@ -81,19 +85,19 @@ type anyBox interface {
propagate (func (anyBox) bool) bool propagate (func (anyBox) bool) bool
propagateAlt (func (anyBox) bool) bool propagateAlt (func (anyBox) bool) bool
handleFocusEnter () handleFocusEnter ()
handleFocusLeave () handleFocusLeave ()
// handleDndEnter () // handleDndEnter ()
// handleDndLeave () // handleDndLeave ()
// handleDndDrop (data.Data) // handleDndDrop (data.Data)
handleMouseEnter () handleMouseEnter ()
handleMouseLeave () handleMouseLeave ()
handleMouseMove () handleMouseMove ()
handleMouseDown (input.Button) handleMouseDown (input.Button)
handleMouseUp (input.Button) handleMouseUp (input.Button)
handleScroll (float64, float64) handleScroll (float64, float64)
handleKeyDown (input.Key, bool) handleKeyDown (input.Key, bool)
handleKeyUp (input.Key, bool) handleKeyUp (input.Key, bool)
} }
func assertAnyBox (unknown tomo.Box) anyBox { func assertAnyBox (unknown tomo.Box) anyBox {

View File

@@ -2,12 +2,20 @@ package system
import "io" import "io"
import "image" import "image"
import "git.tebibyte.media/tomo/tomo"
import "git.tebibyte.media/tomo/tomo/canvas" import "git.tebibyte.media/tomo/tomo/canvas"
import "git.tebibyte.media/tomo/backend/internal/util"
// System is coupled to a tomo.Backend implementation, and manages Hierarchies // System is coupled to a tomo.Backend implementation, and manages Hierarchies
// and Boxes. // and Boxes.
type System struct { type System struct {
link BackendLink link BackendLink
style tomo.Style
styleNonce int
iconsNonce int
hierarchies util.Set[*Hierarchy]
} }
// BackendLink allows the System to call up into the tomo.Backend implementation // BackendLink allows the System to call up into the tomo.Backend implementation
@@ -30,6 +38,29 @@ type SurfaceLink interface {
// New creates a new System. // New creates a new System.
func New (link BackendLink) *System { func New (link BackendLink) *System {
return &System { return &System {
link: link, link: link,
hierarchies: make(util.Set[*Hierarchy]),
} }
} }
// SetStyle sets the tomo.Style that is applied to objects, and notifies them
// that the style has changed.
func (this *System) SetStyle (style tomo.Style) {
this.style = style
this.styleNonce ++
for hierarchy := range this.hierarchies {
hierarchy.setStyle()
}
}
// SetIcons notifies objects that the icons have changed.
func (this *System) SetIcons (icons tomo.Icons) {
this.iconsNonce ++
for hierarchy := range this.hierarchies {
hierarchy.setIcons()
}
}
func (this *System) removeHierarchy (hierarchy *Hierarchy) {
delete(this.hierarchies, hierarchy)
}

View File

@@ -65,6 +65,15 @@ func (this *textBox) ScrollTo (point image.Point) {
this.invalidateLayout() this.invalidateLayout()
} }
func (this *textBox) RecommendedHeight (width int) int {
return this.drawer.ReccomendedHeightFor(width) + this.borderAndPaddingSum().Vertical()
}
func (this *textBox) RecommendedWidth (height int) int {
// TODO maybe not the best idea?
return this.MinimumSize().X
}
func (this *textBox) OnContentBoundsChange (callback func()) event.Cookie { func (this *textBox) OnContentBoundsChange (callback func()) event.Cookie {
return this.on.contentBoundsChange.Connect(callback) return this.on.contentBoundsChange.Connect(callback)
} }

View File

@@ -126,6 +126,14 @@ func (this *Backend) NewCanvas (bounds image.Rectangle) canvas.CanvasCloser {
return xcanvas.NewCanvas(this.x, bounds) return xcanvas.NewCanvas(this.x, bounds)
} }
func (this *Backend) SetStyle (style tomo.Style) {
this.system.SetStyle(style)
}
func (this *Backend) SetIcons (icons tomo.Icons) {
this.system.SetIcons(icons)
}
func (this *Backend) assert () { func (this *Backend) assert () {
if this == nil { panic("x: nil backend") } if this == nil { panic("x: nil backend") }
} }

View File

@@ -1,6 +1,7 @@
package xcanvas package xcanvas
import "sort" import "sort"
import "math"
import "image" import "image"
import "github.com/jezek/xgbutil/xgraphics" import "github.com/jezek/xgbutil/xgraphics"
@@ -158,12 +159,12 @@ func (this *pen) fillPolygon (c xgraphics.BGRA, points ...image.Point) {
area = this.image.Bounds().Intersect(area) area = this.image.Bounds().Intersect(area)
if area.Empty() { return } if area.Empty() { return }
boundaries := make([]int, len(points))
context := fillingContext { context := fillingContext {
image: this.image, image: this.image,
color: this.fill, color: this.fill,
min: area.Min.X, min: area.Min.X,
max: area.Max.X, max: area.Max.X,
boundaries: make([]int, len(points)),
points: points, points: points,
} }
@@ -181,19 +182,19 @@ func (this *pen) fillPolygon (c xgraphics.BGRA, points ...image.Point) {
(fPointY < fy && fPrevY >= fy) || (fPointY < fy && fPrevY >= fy) ||
(fPrevY < fy && fPointY >= fy) (fPrevY < fy && fPointY >= fy)
if addboundary { if addboundary {
context.boundaries[boundaryCount] = int ( boundaries[boundaryCount] = int(math.Round (
fPointX + fPointX +
(fy - fPointY) / (fy - fPointY) /
(fPrevY - fPointY) * (fPrevY - fPointY) *
(fPrevX - fPointX)) (fPrevX - fPointX)))
boundaryCount ++ boundaryCount ++
} }
prevPoint = point prevPoint = point
} }
// sort boundary list // sort boundary list
cutBoundaries := context.boundaries[:boundaryCount] context.boundaries = boundaries[:boundaryCount]
sort.Ints(cutBoundaries) sort.Ints(context.boundaries)
// fill pixels between boundary pairs // fill pixels between boundary pairs
if c.A == 255 { if c.A == 255 {
@@ -215,7 +216,7 @@ type fillingContext struct {
} }
func (context *fillingContext) fillPolygonHotOpaque () { func (context *fillingContext) fillPolygonHotOpaque () {
for index := 0; index < len(context.boundaries); index += 2 { for index := 0; index < len(context.boundaries) - 1; index += 2 {
left := context.boundaries[index] left := context.boundaries[index]
right := context.boundaries[index + 1] right := context.boundaries[index + 1]
@@ -240,7 +241,7 @@ func (context *fillingContext) fillPolygonHotOpaque () {
} }
func (context *fillingContext) fillPolygonHotTransparent () { func (context *fillingContext) fillPolygonHotTransparent () {
for index := 0; index < len(context.boundaries); index += 2 { for index := 0; index < len(context.boundaries) - 1; index += 2 {
left := context.boundaries[index] left := context.boundaries[index]
right := context.boundaries[index + 1] right := context.boundaries[index + 1]

View File

@@ -32,6 +32,8 @@ type window struct {
hasModal bool hasModal bool
shy bool shy bool
visible bool visible bool
resizeX bool
resizeY bool
metrics struct { metrics struct {
bounds image.Rectangle bounds image.Rectangle
@@ -97,6 +99,8 @@ func (this *Backend) newWindow (
link := &windowLink { window: window } link := &windowLink { window: window }
window.hierarchy = this.system.NewHierarchy(link) window.hierarchy = this.system.NewHierarchy(link)
window.leader = window window.leader = window
window.resizeX = true
window.resizeY = true
window.xWindow, err = xwindow.Generate(this.x) window.xWindow, err = xwindow.Generate(this.x)
if err != nil { return } if err != nil { return }
@@ -179,9 +183,12 @@ func (this *window) SetIcon (sizes ...canvas.Texture) {
wmIcons := []ewmh.WmIcon { } wmIcons := []ewmh.WmIcon { }
for _, icon := range sizes { for _, icon := range sizes {
// TODO we use textures now. make this better icon, ok := icon.(*xcanvas.Texture)
width := icon.Bounds().Max.X if !ok { continue }
height := icon.Bounds().Max.Y
bounds := icon.Bounds()
width := bounds.Dx()
height := bounds.Dy()
wmIcon := ewmh.WmIcon { wmIcon := ewmh.WmIcon {
Width: uint(width), Width: uint(width),
Height: uint(height), Height: uint(height),
@@ -191,18 +198,14 @@ func (this *window) SetIcon (sizes ...canvas.Texture) {
// manually convert image data beacuse of course we have to do // manually convert image data beacuse of course we have to do
// this // this
index := 0 index := 0
for y := 0; y < height; y ++ { for y := bounds.Min.Y; y < bounds.Max.Y; y ++ {
for x := 0; x < width; x ++ { for x := bounds.Min.X; x < bounds.Max.X; x ++ {
r, g, b, a := icon.At(x, y).RGBA() pixel := icon.BGRAAt(x, y)
r >>= 8
g >>= 8
b >>= 8
a >>= 8
wmIcon.Data[index] = wmIcon.Data[index] =
(uint(a) << 24) | (uint(pixel.A) << 24) |
(uint(r) << 16) | (uint(pixel.R) << 16) |
(uint(g) << 8) | (uint(pixel.G) << 8) |
(uint(b) << 0) (uint(pixel.B) << 0)
index ++ index ++
}} }}
@@ -215,6 +218,20 @@ func (this *window) SetIcon (sizes ...canvas.Texture) {
wmIcons) wmIcons)
} }
func (this *window) SetResizable (x, y bool) {
if this.resizeX == x && this.resizeY == y { return }
this.resizeX = x
this.resizeY = y
this.doMinimumSize()
}
func (this *window) SetBounds (bounds image.Rectangle) {
this.xWindow.WMMoveResize (
bounds.Min.X, bounds.Min.Y,
bounds.Min.X + bounds.Dx(),
bounds.Min.Y + bounds.Dy())
}
func (this *window) NewMenu (bounds image.Rectangle) (tomo.Window, error) { func (this *window) NewMenu (bounds image.Rectangle) (tomo.Window, error) {
menu, err := this.backend.newWindow ( menu, err := this.backend.newWindow (
bounds.Add(this.metrics.bounds.Min), true) bounds.Add(this.metrics.bounds.Min), true)
@@ -309,6 +326,7 @@ func (this *window) Close () {
this.SetRoot(nil) this.SetRoot(nil)
delete(this.backend.windows, this.xWindow.Id) delete(this.backend.windows, this.xWindow.Id)
this.xWindow.Destroy() this.xWindow.Destroy()
this.hierarchy.Close()
} }
func (this *window) OnClose (callback func ()) event.Cookie { func (this *window) OnClose (callback func ()) event.Cookie {
@@ -407,14 +425,30 @@ func (this *window) doMinimumSize () {
if size.X < 8 { size.X = 8 } if size.X < 8 { size.X = 8 }
if size.Y < 8 { size.Y = 8 } if size.Y < 8 { size.Y = 8 }
hints := icccm.NormalHints {
Flags: icccm.SizeHintPMinSize,
MinWidth: uint(size.X),
MinHeight: uint(size.Y),
// now you can tell your friends that the max size of a Tomo
// window under X when one of the dimensions is constrained is
// 99999999999
MaxWidth: uint(99999999999),
MaxHeight: uint(99999999999),
}
if !this.resizeX {
hints.Flags |= icccm.SizeHintPMaxSize
hints.MaxWidth = uint(size.X)
}
if !this.resizeY {
hints.Flags |= icccm.SizeHintPMaxSize
hints.MaxHeight = uint(size.Y)
}
icccm.WmNormalHintsSet ( icccm.WmNormalHintsSet (
this.backend.x, this.backend.x,
this.xWindow.Id, this.xWindow.Id,
&icccm.NormalHints { &hints)
Flags: icccm.SizeHintPMinSize,
MinWidth: uint(size.X),
MinHeight: uint(size.Y),
})
newWidth := this.metrics.bounds.Dx() newWidth := this.metrics.bounds.Dx()
newHeight := this.metrics.bounds.Dy() newHeight := this.metrics.bounds.Dy()
if newWidth < size.X { newWidth = size.X } if newWidth < size.X { newWidth = size.X }