25 Commits

Author SHA1 Message Date
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
52a0136e60 Fixed Window.NewChild returning the parent (oops) 2024-06-07 01:47:24 -04:00
5657f85c83 Update Window.SetIcon 2024-06-07 01:12:04 -04:00
a9ac9f6600 Change scrolling speed from 16 to 32 2024-06-07 01:07:48 -04:00
6eff6887e7 Unify window and mainWindow 2024-06-07 01:07:28 -04:00
adf0ef3a89 Update Tomo API 2024-06-07 01:07:15 -04:00
07bcd119d7 X backend uses Close method instead of Destroy to free canvas 2024-06-03 20:54:45 -04:00
c51ce65c88 System no longer requires a NewCanvas method 2024-06-03 20:44:58 -04:00
0d93d73c32 Backend returns CanvasCloser 2024-06-03 20:43:05 -04:00
006921d690 Store Role in Box 2024-06-03 20:42:54 -04:00
f2bcc217a4 Update Tomo API 2024-06-03 20:42:08 -04:00
a4e067cacb HandleKeyUp causes a... key up event. As it should. 2024-06-03 02:20:37 -04:00
5a2b4cc2f5 Boxes are able to check if they are focused 2024-06-03 02:18:30 -04:00
250c3076fb Fixed another segfault. Do not write code at 4 am. 2024-06-03 02:11:47 -04:00
13 changed files with 306 additions and 89 deletions

5
go.mod
View File

@@ -3,11 +3,8 @@ module git.tebibyte.media/tomo/backend
go 1.20 go 1.20
require ( require (
git.tebibyte.media/tomo/tomo v0.34.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
)
require (
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
github.com/jezek/xgbutil v0.0.0-20231116234834-47f30c120111 github.com/jezek/xgbutil v0.0.0-20231116234834-47f30c120111

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.34.0 h1:r5yJPks9rtzdDI2RyAUdqa1qb6BebG0QFe2cTmcFi+0= git.tebibyte.media/tomo/tomo v0.38.0 h1:K5TP67RxnszudeNfmGZiU5cFTRjFueXiI3NCsgw+05U=
git.tebibyte.media/tomo/tomo v0.34.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,6 +19,11 @@ type box struct {
parent parent parent parent
outer anyBox outer anyBox
role tomo.Role
styleCookie event.Cookie
lastStyleNonce int
lastIconsNonce int
bounds image.Rectangle bounds image.Rectangle
minSize image.Point minSize image.Point
userMinSize image.Point userMinSize image.Point
@@ -55,6 +60,8 @@ type box struct {
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
} }
} }
@@ -106,6 +113,10 @@ func (this *box) MinimumSize () image.Point {
return this.minSize return this.minSize
} }
func (this *box) Role () tomo.Role {
return this.role
}
func (this *box) borderSum () tomo.Inset { func (this *box) borderSum () tomo.Inset {
sum := tomo.Inset { } sum := tomo.Inset { }
for _, border := range this.border { for _, border := range this.border {
@@ -117,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
@@ -182,6 +202,10 @@ func (this *box) SetPadding (padding tomo.Inset) {
this.invalidateMinimum() this.invalidateMinimum()
} }
func (this *box) SetRole (role tomo.Role) {
this.role = role
}
func (this *box) SetDNDData (dat data.Data) { func (this *box) SetDNDData (dat data.Data) {
this.dndData = dat this.dndData = dat
} }
@@ -191,7 +215,7 @@ func (this *box) SetDNDAccept (types ...data.Mime) {
} }
func (this *box) SetFocused (focused bool) { func (this *box) SetFocused (focused bool) {
hierarchy := this.parent.getHierarchy() hierarchy := this.getHierarchy()
if hierarchy == nil { if hierarchy == nil {
focusedCopy := focused focusedCopy := focused
this.focusQueued = &focusedCopy this.focusQueued = &focusedCopy
@@ -217,7 +241,7 @@ func (this *box) SetFocusable (focusable bool) {
func (this *box) Focused () bool { func (this *box) Focused () bool {
hierarchy := this.getHierarchy() hierarchy := this.getHierarchy()
if hierarchy == nil { return false } if hierarchy == nil { return false }
return hierarchy.isFocused(this) return hierarchy.isFocused(this.outer)
} }
func (this *box) Modifiers () input.Modifiers { func (this *box) Modifiers () input.Modifiers {
@@ -272,6 +296,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()
} }
@@ -441,7 +471,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) {
@@ -449,6 +479,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 {
@@ -471,7 +502,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)
} }
@@ -496,6 +527,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
} }
@@ -529,7 +590,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

@@ -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

@@ -36,7 +36,7 @@ func (this *Hierarchy) HandleKeyDown (key input.Key, numberPad bool) {
// be called *before* HandleKeyUp. // be called *before* HandleKeyUp.
func (this *Hierarchy) HandleKeyUp (key input.Key, numberPad bool) { func (this *Hierarchy) HandleKeyUp (key input.Key, numberPad bool) {
if target := this.keyboardTarget(); target != nil { if target := this.keyboardTarget(); target != nil {
target.handleKeyDown(key, numberPad) target.handleKeyUp(key, numberPad)
} }
} }

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

@@ -57,7 +57,11 @@ type anyBox interface {
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

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
@@ -15,8 +23,6 @@ type System struct {
type BackendLink interface { type BackendLink interface {
// NewTexture creates a new texture from an image. // NewTexture creates a new texture from an image.
NewTexture (image.Image) canvas.TextureCloser NewTexture (image.Image) canvas.TextureCloser
// NewCanvas creates a new blank canvas with the specified bounds.
NewCanvas (image.Rectangle) canvas.Canvas
// NewSurface creates a new surface with the specified bounds. // NewSurface creates a new surface with the specified bounds.
NewSurface (image.Rectangle) (SurfaceLink, error) NewSurface (image.Rectangle) (SurfaceLink, error)
} }
@@ -33,5 +39,28 @@ type SurfaceLink interface {
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

@@ -31,10 +31,6 @@ func (this *backendLink) NewTexture (source image.Image) canvas.TextureCloser {
return this.backend.NewTexture(source) return this.backend.NewTexture(source)
} }
func (this *backendLink) NewCanvas (bounds image.Rectangle) canvas.Canvas {
return this.backend.NewCanvas(bounds)
}
func (this *backendLink) NewSurface (bounds image.Rectangle) (system.SurfaceLink, error) { func (this *backendLink) NewSurface (bounds image.Rectangle) (system.SurfaceLink, error) {
// TODO // TODO
return nil, errors.New("x: not implemented") return nil, errors.New("x: not implemented")
@@ -126,10 +122,18 @@ func (this *Backend) NewTexture (source image.Image) canvas.TextureCloser {
return xcanvas.NewTextureFrom(source) return xcanvas.NewTextureFrom(source)
} }
func (this *Backend) NewCanvas (bounds image.Rectangle) canvas.Canvas { 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

@@ -49,9 +49,10 @@ func (this *Canvas) Push (window xproto.Window) {
} }
// Close frees this canvas from the X server. // Close frees this canvas from the X server.
func (this *Canvas) Close () { func (this *Canvas) Close () error {
this.assert() this.assert()
this.Image.Destroy() this.Image.Destroy()
return nil
} }
func (this *Canvas) assert () { func (this *Canvas) assert () {

View File

@@ -13,7 +13,7 @@ type scrollSum struct {
} }
// TODO: this needs to be configurable, we need a config api // TODO: this needs to be configurable, we need a config api
const scrollDistance = 16 const scrollDistance = 32
func (sum *scrollSum) add (button xproto.Button, window *window, state uint16) { func (sum *scrollSum) add (button xproto.Button, window *window, state uint16) {
if xgbkb.StateToModifiers(state).Shift { if xgbkb.StateToModifiers(state).Shift {

View File

@@ -18,7 +18,6 @@ import "github.com/jezek/xgbutil/keybind"
import "github.com/jezek/xgbutil/mousebind" import "github.com/jezek/xgbutil/mousebind"
import "github.com/jezek/xgbutil/xgraphics" import "github.com/jezek/xgbutil/xgraphics"
type mainWindow struct { *window }
type window struct { type window struct {
backend *Backend backend *Backend
hierarchy *system.Hierarchy hierarchy *system.Hierarchy
@@ -28,10 +27,13 @@ type window struct {
title string title string
leader *window
modalParent *window modalParent *window
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
@@ -63,28 +65,24 @@ func (this *windowLink) NotifyMinimumSizeChange () {
func (this *Backend) NewWindow ( func (this *Backend) NewWindow (
bounds image.Rectangle, bounds image.Rectangle,
) ( ) (
output tomo.MainWindow, output tomo.Window,
err error, err error,
) { ) {
this.assert() this.assert()
window, err := this.newWindow(bounds, false) return this.newWindow(bounds, false)
output = mainWindow { window: window }
return output, err
} }
func (this *Backend) NewPlainWindow ( func (this *Backend) NewPlainWindow (
bounds image.Rectangle, bounds image.Rectangle,
) ( ) (
output tomo.MainWindow, output tomo.Window,
err error, err error,
) { ) {
this.assert() this.assert()
window, err := this.newWindow(bounds, false) window, err := this.newWindow(bounds, false)
window.setType("dock") window.setType("dock")
output = mainWindow { window: window } return window, err
return output, err
} }
func (this *Backend) newWindow ( func (this *Backend) newWindow (
@@ -100,6 +98,9 @@ func (this *Backend) newWindow (
window := &window { backend: this } window := &window { backend: this }
link := &windowLink { window: window } link := &windowLink { window: window }
window.hierarchy = this.system.NewHierarchy(link) window.hierarchy = this.system.NewHierarchy(link)
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 }
@@ -178,12 +179,16 @@ func (this *window) SetTitle (title string) {
icccm.WmIconNameSet (this.backend.x, this.xWindow.Id, title) icccm.WmIconNameSet (this.backend.x, this.xWindow.Id, title)
} }
func (this *window) SetIcon (sizes ...image.Image) { func (this *window) SetIcon (sizes ...canvas.Texture) {
wmIcons := []ewmh.WmIcon { } wmIcons := []ewmh.WmIcon { }
for _, icon := range sizes { for _, icon := range sizes {
width := icon.Bounds().Max.X icon, ok := icon.(*xcanvas.Texture)
height := icon.Bounds().Max.Y if !ok { continue }
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),
@@ -193,18 +198,14 @@ func (this *window) SetIcon (sizes ...image.Image) {
// 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 ++
}} }}
@@ -217,6 +218,20 @@ func (this *window) SetIcon (sizes ...image.Image) {
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)
@@ -247,19 +262,24 @@ func (this *window) NewModal (bounds image.Rectangle) (tomo.Window, error) {
return modal, err return modal, err
} }
func (this mainWindow) NewChild (bounds image.Rectangle) (tomo.Window, error) { func (this *window) NewChild (bounds image.Rectangle) (tomo.Window, error) {
leader := this.leader
child, err := this.backend.newWindow ( child, err := this.backend.newWindow (
bounds.Add(this.metrics.bounds.Min), false) bounds.Add(this.metrics.bounds.Min), false)
child.leader = leader
if err != nil { return nil, err } if err != nil { return nil, err }
child.setClientLeader(this.window)
this.setClientLeader(this.window) child.setClientLeader(leader)
leader.setClientLeader(leader)
icccm.WmTransientForSet ( icccm.WmTransientForSet (
this.backend.x, this.backend.x,
this.xWindow.Id, child.xWindow.Id,
this.xWindow.Id) leader.xWindow.Id)
this.setType("UTILITY") child.setType("UTILITY")
// this.inheritProperties(this.window) // child.inheritProperties(leader.window)
return this, err return child, err
} }
func (this *window) Widget () (tomo.Window, error) { func (this *window) Widget () (tomo.Window, error) {
@@ -306,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 {
@@ -361,7 +382,7 @@ func (this *window) reallocateCanvas () {
if larger || smaller { if larger || smaller {
if this.xCanvas != nil { if this.xCanvas != nil {
this.xCanvas.Destroy() this.xCanvas.Close()
} }
this.xCanvas = xcanvas.NewCanvasFrom(xgraphics.New ( this.xCanvas = xcanvas.NewCanvasFrom(xgraphics.New (
this.backend.x, this.backend.x,
@@ -404,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 }
icccm.WmNormalHintsSet (
this.backend.x, hints := icccm.NormalHints {
this.xWindow.Id,
&icccm.NormalHints {
Flags: icccm.SizeHintPMinSize, Flags: icccm.SizeHintPMinSize,
MinWidth: uint(size.X), MinWidth: uint(size.X),
MinHeight: uint(size.Y), 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 (
this.backend.x,
this.xWindow.Id,
&hints)
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 }