diff --git a/backend.go b/backend.go index 956b906..b6d9e83 100644 --- a/backend.go +++ b/backend.go @@ -1,5 +1,6 @@ package tomo +import "image" import "errors" // Backend represents a connection to a display server, or something similar. @@ -7,7 +8,7 @@ import "errors" type Backend interface { // Run runs the backend's event loop. It must block until the backend // experiences a fatal error, or Stop() is called. - Run () (err error) + Run () error // Stop stops the backend's event loop. Stop () @@ -16,10 +17,10 @@ type Backend interface { // possible. This method must be safe to call from other threads. Do (callback func ()) - // NewWindow creates a new window with the specified width and height, - // and returns a struct representing it that fulfills the MainWindow - // interface. - NewWindow (width, height int) (window MainWindow, err error) + // NewWindow creates a new window within the specified bounding + // rectangle. The position on screen may be overridden by the backend or + // operating system. + NewWindow (bounds image.Rectangle) (MainWindow, error) // SetTheme sets the theme of all open windows. SetTheme (Theme) diff --git a/backends/x/event.go b/backends/x/event.go index 2f96794..7da9b1a 100644 --- a/backends/x/event.go +++ b/backends/x/event.go @@ -52,6 +52,12 @@ func (window *window) handleExpose ( window.pushRegion(region) } +func (window *window) updateBounds (x, y int16, width, height uint16) { + window.metrics.bounds = + image.Rect(0, 0, int(width), int(height)). + Add(image.Pt(int(x), int(y))) +} + func (window *window) handleConfigureNotify ( connection *xgbutil.XUtil, event xevent.ConfigureNotifyEvent, @@ -63,15 +69,18 @@ func (window *window) handleConfigureNotify ( newWidth := int(configureEvent.Width) newHeight := int(configureEvent.Height) sizeChanged := - window.metrics.width != newWidth || - window.metrics.height != newHeight - window.metrics.width = newWidth - window.metrics.height = newHeight + window.metrics.bounds.Dx() != newWidth || + window.metrics.bounds.Dy() != newHeight + window.updateBounds ( + configureEvent.X, configureEvent.Y, + configureEvent.Width, configureEvent.Height) + if sizeChanged { configureEvent = window.compressConfigureNotify(configureEvent) - window.metrics.width = int(configureEvent.Width) - window.metrics.height = int(configureEvent.Height) + window.updateBounds ( + configureEvent.X, configureEvent.Y, + configureEvent.Width, configureEvent.Height) window.reallocateCanvas() window.resizeChildToFit() diff --git a/backends/x/window.go b/backends/x/window.go index 10f26e8..315d33f 100644 --- a/backends/x/window.go +++ b/backends/x/window.go @@ -37,36 +37,37 @@ type window struct { selectionClaim *selectionClaim metrics struct { - width int - height int + bounds image.Rectangle } } func (backend *Backend) NewWindow ( - width, height int, + bounds image.Rectangle, ) ( output tomo.MainWindow, err error, ) { if backend == nil { panic("nil backend") } - window, err := backend.newWindow(width, height) + window, err := backend.newWindow(bounds) output = mainWindow { window } return output, err } func (backend *Backend) newWindow ( - width, height int, + bounds image.Rectangle, ) ( output *window, err error, ) { + // TODO: take position flag into account + window := &window { backend: backend } window.xWindow, err = xwindow.Generate(backend.connection) if err != nil { return } window.xWindow.Create ( backend.connection.RootWin(), - 0, 0, width, height, 0) + bounds.Min.X, bounds.Min.Y, bounds.Dx(), bounds.Dy(), 0) err = window.xWindow.Listen ( xproto.EventMaskExposure, xproto.EventMaskStructureNotify, @@ -108,8 +109,7 @@ func (backend *Backend) newWindow ( window.SetTheme(backend.theme) window.SetConfig(backend.config) - window.metrics.width = width - window.metrics.height = height + window.metrics.bounds = bounds window.childMinimumSizeChangeCallback(8, 8) window.reallocateCanvas() @@ -249,8 +249,8 @@ func (window *window) SetIcon (sizes []image.Image) { wmIcons) } -func (window *window) NewModal (width, height int) (tomo.Window, error) { - modal, err := window.backend.newWindow(width, height) +func (window *window) NewModal (bounds image.Rectangle) (tomo.Window, error) { + modal, err := window.backend.newWindow(bounds.Add(window.metrics.bounds.Min)) icccm.WmTransientForSet ( window.backend.connection, modal.xWindow.Id, @@ -265,8 +265,8 @@ func (window *window) NewModal (width, height int) (tomo.Window, error) { return modal, err } -func (window mainWindow) NewPanel (width, height int) (tomo.Window, error) { - panel, err := window.backend.newWindow(width, height) +func (window mainWindow) NewPanel (bounds image.Rectangle) (tomo.Window, error) { + panel, err := window.backend.newWindow(bounds.Add(window.metrics.bounds.Min)) if err != nil { return nil, err } panel.setClientLeader(window.window) window.setClientLeader(window.window) @@ -379,7 +379,9 @@ func (window *window) SetConfig (config tomo.Config) { } func (window *window) reallocateCanvas () { - window.canvas.Reallocate(window.metrics.width, window.metrics.height) + window.canvas.Reallocate ( + window.metrics.bounds.Dx(), + window.metrics.bounds.Dy()) previousWidth, previousHeight := 0, 0 if window.xCanvas != nil { @@ -387,8 +389,8 @@ func (window *window) reallocateCanvas () { previousHeight = window.xCanvas.Bounds().Dy() } - newWidth := window.metrics.width - newHeight := window.metrics.height + newWidth := window.metrics.bounds.Dx() + newHeight := window.metrics.bounds.Dy() larger := newWidth > previousWidth || newHeight > previousHeight smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2 @@ -461,12 +463,12 @@ func (window *window) childMinimumSizeChangeCallback (width, height int) (resize MinWidth: uint(width), MinHeight: uint(height), }) - newWidth := window.metrics.width - newHeight := window.metrics.height + newWidth := window.metrics.bounds.Dx() + newHeight := window.metrics.bounds.Dy() if newWidth < width { newWidth = width } if newHeight < height { newHeight = height } - if newWidth != window.metrics.width || - newHeight != window.metrics.height { + if newWidth != window.metrics.bounds.Dx() || + newHeight != window.metrics.bounds.Dy() { window.xWindow.Resize(newWidth, newHeight) return true } diff --git a/tomo.go b/tomo.go index 15e8632..7273c5c 100644 --- a/tomo.go +++ b/tomo.go @@ -1,5 +1,7 @@ package tomo +import "image" + var backend Backend // Run initializes a backend, calls the callback function, and begins the event @@ -30,9 +32,9 @@ func Do (callback func ()) { // NewWindow creates a new window using the current backend, and returns it as a // MainWindow. If the window could not be created, an error is returned // explaining why. -func NewWindow (width, height int) (window MainWindow, err error) { +func NewWindow (bounds image.Rectangle) (window MainWindow, err error) { assertBackend() - return backend.NewWindow(width, height) + return backend.NewWindow(bounds) } // SetTheme sets the theme of all open windows. diff --git a/window.go b/window.go index 2400c3f..012fe74 100644 --- a/window.go +++ b/window.go @@ -3,6 +3,10 @@ package tomo import "image" import "git.tebibyte.media/sashakoshka/tomo/data" +// TODO: add support for the icon window because imagine if we allowed +// applications to display live updating information readouts on their icons. +// that would be baller + // Window represents a top-level container generated by the currently running // backend. It can contain a single element. It is hidden by default, and must // be explicitly shown with the Show() method. @@ -30,8 +34,10 @@ type Window interface { // NewModal creates a new modal dialog window. The resulting window will // inherit this window's application name and icon, but these can be - // manually overridden. - NewModal (width, height int) (window Window, err error) + // manually overridden. The modal will be placed relative to the parent + // window, but this position may be overridden by the backend or + // operating system. + NewModal (bounds image.Rectangle) (Window, error) // Copy puts data into the clipboard. Copy (data.Data) @@ -61,8 +67,9 @@ type MainWindow interface { Window // NewPanel creates a panel window that is semantically tied to this - // window. This is intended to be used for utility windows, tool bars, - // torn-off menus, etc. The resulting window will inherit this window's - // application name and icon, but these can be manually overridden. - NewPanel (width, height int) (window Window, err error) + // window, positioned relative to it. This is intended to be used for + // utility windows, tool bars, torn-off menus, etc. The resulting window + // will inherit this window's application name and icon, but these can + // be manually overridden. + NewPanel (bounds image.Rectangle) (Window, error) }