Compare commits
	
		
			26 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e38cbe7205 | |||
| 09cec81f30 | |||
| 0799f3645b | |||
| 9214f70b61 | |||
| 36ac66b644 | |||
| c13db1217d | |||
| c0124bf232 | |||
| 9dc929f545 | |||
| 0f9c27c19a | |||
| 74fd3fdd55 | |||
| 398ad08867 | |||
| c92377f50b | |||
| 8b44526c94 | |||
| a5830c9823 | |||
| dd201f1b5f | |||
| 8f47da654c | |||
| bb4080bd73 | |||
| c0c4bdb266 | |||
| b8ce9d15f7 | |||
| 05f3ebc9e5 | |||
| 996d747d45 | |||
| cd72ae5bdd | |||
| a64c27953a | |||
| 8f555f82ee | |||
| 79f81688bb | |||
| b926881233 | 
| @ -3,12 +3,3 @@ | ||||
| [](https://pkg.go.dev/git.tebibyte.media/tomo/x) | ||||
| 
 | ||||
| An X11 backend for Tomo. | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| ``` | ||||
| cd x/x | ||||
| go build -buildmode=plugin . | ||||
| mkdir -p ~/.local/lib/tomo/plugins | ||||
| mv x.so ~/.local/lib/tomo/plugins | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										110
									
								
								box.go
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								box.go
									
									
									
									
									
								
							| @ -9,23 +9,29 @@ import "git.tebibyte.media/tomo/tomo/input" | ||||
| import "git.tebibyte.media/tomo/tomo/event" | ||||
| import "git.tebibyte.media/tomo/tomo/canvas" | ||||
| 
 | ||||
| type textureMode int; const ( | ||||
| 	textureModeTile textureMode = iota | ||||
| 	textureModeCenter | ||||
| ) | ||||
| 
 | ||||
| type box struct { | ||||
| 	backend *Backend | ||||
| 	parent   parent | ||||
| 	outer    anyBox | ||||
| 
 | ||||
| 	bounds      image.Rectangle | ||||
| 	minSize     image.Point | ||||
| 	userMinSize image.Point | ||||
| 	bounds              image.Rectangle | ||||
| 	minSize             image.Point | ||||
| 	userMinSize         image.Point | ||||
| 	innerClippingBounds image.Rectangle | ||||
| 	 | ||||
| 	minSizeQueued bool | ||||
| 	focusQueued   *bool | ||||
| 
 | ||||
| 	padding tomo.Inset | ||||
| 	border  []tomo.Border | ||||
| 	color   color.Color | ||||
| 	texture *xcanvas.Texture | ||||
| 	padding     tomo.Inset | ||||
| 	border      []tomo.Border | ||||
| 	color       color.Color | ||||
| 	texture     *xcanvas.Texture | ||||
| 	textureMode textureMode | ||||
| 
 | ||||
| 	dndData   data.Data | ||||
| 	dndAccept []data.Mime | ||||
| @ -116,15 +122,43 @@ func (this *box) SetColor (c color.Color) { | ||||
| 	this.invalidateDraw() | ||||
| } | ||||
| 
 | ||||
| func (this *box) SetTexture (texture canvas.Texture) { | ||||
| func (this *box) SetTextureTile (texture canvas.Texture) { | ||||
| 	if this.texture == texture && this.textureMode == textureModeTile { return } | ||||
| 	this.textureMode = textureModeTile | ||||
| 	this.texture = xcanvas.AssertTexture(texture) | ||||
| 	this.invalidateDraw() | ||||
| } | ||||
| 
 | ||||
| func (this *box) SetBorder (border ...tomo.Border) { | ||||
| 	this.border = border | ||||
| 	this.invalidateLayout() | ||||
| 	this.invalidateMinimum() | ||||
| func (this *box) SetTextureCenter (texture canvas.Texture) { | ||||
| 	if this.texture == texture && this.textureMode == textureModeCenter { return } | ||||
| 	this.texture = xcanvas.AssertTexture(texture) | ||||
| 	this.textureMode = textureModeCenter | ||||
| 	this.invalidateDraw() | ||||
| } | ||||
| 
 | ||||
| func (this *box) SetBorder (borders ...tomo.Border) { | ||||
| 	previousBorderSum := this.borderSum() | ||||
| 	previousBorders   := this.border | ||||
| 	this.border = borders | ||||
| 
 | ||||
| 	// only invalidate the layout if the border is sized differently | ||||
| 	if this.borderSum() != previousBorderSum { | ||||
| 		this.invalidateLayout() | ||||
| 		this.invalidateMinimum() | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	// if the border takes up the same amount of space, only invalidate the | ||||
| 	// drawing if it looks different | ||||
| 	for index, newBorder := range this.border { | ||||
| 		different := | ||||
| 			index >= len(previousBorders) || | ||||
| 			newBorder != previousBorders[index] | ||||
| 		if different { | ||||
| 			this.invalidateDraw() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (this *box) SetMinimumSize (size image.Point) { | ||||
| @ -279,13 +313,34 @@ func (this *box) handleKeyUp (key input.Key, numberPad bool) { | ||||
| func (this *box) Draw (can canvas.Canvas) { | ||||
| 	if can == nil { return } | ||||
| 	pen := can.Pen() | ||||
| 	bounds := this.Bounds() | ||||
| 
 | ||||
| 	// background | ||||
| 	pen.Fill(this.color) | ||||
| 	pen.Texture(this.texture) | ||||
| 	 | ||||
| 	if this.textureMode == textureModeTile { | ||||
| 		pen.Texture(this.texture) | ||||
| 	} | ||||
| 	if this.transparent() && this.parent != nil { | ||||
| 		this.parent.drawBackgroundPart(can) | ||||
| 	} | ||||
| 	pen.Rectangle(can.Bounds()) | ||||
| 	pen.Rectangle(bounds) | ||||
| 
 | ||||
| 	// centered texture | ||||
| 	if this.textureMode == textureModeCenter && this.texture != nil { | ||||
| 		textureBounds := this.texture.Bounds() | ||||
| 		textureOrigin := | ||||
| 			bounds.Min. | ||||
| 			Add(image.Pt ( | ||||
| 				bounds.Dx() / 2, | ||||
| 				bounds.Dy() / 2)). | ||||
| 			Sub(image.Pt ( | ||||
| 				textureBounds.Dx() / 2, | ||||
| 				textureBounds.Dy() / 2)) | ||||
| 	 | ||||
| 		pen.Fill(color.Transparent) | ||||
| 		pen.Texture(this.texture) | ||||
| 		pen.Rectangle(textureBounds.Sub(textureBounds.Min).Add(textureOrigin)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (this *box) drawBorders (can canvas.Canvas) { | ||||
| @ -296,7 +351,7 @@ func (this *box) drawBorders (can canvas.Canvas) { | ||||
| 	rectangle := func (x0, y0, x1, y1 int, c color.Color) { | ||||
| 		area := image.Rect(x0, y0, x1, y1) | ||||
| 		if transparent(c) && this.parent != nil { | ||||
| 			this.parent.drawBackgroundPart(can.Clip(area)) | ||||
| 			this.parent.drawBackgroundPart(can.SubCanvas(area)) | ||||
| 		} | ||||
| 		pen.Fill(c) | ||||
| 		pen.Rectangle(area) | ||||
| @ -356,20 +411,28 @@ func (this *box) doMinimumSize () { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // var drawcnt int | ||||
| func (this *box) doDraw () { | ||||
| 	// println("DRAW", drawcnt) | ||||
| 	// drawcnt ++ | ||||
| 	 | ||||
| 	if this.canvas == nil { return } | ||||
| 	if this.drawer != nil { | ||||
| 		this.drawBorders(this.canvas) | ||||
| 		this.drawer.Draw(this.canvas.Clip(this.innerClippingBounds)) | ||||
| 		this.drawer.Draw(this.canvas.SubCanvas(this.innerClippingBounds)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // var laycnt int | ||||
| func (this *box) doLayout () { | ||||
| 	// println("LAYOUT", laycnt) | ||||
| 	// laycnt ++ | ||||
| 	 | ||||
| 	this.innerClippingBounds = this.borderSum().Apply(this.bounds) | ||||
| 	if this.parent == nil { this.canvas = nil; return  } | ||||
| 	parentCanvas := this.parent.canvas() | ||||
| 	if parentCanvas == nil { this.canvas = nil; return } | ||||
| 	this.canvas = parentCanvas.Clip(this.bounds) | ||||
| 	this.canvas = parentCanvas.SubCanvas(this.bounds) | ||||
| } | ||||
| 
 | ||||
| func (this *box) setParent (parent parent) { | ||||
| @ -401,12 +464,12 @@ func (this *box) recursiveRedo () { | ||||
| 
 | ||||
| func (this *box) invalidateLayout () { | ||||
| 	if this.parent == nil || this.parent.window() == nil { return } | ||||
| 	this.parent.window().invalidateLayout(this.outer) | ||||
| 	this.window().invalidateLayout(this.outer) | ||||
| } | ||||
| 
 | ||||
| func (this *box) invalidateDraw () { | ||||
| 	if this.parent == nil || this.parent.window() == nil { return } | ||||
| 	this.parent.window().invalidateDraw(this.outer) | ||||
| 	this.window().invalidateDraw(this.outer) | ||||
| } | ||||
| 
 | ||||
| func (this *box) invalidateMinimum () { | ||||
| @ -414,7 +477,7 @@ func (this *box) invalidateMinimum () { | ||||
| 		this.minSizeQueued = true | ||||
| 		return | ||||
| 	} | ||||
| 	this.parent.window().invalidateMinimum(this.outer) | ||||
| 	this.window().invalidateMinimum(this.outer) | ||||
| } | ||||
| 
 | ||||
| func (this *box) canBeFocused () bool { | ||||
| @ -441,3 +504,8 @@ func (this *box) transparent () bool { | ||||
| 	return transparent(this.color) && | ||||
| 		(this.texture == nil || !this.texture.Opaque()) | ||||
| } | ||||
| 
 | ||||
| func (this *box) window () *window { | ||||
| 	if this.parent == nil { return nil } | ||||
| 	return this.parent.window() | ||||
| } | ||||
|  | ||||
| @ -32,8 +32,8 @@ func (this *Canvas) Pen () canvas.Pen { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Clip returns a sub-canvas of this canvas. | ||||
| func (this *Canvas) Clip (bounds image.Rectangle) canvas.Canvas { | ||||
| // SubCanvas returns a subset of this canvas that points to the same data. | ||||
| func (this *Canvas) SubCanvas (bounds image.Rectangle) canvas.Canvas { | ||||
| 	this.assert() | ||||
| 	subImage := this.Image.SubImage(bounds) | ||||
| 	if subImage == nil { return nil } | ||||
|  | ||||
| @ -86,8 +86,8 @@ func (this *Texture) Close () error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Clip returns a subset of this texture that points to the same data. | ||||
| func (this *Texture) Clip (bounds image.Rectangle) canvas.Texture { | ||||
| // SubTexture returns a subset of this texture that points to the same data. | ||||
| func (this *Texture) SubTexture (bounds image.Rectangle) canvas.Texture { | ||||
| 	clipped := *this | ||||
| 	clipped.rect = bounds | ||||
| 	return &clipped | ||||
|  | ||||
| @ -31,5 +31,5 @@ func (this *canvasBox) Invalidate () { | ||||
| func (this *canvasBox) Draw (can canvas.Canvas) { | ||||
| 	this.box.Draw(can) | ||||
| 	this.userDrawer.Draw ( | ||||
| 		can.Clip(this.padding.Apply(this.innerClippingBounds))) | ||||
| 		can.SubCanvas(this.padding.Apply(this.innerClippingBounds))) | ||||
| } | ||||
|  | ||||
| @ -31,12 +31,20 @@ func (backend *Backend) NewContainerBox() tomo.ContainerBox { | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) SetColor (c color.Color) { | ||||
| 	if this.color == c { return } | ||||
| 	this.box.SetColor(c) | ||||
| 	this.invalidateTransparentChildren() | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) SetTexture (texture canvas.Texture) { | ||||
| 	this.box.SetTexture(texture) | ||||
| func (this *containerBox) SetTextureTile (texture canvas.Texture) { | ||||
| 	if this.texture == texture { return } | ||||
| 	this.box.SetTextureTile(texture) | ||||
| 	this.invalidateTransparentChildren() | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) SetTextureCenter (texture canvas.Texture) { | ||||
| 	if this.texture == texture { return } | ||||
| 	this.box.SetTextureTile(texture) | ||||
| 	this.invalidateTransparentChildren() | ||||
| } | ||||
| 
 | ||||
| @ -102,7 +110,7 @@ func (this *containerBox) Add (child tomo.Object) { | ||||
| 	this.invalidateMinimum() | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) Delete (child tomo.Object) { | ||||
| func (this *containerBox) Remove (child tomo.Object) { | ||||
| 	box := assertAnyBox(child.GetBox()) | ||||
| 	index := indexOf(this.children, tomo.Box(box)) | ||||
| 	if index < 0 { return } | ||||
| @ -165,7 +173,7 @@ func (this *containerBox) Draw (can canvas.Canvas) { | ||||
| 		rocks[index] = box.Bounds() | ||||
| 	} | ||||
| 	for _, tile := range canvas.Shatter(this.bounds, rocks...) { | ||||
| 		clipped := can.Clip(tile) | ||||
| 		clipped := can.SubCanvas(tile) | ||||
| 		if this.transparent() && this.parent != nil { | ||||
| 			this.parent.drawBackgroundPart(clipped) | ||||
| 		} | ||||
| @ -225,12 +233,6 @@ func (this *containerBox) notifyMinimumSizeChange (child anyBox) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) boundedLayoutHints () tomo.LayoutHints { | ||||
| 	hints := this.layoutHints() | ||||
| 	hints.Bounds = this.ContentBounds().Add(this.InnerBounds().Min) | ||||
| 	return hints | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) layoutHints () tomo.LayoutHints { | ||||
| 	return tomo.LayoutHints { | ||||
| 		OverflowX: this.hOverflow, | ||||
| @ -245,7 +247,7 @@ func (this *containerBox) contentMinimum () image.Point { | ||||
| 	minimum := this.box.contentMinimum() | ||||
| 	if this.layout != nil { | ||||
| 		layoutMinimum := this.layout.MinimumSize ( | ||||
| 			this.boundedLayoutHints(), | ||||
| 			this.layoutHints(), | ||||
| 			this.children) | ||||
| 		if this.hOverflow { layoutMinimum.X = 0 } | ||||
| 		if this.vOverflow { layoutMinimum.Y = 0 } | ||||
| @ -258,8 +260,8 @@ func (this *containerBox) doLayout () { | ||||
| 	this.box.doLayout() | ||||
| 	previousContentBounds := this.contentBounds | ||||
| 
 | ||||
| 	// by default, use innerBounds for contentBounds. if a direction | ||||
| 	// overflows, use the layout's minimum size for it. | ||||
| 	// by default, use innerBounds (translated to 0, 0) for contentBounds. | ||||
| 	// if a direction overflows, use the layout's minimum size for it. | ||||
| 	var minimum image.Point | ||||
| 	if this.layout != nil { | ||||
| 		minimum = this.layout.MinimumSize ( | ||||
| @ -267,19 +269,33 @@ func (this *containerBox) doLayout () { | ||||
| 			this.children) | ||||
| 	} | ||||
| 	innerBounds := this.InnerBounds() | ||||
| 	this.contentBounds = innerBounds | ||||
| 	this.contentBounds = innerBounds.Sub(innerBounds.Min) | ||||
| 	if this.hOverflow { this.contentBounds.Max.X = this.contentBounds.Min.X + minimum.X } | ||||
| 	if this.vOverflow { this.contentBounds.Max.Y = this.contentBounds.Min.Y + minimum.Y } | ||||
| 
 | ||||
| 	// offset the content bounds by the scroll so children can be positioned | ||||
| 	// accordingly. | ||||
| 	this.constrainScroll() | ||||
| 	this.contentBounds = this.contentBounds.Add(this.scroll).Sub(innerBounds.Min) | ||||
| 	 | ||||
| 	// arrange children | ||||
| 	if this.layout != nil { | ||||
| 		this.layout.Arrange(this.boundedLayoutHints(), this.children) | ||||
| 		layoutHints := this.layoutHints() | ||||
| 		layoutHints.Bounds = this.contentBounds | ||||
| 		this.layout.Arrange(layoutHints, this.children) | ||||
| 	} | ||||
| 
 | ||||
| 	// build an accurate contentBounds by unioning the bounds of all child | ||||
| 	// boxes | ||||
| 	this.contentBounds = image.Rectangle { } | ||||
| 	for _, box := range this.children { | ||||
| 		bounds := box.Bounds() | ||||
| 		this.contentBounds = this.contentBounds.Union(bounds) | ||||
| 	} | ||||
| 
 | ||||
| 	// constrain the scroll | ||||
| 	this.constrainScroll() | ||||
| 
 | ||||
| 	// offset children and contentBounds by scroll | ||||
| 	for _, box := range this.children { | ||||
| 		box.SetBounds(box.Bounds().Add(this.scroll).Add(innerBounds.Min)) | ||||
| 	} | ||||
| 	this.contentBounds = this.contentBounds.Add(this.scroll) | ||||
| 	 | ||||
| 	if previousContentBounds != this.contentBounds { | ||||
| 		this.on.contentBoundsChange.Broadcast() | ||||
| @ -319,6 +335,8 @@ func (this *containerBox) recursiveRedo () { | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) boxUnder (point image.Point, category eventCategory) anyBox { | ||||
| 	if !point.In(this.bounds) { return nil } | ||||
| 
 | ||||
| 	if !this.capture[category] { | ||||
| 		for _, box := range this.children { | ||||
| 			candidate := box.(anyBox).boxUnder(point, category) | ||||
| @ -326,7 +344,7 @@ func (this *containerBox) boxUnder (point image.Point, category eventCategory) a | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return this.box.boxUnder(point, category) | ||||
| 	return this | ||||
| } | ||||
| 
 | ||||
| func (this *containerBox) propagate (callback func (anyBox) bool) bool { | ||||
|  | ||||
							
								
								
									
										13
									
								
								event.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								event.go
									
									
									
									
									
								
							| @ -161,16 +161,17 @@ func (window *window) handleConfigureNotify ( | ||||
| 	if window.root == nil { return } | ||||
| 	 | ||||
| 	configureEvent := *event.ConfigureNotifyEvent | ||||
| 	configureEvent = window.compressConfigureNotify(configureEvent) | ||||
| 	 | ||||
| 	newWidth  := int(configureEvent.Width) | ||||
| 	newHeight := int(configureEvent.Height) | ||||
| 	sizeChanged := | ||||
| 		window.metrics.bounds.Dx() != newWidth || | ||||
| 		window.metrics.bounds.Dy() != newHeight | ||||
| 	oldBounds := window.metrics.bounds | ||||
| 	window.updateBounds() | ||||
| 	newBounds := window.metrics.bounds | ||||
| 
 | ||||
| 	sizeChanged := | ||||
| 		oldBounds.Dx() != newBounds.Dx() || | ||||
| 		oldBounds.Dy() != newBounds.Dy() | ||||
| 
 | ||||
| 	if sizeChanged { | ||||
| 		configureEvent = window.compressConfigureNotify(configureEvent) | ||||
| 		window.reallocateCanvas() | ||||
| 
 | ||||
| 		// TODO figure out what to do with this | ||||
|  | ||||
							
								
								
									
										18
									
								
								examples/blank/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								examples/blank/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| package main | ||||
| 
 | ||||
| import "image" | ||||
| import "git.tebibyte.media/tomo/x" | ||||
| import "git.tebibyte.media/tomo/tomo" | ||||
| 
 | ||||
| func main () { | ||||
| 	tomo.Register(0, x.NewBackend) | ||||
| 	err := tomo.Run(run) | ||||
| 	if err != nil { panic(err) } | ||||
| } | ||||
| 
 | ||||
| func run () { | ||||
| 	window, err := tomo.NewWindow(image.Rect(0, 0, 200, 300)) | ||||
| 	if err != nil { panic(err) } | ||||
| 	window.OnClose(tomo.Stop) | ||||
| 	window.SetVisible(true) | ||||
| } | ||||
							
								
								
									
										29
									
								
								examples/text/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								examples/text/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | ||||
| package main | ||||
| 
 | ||||
| import "image" | ||||
| import "image/color" | ||||
| import "git.tebibyte.media/tomo/x" | ||||
| import "git.tebibyte.media/tomo/tomo" | ||||
| import "golang.org/x/image/font/basicfont" | ||||
| 
 | ||||
| func main () { | ||||
| 	tomo.Register(0, x.NewBackend) | ||||
| 	err := tomo.Run(run) | ||||
| 	if err != nil { panic(err) } | ||||
| } | ||||
| 
 | ||||
| func run () { | ||||
| 	window, err := tomo.NewWindow(image.Rectangle { }) | ||||
| 	if err != nil { panic(err) } | ||||
| 
 | ||||
| 	text := tomo.NewTextBox() | ||||
| 	text.SetText("hello, world!") | ||||
| 	text.SetTextColor(color.White) | ||||
| 	text.SetColor(color.Black) | ||||
| 	text.SetFace(basicfont.Face7x13) | ||||
| 	text.SetPadding(tomo.I(8)) | ||||
| 	window.SetRoot(text) | ||||
| 	 | ||||
| 	window.OnClose(tomo.Stop) | ||||
| 	window.SetVisible(true) | ||||
| } | ||||
							
								
								
									
										83
									
								
								examples/texture/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								examples/texture/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| package main | ||||
| 
 | ||||
| import "math" | ||||
| import "image" | ||||
| import "math/rand" | ||||
| import "image/color" | ||||
| import "git.tebibyte.media/tomo/x" | ||||
| import "git.tebibyte.media/tomo/tomo" | ||||
| 
 | ||||
| func main () { | ||||
| 	tomo.Register(0, x.NewBackend) | ||||
| 	err := tomo.Run(run) | ||||
| 	if err != nil { panic(err) } | ||||
| } | ||||
| 
 | ||||
| func run () { | ||||
| 	window, err := tomo.NewWindow(image.Rect(0, 0, 256, 256)) | ||||
| 	if err != nil { panic(err) } | ||||
| 	 | ||||
| 	texture := tomo.NewTexture(coolTexture()) | ||||
| 	 | ||||
| 	box := tomo.NewBox() | ||||
| 	box.SetColor(color.Black) | ||||
| 	box.SetTextureCenter(texture) | ||||
| 	box.SetBorder ( | ||||
| 		tomo.Border { | ||||
| 			Color: [4] color.Color { | ||||
| 				color.Black, color.Black, | ||||
| 				color.Black, color.Black }, | ||||
| 			Width: tomo.I(2), | ||||
| 		}, | ||||
| 		tomo.Border { | ||||
| 			Color: [4] color.Color { | ||||
| 			color.White, color.White, | ||||
| 			color.White, color.White }, | ||||
| 			Width: tomo.I(2), | ||||
| 		}) | ||||
| 	 | ||||
| 	window.SetRoot(box) | ||||
| 	window.OnClose(tomo.Stop) | ||||
| 	window.SetVisible(true) | ||||
| } | ||||
| 
 | ||||
| func coolTexture () image.Image { | ||||
| 	// this picture IS COOL because i spent AN HOUR on it when i could have | ||||
| 	// been FIXING BUGS! | ||||
| 	speedX := 0.015 | ||||
| 	speedY := 0.035 | ||||
| 	bitmap := image.NewRGBA (image.Rect(0, 0, 200, 200)) | ||||
| 	for y := bitmap.Bounds().Min.Y; y < bitmap.Bounds().Max.Y; y++ { | ||||
| 	for x := bitmap.Bounds().Min.X; x < bitmap.Bounds().Max.X; x++ { | ||||
| 		value := (( | ||||
| 			math.Sin(float64(y) * speedY) + | ||||
| 			math.Cos(float64(x) * speedX)) + 2) / 4 | ||||
| 		value *= 0.7 | ||||
| 
 | ||||
| 		r := value * 0.7 + 0.3 | ||||
| 		g := math.Sin(value * 7) * 0.7 | ||||
| 		b := (1 - value) * 0.7 | ||||
| 
 | ||||
| 		noise := math.Mod(rand.Float64(), 1) | ||||
| 		noise = math.Pow(noise, 2) + noise * 0.5 | ||||
| 		noise *= 0.05 | ||||
| 		 | ||||
| 		channel := func (f float64) uint8 { | ||||
| 			contrast := 1.4 | ||||
| 			f = f * (contrast) - ((contrast - 1) / 2) | ||||
| 			 | ||||
| 			if f < 0 { f = 0 } | ||||
| 			if f > 1 { f = 1 } | ||||
| 			 | ||||
| 			return uint8(f * 255) | ||||
| 		} | ||||
| 		 | ||||
| 		bitmap.Set(x, y, color.RGBA { | ||||
| 			R: channel(r + noise), | ||||
| 			G: channel(g + noise), | ||||
| 			B: channel(b + noise), | ||||
| 			A: 255, | ||||
| 		}) | ||||
| 	}} | ||||
| 	return bitmap | ||||
| } | ||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							| @ -3,7 +3,7 @@ module git.tebibyte.media/tomo/x | ||||
| go 1.20 | ||||
| 
 | ||||
| require ( | ||||
| 	git.tebibyte.media/tomo/tomo v0.31.0 | ||||
| 	git.tebibyte.media/tomo/tomo v0.34.0 | ||||
| 	git.tebibyte.media/tomo/typeset v0.7.1 | ||||
| 	git.tebibyte.media/tomo/xgbkb v1.0.1 | ||||
| 	github.com/jezek/xgb v1.1.0 | ||||
|  | ||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| git.tebibyte.media/sashakoshka/xgbkb v1.0.0/go.mod h1:pNcE6TRO93vHd6q42SdwLSTTj25L0Yzggz7yLe0JV6Q= | ||||
| git.tebibyte.media/tomo/tomo v0.31.0 h1:LHPpj3AWycochnC8F441aaRNS6Tq6w6WnBrp/LGjyhM= | ||||
| git.tebibyte.media/tomo/tomo v0.31.0/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps= | ||||
| git.tebibyte.media/tomo/tomo v0.34.0 h1:r5yJPks9rtzdDI2RyAUdqa1qb6BebG0QFe2cTmcFi+0= | ||||
| git.tebibyte.media/tomo/tomo v0.34.0/go.mod h1:C9EzepS9wjkTJjnZaPBh22YvVPyA4hbBAJVU20Rdmps= | ||||
| 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/xgbkb v1.0.1 h1:b3HDUopjdQp1MZrb5Vpil4bOtk3NnNXtfQW27Blw2kE= | ||||
|  | ||||
							
								
								
									
										8
									
								
								surfacebox.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								surfacebox.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| package x | ||||
| 
 | ||||
| import "errors" | ||||
| import "git.tebibyte.media/tomo/tomo" | ||||
| 
 | ||||
| func (backend *Backend) NewSurfaceBox() (tomo.SurfaceBox, error) { | ||||
| 	return nil, errors.New("SurfaceBox not implemented yet") | ||||
| } | ||||
| @ -241,7 +241,9 @@ func (window *window) afterEvent () { | ||||
| 		// set child bounds | ||||
| 		childBounds := window.metrics.bounds | ||||
| 		childBounds = childBounds.Sub(childBounds.Min) | ||||
| 		window.root.SetBounds(childBounds) | ||||
| 		if window.root != nil { | ||||
| 			window.root.SetBounds(childBounds)	 | ||||
| 		} | ||||
| 
 | ||||
| 		// full relayout/redraw | ||||
| 		if window.root != nil { | ||||
|  | ||||
| @ -227,7 +227,6 @@ func (this *textBox) textOffset () image.Point { | ||||
| } | ||||
| 
 | ||||
| func (this *textBox) handleFocusLeave () { | ||||
| 	this.dot = text.EmptyDot(0) | ||||
| 	this.on.dotChange.Broadcast() | ||||
| 	this.invalidateDraw() | ||||
| 	this.box.handleFocusLeave() | ||||
|  | ||||
							
								
								
									
										24
									
								
								window.go
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								window.go
									
									
									
									
									
								
							| @ -32,6 +32,7 @@ type window struct { | ||||
| 	modalParent *window | ||||
| 	hasModal    bool | ||||
| 	shy         bool | ||||
| 	visible     bool | ||||
| 
 | ||||
| 	metrics struct { | ||||
| 		bounds image.Rectangle | ||||
| @ -265,14 +266,21 @@ func (window *window) Paste (callback func (data.Data, error), accept ...data.Mi | ||||
| 	// TODO | ||||
| } | ||||
| 
 | ||||
| func (window *window) Show () { | ||||
| 	window.xWindow.Map() | ||||
| 	if window.shy { window.grabInput() } | ||||
| func (window *window) SetVisible (visible bool) { | ||||
| 	if window.visible == visible { return } | ||||
| 	window.visible = visible | ||||
| 	 | ||||
| 	if window.visible { | ||||
| 		window.xWindow.Map() | ||||
| 		if window.shy { window.grabInput() } | ||||
| 	} else { | ||||
| 		window.xWindow.Unmap() | ||||
| 		if window.shy { window.ungrabInput() } | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (window *window) Hide () { | ||||
| 	window.xWindow.Unmap() | ||||
| 	if window.shy { window.ungrabInput() } | ||||
| func (window *window) Visible () bool { | ||||
| 	return window.visible | ||||
| } | ||||
| 
 | ||||
| func (window *window) Close () { | ||||
| @ -285,7 +293,7 @@ func (window *window) Close () { | ||||
| 		// we are a modal dialog, so unlock the parent | ||||
| 		window.modalParent.hasModal = false | ||||
| 	} | ||||
| 	window.Hide() | ||||
| 	window.SetVisible(false) | ||||
| 	window.SetRoot(nil) | ||||
| 	delete(window.backend.windows, window.xWindow.Id) | ||||
| 	window.xWindow.Destroy() | ||||
| @ -369,7 +377,7 @@ func (window *window) pushRegion (region image.Rectangle) { | ||||
| 		return | ||||
| 	} | ||||
| 	 | ||||
| 	subCanvas := window.xCanvas.Clip(region) | ||||
| 	subCanvas := window.xCanvas.SubCanvas(region) | ||||
| 	if subCanvas == nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
		Reference in New Issue
	
	Block a user