Overhauled event system
This commit is contained in:
		
							parent
							
								
									3a3fb66db8
								
							
						
					
					
						commit
						e030f8632b
					
				| @ -10,12 +10,12 @@ type Application struct { | |||||||
| 	icons []image.Image | 	icons []image.Image | ||||||
| 	backend Backend | 	backend Backend | ||||||
| 	config Config | 	config Config | ||||||
|  | 	callbackManager CallbackManager | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Run initializes the application, starts it, and then returns a channel that | // Run initializes the application, starts it, and then returns a channel that | ||||||
| // broadcasts events. If no suitable backend can be found, an error is returned. | // broadcasts events. If no suitable backend can be found, an error is returned. | ||||||
| func (application *Application) Run () ( | func (application *Application) Run () ( | ||||||
| 	channel chan(Event), |  | ||||||
| 	err     error, | 	err     error, | ||||||
| ) { | ) { | ||||||
| 	// default values for certain parameters | 	// default values for certain parameters | ||||||
| @ -29,12 +29,46 @@ func (application *Application) Run () ( | |||||||
| 	application.backend, err = instantiateBackend(application) | 	application.backend, err = instantiateBackend(application) | ||||||
| 	if err != nil { return } | 	if err != nil { return } | ||||||
| 	 | 	 | ||||||
| 	channel = make(chan(Event)) | 	application.backend.Run() | ||||||
| 	go application.backend.Run(channel) |  | ||||||
| 
 |  | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (application *Application) OnQuit ( | ||||||
|  | 	onQuit func (), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onQuit = onQuit | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (application *Application) OnPress ( | ||||||
|  | 	onPress func (button Button), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onPress = onPress | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (application *Application) OnRelease ( | ||||||
|  | 	onRelease func (button Button), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onRelease = onRelease | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (application *Application) OnResize ( | ||||||
|  | 	onResize func (), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onResize = onResize | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (application *Application) OnMouseMove ( | ||||||
|  | 	onMouseMove func (x, y int), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onMouseMove = onMouseMove | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (application *Application) OnStart ( | ||||||
|  | 	onStart func (), | ||||||
|  | ) { | ||||||
|  | 	application.callbackManager.onStart = onStart | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Draw "commits" changes made in the buffer to the display. | // Draw "commits" changes made in the buffer to the display. | ||||||
| func (application *Application) Draw () { | func (application *Application) Draw () { | ||||||
| 	application.backend.Draw() | 	application.backend.Draw() | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								backend.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								backend.go
									
									
									
									
									
								
							| @ -4,13 +4,19 @@ import "image" | |||||||
| import "errors" | import "errors" | ||||||
| 
 | 
 | ||||||
| type Backend interface { | type Backend interface { | ||||||
| 	Run      (channel chan(Event)) | 	Run      () | ||||||
| 	SetTitle (title string) (err error) | 	SetTitle (title string) (err error) | ||||||
| 	SetIcon  (icons []image.Image) (err error) | 	SetIcon  (icons []image.Image) (err error) | ||||||
| 	Draw     () | 	Draw     () | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type BackendFactory func (application *Application) (backend Backend, err error) | type BackendFactory func ( | ||||||
|  | 	application *Application, | ||||||
|  | 	callbackManager *CallbackManager, | ||||||
|  | ) ( | ||||||
|  | 	backend Backend, | ||||||
|  | 	err error, | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| var factories []BackendFactory | var factories []BackendFactory | ||||||
| 
 | 
 | ||||||
| @ -21,7 +27,7 @@ func RegisterBackend (factory BackendFactory) { | |||||||
| func instantiateBackend (application *Application) (backend Backend, err error) { | func instantiateBackend (application *Application) (backend Backend, err error) { | ||||||
| 	// find a suitable backend | 	// find a suitable backend | ||||||
| 	for _, factory := range factories { | 	for _, factory := range factories { | ||||||
| 		backend, err = factory(application) | 		backend, err = factory(application, &application.callbackManager) | ||||||
| 		if err == nil && backend != nil { return } | 		if err == nil && backend != nil { return } | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,12 +3,13 @@ package x | |||||||
| import "image" | import "image" | ||||||
| import "image/draw" | import "image/draw" | ||||||
| import "golang.org/x/image/math/fixed" | import "golang.org/x/image/math/fixed" | ||||||
|  | import "github.com/jezek/xgbutil/xgraphics" | ||||||
| 
 | 
 | ||||||
| import "git.tebibyte.media/sashakoshka/stone" | import "git.tebibyte.media/sashakoshka/stone" | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) Draw () { | func (backend *Backend) Draw () { | ||||||
| 	backend.drawLock.Lock() | 	backend.lock.Lock() | ||||||
| 	defer backend.drawLock.Unlock() | 	defer backend.lock.Unlock() | ||||||
| 
 | 
 | ||||||
| 	boundsChanged := | 	boundsChanged := | ||||||
| 		backend.memory.windowWidth  != backend.metrics.windowWidth || | 		backend.memory.windowWidth  != backend.metrics.windowWidth || | ||||||
| @ -22,25 +23,77 @@ func (backend *Backend) Draw () { | |||||||
| 		backend.canvas.XDraw() | 		backend.canvas.XDraw() | ||||||
| 		backend.canvas.XPaint(backend.window.Id) | 		backend.canvas.XPaint(backend.window.Id) | ||||||
| 	} else { | 	} else { | ||||||
| 		backend.updateWindowAreas(backend.drawCells(false)...) | 		backend.canvas.XPaintRects ( | ||||||
|  | 			backend.window.Id, | ||||||
|  | 			backend.drawCells(false)...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) updateWindowAreas (areas ...image.Rectangle) { | func (backend *Backend) reallocateCanvas () { | ||||||
| 	backend.canvas.XPaintRects(backend.window.Id, areas...) | 	if backend.canvas != nil { | ||||||
|  | 		backend.canvas.Destroy() | ||||||
|  | 	} | ||||||
|  | 	backend.canvas = xgraphics.New ( | ||||||
|  | 		backend.connection, | ||||||
|  | 		image.Rect ( | ||||||
|  | 			0, 0, | ||||||
|  | 			backend.metrics.windowWidth, | ||||||
|  | 			backend.metrics.windowHeight)) | ||||||
|  | 	backend.canvas.For (func (x, y int) xgraphics.BGRA { | ||||||
|  | 		return backend.colors[stone.ColorBackground] | ||||||
|  | 	}) | ||||||
|  | 	 | ||||||
|  | 	backend.canvas.XSurfaceSet(backend.window.Id) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) drawRune (x, y int, character rune, runeColor stone.Color) { | func (backend *Backend) drawCells (forceRedraw bool) (areas []image.Rectangle) { | ||||||
|  | 	width, height := backend.application.Size() | ||||||
|  | 	for y := 0; y < height; y ++ { | ||||||
|  | 	for x := 0; x < width;  x ++ { | ||||||
|  | 		if !forceRedraw && backend.application.Clean(x, y) { continue } | ||||||
|  | 		backend.application.MarkClean(x, y) | ||||||
|  | 
 | ||||||
|  | 		cell := backend.application.Cell(x, y) | ||||||
|  | 		content := cell.Rune() | ||||||
|  | 
 | ||||||
|  | 		if forceRedraw && content < 32 { continue } | ||||||
|  | 
 | ||||||
|  | 		areas = append(areas, backend.boundsOfCell(x, y)) | ||||||
|  | 		backend.drawRune(x, y, content, cell.Color(), !forceRedraw) | ||||||
|  | 	}} | ||||||
|  | 
 | ||||||
|  | 	if backend.drawBufferBounds && forceRedraw { | ||||||
|  | 		strokeRectangle ( | ||||||
|  | 			&image.Uniform { | ||||||
|  | 				C: backend.config.Color(stone.ColorForeground), | ||||||
|  | 			}, | ||||||
|  | 			backend.canvas, | ||||||
|  | 			image.Rectangle { | ||||||
|  | 				Min: backend.originOfCell(0, 0), | ||||||
|  | 				Max: backend.originOfCell(width, height), | ||||||
|  | 			}) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (backend *Backend) drawRune ( | ||||||
|  | 	x, y int, | ||||||
|  | 	character rune, | ||||||
|  | 	runeColor stone.Color, | ||||||
|  | 	drawBackground bool, | ||||||
|  | ) { | ||||||
| 	// TODO: cache these draws as non-transparent buffers with the | 	// TODO: cache these draws as non-transparent buffers with the | ||||||
| 	// application background color as the background. that way, we won't | 	// application background color as the background. that way, we won't | ||||||
| 	// need to redraw the characters *or* composite them. | 	// need to redraw the characters *or* composite them. | ||||||
| 
 | 
 | ||||||
| 	fillRectangle ( | 	if drawBackground { | ||||||
| 		&image.Uniform { | 		fillRectangle ( | ||||||
| 			C: backend.config.Color(stone.ColorBackground), | 			&image.Uniform { | ||||||
| 		}, | 				C: backend.config.Color(stone.ColorBackground), | ||||||
| 		backend.canvas, | 			}, | ||||||
| 		backend.boundsOfCell(x, y)) | 			backend.canvas, | ||||||
|  | 			backend.boundsOfCell(x, y)) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	if character < 32 { return } | 	if character < 32 { return } | ||||||
| 	 | 	 | ||||||
| @ -73,36 +126,6 @@ func (backend *Backend) drawRune (x, y int, character rune, runeColor stone.Colo | |||||||
| 		draw.Over) | 		draw.Over) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) drawCells (forceRedraw bool) (areas []image.Rectangle) { |  | ||||||
| 	width, height := backend.application.Size() |  | ||||||
| 	for y := 0; y < height; y ++ { |  | ||||||
| 	for x := 0; x < width;  x ++ { |  | ||||||
| 		if !forceRedraw && backend.application.Clean(x, y) { continue } |  | ||||||
| 		backend.application.MarkClean(x, y) |  | ||||||
| 
 |  | ||||||
| 		cell := backend.application.Cell(x, y) |  | ||||||
| 		content := cell.Rune() |  | ||||||
| 
 |  | ||||||
| 		if forceRedraw && content < 32 { continue } |  | ||||||
| 
 |  | ||||||
| 		areas = append(areas, backend.boundsOfCell(x, y)) |  | ||||||
| 		backend.drawRune(x, y, content, cell.Color()) |  | ||||||
| 	}} |  | ||||||
| 
 |  | ||||||
| 	if backend.drawBufferBounds && forceRedraw { |  | ||||||
| 		strokeRectangle ( |  | ||||||
| 			&image.Uniform { |  | ||||||
| 				C: backend.config.Color(stone.ColorForeground), |  | ||||||
| 			}, |  | ||||||
| 			backend.canvas, |  | ||||||
| 			image.Rectangle { |  | ||||||
| 				Min: backend.originOfCell(0, 0), |  | ||||||
| 				Max: backend.originOfCell(width, height), |  | ||||||
| 			}) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func fillRectangle ( | func fillRectangle ( | ||||||
| 	source      image.Image, | 	source      image.Image, | ||||||
| 	destination draw.Image, | 	destination draw.Image, | ||||||
|  | |||||||
| @ -8,17 +8,19 @@ import "github.com/jezek/xgbutil/xevent" | |||||||
| 
 | 
 | ||||||
| import "git.tebibyte.media/sashakoshka/stone" | import "git.tebibyte.media/sashakoshka/stone" | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) Run (channel chan(stone.Event)) { | func (backend *Backend) Run () { | ||||||
| 	backend.channel = channel | 	backend.callbackManager.RunStart() | ||||||
|  | 	backend.Draw() | ||||||
| 	xevent.Main(backend.connection) | 	xevent.Main(backend.connection) | ||||||
| 	backend.shutDown() | 	backend.shutDown() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| func (backend *Backend) handleConfigureNotify ( | func (backend *Backend) handleConfigureNotify ( | ||||||
| 	connection *xgbutil.XUtil, | 	connection *xgbutil.XUtil, | ||||||
| 	event xevent.ConfigureNotifyEvent, | 	event xevent.ConfigureNotifyEvent, | ||||||
| ) { | ) { | ||||||
|  | 	backend.lock.Lock() | ||||||
|  | 	 | ||||||
| 	configureEvent := *event.ConfigureNotifyEvent | 	configureEvent := *event.ConfigureNotifyEvent | ||||||
| 	 | 	 | ||||||
| 	newWidth  := int(configureEvent.Width) | 	newWidth  := int(configureEvent.Width) | ||||||
| @ -44,8 +46,13 @@ func (backend *Backend) handleConfigureNotify ( | |||||||
| 			(backend.metrics.windowWidth - frameWidth) / 2 | 			(backend.metrics.windowWidth - frameWidth) / 2 | ||||||
| 		backend.metrics.paddingY = | 		backend.metrics.paddingY = | ||||||
| 			(backend.metrics.windowHeight - frameHeight) / 2 | 			(backend.metrics.windowHeight - frameHeight) / 2 | ||||||
|  | 	} | ||||||
| 	 | 	 | ||||||
| 		backend.channel <- stone.EventResize { } | 	backend.lock.Unlock() | ||||||
|  | 
 | ||||||
|  | 	if sizeChanged { | ||||||
|  | 		backend.callbackManager.RunResize() | ||||||
|  | 		backend.Draw() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -54,9 +61,7 @@ func (backend *Backend) handleButtonPress ( | |||||||
| 	event xevent.ButtonPressEvent, | 	event xevent.ButtonPressEvent, | ||||||
| ) { | ) { | ||||||
| 	buttonEvent := *event.ButtonPressEvent | 	buttonEvent := *event.ButtonPressEvent | ||||||
| 	backend.channel <- stone.EventPress { | 	backend.callbackManager.RunPress(stone.Button(buttonEvent.Detail + 127)) | ||||||
| 		Button: stone.Button(buttonEvent.Detail + 127), |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) handleButtonRelease ( | func (backend *Backend) handleButtonRelease ( | ||||||
| @ -64,9 +69,7 @@ func (backend *Backend) handleButtonRelease ( | |||||||
| 	event xevent.ButtonReleaseEvent, | 	event xevent.ButtonReleaseEvent, | ||||||
| ) { | ) { | ||||||
| 	buttonEvent := *event.ButtonReleaseEvent | 	buttonEvent := *event.ButtonReleaseEvent | ||||||
| 	backend.channel <- stone.EventRelease { | 	backend.callbackManager.RunRelease(stone.Button(buttonEvent.Detail + 127)) | ||||||
| 		Button: stone.Button(buttonEvent.Detail + 127), |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) handleKeyPress ( | func (backend *Backend) handleKeyPress ( | ||||||
| @ -75,7 +78,7 @@ func (backend *Backend) handleKeyPress ( | |||||||
| ) { | ) { | ||||||
| 	keyEvent := *event.KeyPressEvent | 	keyEvent := *event.KeyPressEvent | ||||||
| 	button   := backend.keycodeToButton(keyEvent.Detail, keyEvent.State) | 	button   := backend.keycodeToButton(keyEvent.Detail, keyEvent.State) | ||||||
| 	backend.channel <- stone.EventPress { Button: button } | 	backend.callbackManager.RunPress(button) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) handleKeyRelease ( | func (backend *Backend) handleKeyRelease ( | ||||||
| @ -84,7 +87,7 @@ func (backend *Backend) handleKeyRelease ( | |||||||
| ) { | ) { | ||||||
| 	keyEvent := *event.KeyReleaseEvent | 	keyEvent := *event.KeyReleaseEvent | ||||||
| 	button   := backend.keycodeToButton(keyEvent.Detail, keyEvent.State) | 	button   := backend.keycodeToButton(keyEvent.Detail, keyEvent.State) | ||||||
| 	backend.channel <- stone.EventRelease { Button: button } | 	backend.callbackManager.RunRelease(button) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) handleMotionNotify ( | func (backend *Backend) handleMotionNotify ( | ||||||
| @ -96,10 +99,7 @@ func (backend *Backend) handleMotionNotify ( | |||||||
| 		X: int(motionEvent.EventX), | 		X: int(motionEvent.EventX), | ||||||
| 		Y: int(motionEvent.EventY), | 		Y: int(motionEvent.EventY), | ||||||
| 	}) | 	}) | ||||||
| 	backend.channel <- stone.EventMouseMove { | 	backend.callbackManager.RunMouseMove(x, y) | ||||||
| 		X: x, |  | ||||||
| 		Y: y, |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) compressConfigureNotify ( | func (backend *Backend) compressConfigureNotify ( | ||||||
| @ -127,5 +127,5 @@ func (backend *Backend) compressConfigureNotify ( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) shutDown () { | func (backend *Backend) shutDown () { | ||||||
| 	backend.channel <- stone.EventQuit { } | 	backend.callbackManager.RunQuit() | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,10 +18,17 @@ import "git.tebibyte.media/sashakoshka/stone" | |||||||
| import "github.com/flopp/go-findfont" | import "github.com/flopp/go-findfont" | ||||||
| 
 | 
 | ||||||
| // factory instantiates an X backend. | // factory instantiates an X backend. | ||||||
| func factory (application *stone.Application) (output stone.Backend, err error) { | func factory ( | ||||||
|  | 	application *stone.Application, | ||||||
|  | 	callbackManager *stone.CallbackManager, | ||||||
|  | ) ( | ||||||
|  | 	output stone.Backend, | ||||||
|  | 	err error, | ||||||
|  | ) { | ||||||
| 	backend := &Backend { | 	backend := &Backend { | ||||||
| 		application: application, | 		application:     application, | ||||||
| 		config:      application.Config(), | 		config:          application.Config(), | ||||||
|  | 		callbackManager: callbackManager, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// load font | 	// load font | ||||||
| @ -76,8 +83,6 @@ func factory (application *stone.Application) (output stone.Backend, err error) | |||||||
| 		backend.metrics.windowWidth, backend.metrics.windowHeight, | 		backend.metrics.windowWidth, backend.metrics.windowHeight, | ||||||
| 		0) | 		0) | ||||||
| 	backend.window.Map() | 	backend.window.Map() | ||||||
| 	// TODO: also listen to mouse movement (compressed) and mouse and |  | ||||||
| 	// keyboard buttons (uncompressed) |  | ||||||
| 	err = backend.window.Listen ( | 	err = backend.window.Listen ( | ||||||
| 		xproto.EventMaskStructureNotify, | 		xproto.EventMaskStructureNotify, | ||||||
| 		xproto.EventMaskPointerMotion, | 		xproto.EventMaskPointerMotion, | ||||||
|  | |||||||
| @ -14,17 +14,17 @@ import "github.com/jezek/xgbutil/xgraphics" | |||||||
| import "git.tebibyte.media/sashakoshka/stone" | import "git.tebibyte.media/sashakoshka/stone" | ||||||
| 
 | 
 | ||||||
| type Backend struct { | type Backend struct { | ||||||
| 	application *stone.Application | 	application     *stone.Application | ||||||
| 	config      *stone.Config | 	config          *stone.Config | ||||||
| 	connection  *xgbutil.XUtil | 	callbackManager *stone.CallbackManager | ||||||
| 	window      *xwindow.Window | 	connection      *xgbutil.XUtil | ||||||
| 	canvas      *xgraphics.Image | 	window          *xwindow.Window | ||||||
| 	channel     chan(stone.Event) | 	canvas          *xgraphics.Image | ||||||
| 
 | 
 | ||||||
| 	drawCellBounds   bool | 	drawCellBounds   bool | ||||||
| 	drawBufferBounds bool | 	drawBufferBounds bool | ||||||
| 
 | 
 | ||||||
| 	drawLock sync.Mutex | 	lock sync.Mutex | ||||||
| 
 | 
 | ||||||
| 	font struct { | 	font struct { | ||||||
| 		face font.Face | 		face font.Face | ||||||
| @ -114,23 +114,6 @@ func (backend *Backend) calculateBufferSize () (width, height int) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (backend *Backend) reallocateCanvas () { |  | ||||||
| 	if backend.canvas != nil { |  | ||||||
| 		backend.canvas.Destroy() |  | ||||||
| 	} |  | ||||||
| 	backend.canvas = xgraphics.New ( |  | ||||||
| 		backend.connection, |  | ||||||
| 		image.Rect ( |  | ||||||
| 			0, 0, |  | ||||||
| 			backend.metrics.windowWidth, |  | ||||||
| 			backend.metrics.windowHeight)) |  | ||||||
| 	backend.canvas.For (func (x, y int) xgraphics.BGRA { |  | ||||||
| 		return backend.colors[stone.ColorBackground] |  | ||||||
| 	}) |  | ||||||
| 	 |  | ||||||
| 	backend.canvas.XSurfaceSet(backend.window.Id) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (backend *Backend) cellAt (onScreen image.Point) (x, y int) { | func (backend *Backend) cellAt (onScreen image.Point) (x, y int) { | ||||||
| 	x = (onScreen.X - backend.metrics.paddingX) / backend.metrics.cellWidth | 	x = (onScreen.X - backend.metrics.paddingX) / backend.metrics.cellWidth | ||||||
| 	y = (onScreen.Y - backend.metrics.paddingY) / backend.metrics.cellHeight | 	y = (onScreen.Y - backend.metrics.paddingY) / backend.metrics.cellHeight | ||||||
|  | |||||||
| @ -153,6 +153,10 @@ func (buffer *Buffer) SetRune (x, y int, content rune) { | |||||||
| 	buffer.lock.RLock() | 	buffer.lock.RLock() | ||||||
| 	defer buffer.lock.RUnlock() | 	defer buffer.lock.RUnlock() | ||||||
| 	 | 	 | ||||||
|  | 	buffer.setRune(x, y, content) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (buffer *Buffer) setRune (x, y int, content rune) { | ||||||
| 	if buffer.isOutOfBounds(x, y) { return } | 	if buffer.isOutOfBounds(x, y) { return } | ||||||
| 	index := x + y * buffer.width | 	index := x + y * buffer.width | ||||||
| 	buffer.clean[index] = buffer.content[index].content == content | 	buffer.clean[index] = buffer.content[index].content == content | ||||||
| @ -169,7 +173,7 @@ func (buffer *Buffer) Write (bytes []byte) (bytesWritten int, err error) { | |||||||
| 	bytesWritten = len(bytes) | 	bytesWritten = len(bytes) | ||||||
| 	 | 	 | ||||||
| 	for _, character := range text { | 	for _, character := range text { | ||||||
| 		buffer.SetRune(buffer.dot.x, buffer.dot.y, character) | 		buffer.setRune(buffer.dot.x, buffer.dot.y, character) | ||||||
| 		buffer.dot.x ++ | 		buffer.dot.x ++ | ||||||
| 		if buffer.dot.x > buffer.width { break } | 		if buffer.dot.x > buffer.width { break } | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										61
									
								
								event.go
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								event.go
									
									
									
									
									
								
							| @ -1,27 +1,40 @@ | |||||||
| package stone | package stone | ||||||
| 
 | 
 | ||||||
| // Event can be any event. | type CallbackManager struct { | ||||||
| type Event interface { } | 	onQuit      func () | ||||||
| 
 | 	onPress     func (button Button) | ||||||
| // EventQuit is sent when the backend shuts down due to a window close, error, | 	onRelease   func (button Button) | ||||||
| // or something else. | 	onResize    func () | ||||||
| type EventQuit struct { } | 	onMouseMove func (x, y int) | ||||||
| 
 | 	onStart     func () | ||||||
| // EventPress is sent when a button is pressed, or a key repeat event is | } | ||||||
| // triggered. | 
 | ||||||
| type EventPress struct { Button } | func (manager *CallbackManager) RunQuit () { | ||||||
| 
 | 	if manager.onQuit == nil { return } | ||||||
| // Release is sent when a button is released. | 	manager.onQuit() | ||||||
| type EventRelease struct { Button } | } | ||||||
| 
 | 
 | ||||||
| // Resize is sent when the application window is resized by the user. This event | func (manager *CallbackManager) RunPress (button Button) { | ||||||
| // must be handled, as it implies that the buffer has been resized and therefore | 	if manager.onPress == nil { return } | ||||||
| // cleared. Application.Draw() must be called after this event is recieved. | 	manager.onPress(button) | ||||||
| type EventResize struct { } | } | ||||||
| 
 | 
 | ||||||
| // EventMouseMove is sent when the mouse changes position. It contains the X and | func (manager *CallbackManager) RunRelease (button Button) { | ||||||
| // Y position of the mouse. | 	if manager.onRelease == nil { return } | ||||||
| type EventMouseMove struct { | 	manager.onRelease(button) | ||||||
| 	X int | } | ||||||
| 	Y int | 
 | ||||||
|  | func (manager *CallbackManager) RunResize () { | ||||||
|  | 	if manager.onResize == nil { return } | ||||||
|  | 	manager.onResize() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (manager *CallbackManager) RunMouseMove (x, y int) { | ||||||
|  | 	if manager.onMouseMove == nil { return } | ||||||
|  | 	manager.onMouseMove(x, y) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (manager *CallbackManager) RunStart () { | ||||||
|  | 	if manager.onStart == nil { return } | ||||||
|  | 	manager.onStart() | ||||||
| } | } | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ var application = &stone.Application { } | |||||||
| var mousePressed bool | var mousePressed bool | ||||||
| 
 | 
 | ||||||
| func main () { | func main () { | ||||||
| 	application.SetTitle("hellorld") | 	application.SetTitle("drawing canvas") | ||||||
| 	application.SetSize(32, 16) | 	application.SetSize(32, 16) | ||||||
| 
 | 
 | ||||||
| 	iconFile16, err := os.Open("assets/scaffold16.png") | 	iconFile16, err := os.Open("assets/scaffold16.png") | ||||||
| @ -26,42 +26,37 @@ func main () { | |||||||
| 	 | 	 | ||||||
| 	application.SetIcon([]image.Image { icon16, icon32 }) | 	application.SetIcon([]image.Image { icon16, icon32 }) | ||||||
| 	 | 	 | ||||||
| 	channel, err := application.Run() | 	application.OnPress(onPress) | ||||||
|  | 	application.OnRelease(onRelease) | ||||||
|  | 	application.OnMouseMove(onMouseMove) | ||||||
|  | 	application.OnQuit(onQuit) | ||||||
|  | 	 | ||||||
|  | 	err = application.Run() | ||||||
| 	if err != nil { panic(err) } | 	if err != nil { panic(err) } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| 	application.Draw() | func onPress (button stone.Button) { | ||||||
| 
 | 	if button == stone.MouseButtonLeft { | ||||||
| 	for { | 		mousePressed = true | ||||||
| 		event := <- channel | 		application.SetRune(0, 0, '+') | ||||||
| 		switch event.(type) { | 		application.Draw() | ||||||
| 		case stone.EventQuit: |  | ||||||
| 			os.Exit(0) |  | ||||||
| 
 |  | ||||||
| 		case stone.EventPress: |  | ||||||
| 			button := event.(stone.EventPress).Button |  | ||||||
| 			if button == stone.MouseButtonLeft { |  | ||||||
| 				mousePressed = true |  | ||||||
| 				application.SetRune(0, 0, '+') |  | ||||||
| 				application.Draw() |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		case stone.EventRelease: |  | ||||||
| 			button := event.(stone.EventRelease).Button |  | ||||||
| 			if button == stone.MouseButtonLeft { |  | ||||||
| 				mousePressed = false |  | ||||||
| 				application.SetRune(0, 0, 0) |  | ||||||
| 				application.Draw() |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		case stone.EventMouseMove: |  | ||||||
| 			event := event.(stone.EventMouseMove) |  | ||||||
| 			if mousePressed { |  | ||||||
| 				application.SetRune(event.X, event.Y, '#') |  | ||||||
| 				application.Draw() |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 		case stone.EventResize: |  | ||||||
| 			application.Draw() |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func onRelease (button stone.Button) { | ||||||
|  | 	if button == stone.MouseButtonLeft { | ||||||
|  | 		mousePressed = false | ||||||
|  | 		application.SetRune(0, 0, 0) | ||||||
|  | 		application.Draw() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func onMouseMove (x, y int) {	if mousePressed { | ||||||
|  | 		application.SetRune(x, y, '#') | ||||||
|  | 		application.Draw() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func onQuit () { | ||||||
|  | 	os.Exit(0) | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user