Added support for relative window positioning
This commit is contained in:
parent
8abb45e77a
commit
6db5901247
11
backend.go
11
backend.go
@ -1,5 +1,6 @@
|
|||||||
package tomo
|
package tomo
|
||||||
|
|
||||||
|
import "image"
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
||||||
// Backend represents a connection to a display server, or something similar.
|
// Backend represents a connection to a display server, or something similar.
|
||||||
@ -7,7 +8,7 @@ import "errors"
|
|||||||
type Backend interface {
|
type Backend interface {
|
||||||
// Run runs the backend's event loop. It must block until the backend
|
// Run runs the backend's event loop. It must block until the backend
|
||||||
// experiences a fatal error, or Stop() is called.
|
// experiences a fatal error, or Stop() is called.
|
||||||
Run () (err error)
|
Run () error
|
||||||
|
|
||||||
// Stop stops the backend's event loop.
|
// Stop stops the backend's event loop.
|
||||||
Stop ()
|
Stop ()
|
||||||
@ -16,10 +17,10 @@ type Backend interface {
|
|||||||
// possible. This method must be safe to call from other threads.
|
// possible. This method must be safe to call from other threads.
|
||||||
Do (callback func ())
|
Do (callback func ())
|
||||||
|
|
||||||
// NewWindow creates a new window with the specified width and height,
|
// NewWindow creates a new window within the specified bounding
|
||||||
// and returns a struct representing it that fulfills the MainWindow
|
// rectangle. The position on screen may be overridden by the backend or
|
||||||
// interface.
|
// operating system.
|
||||||
NewWindow (width, height int) (window MainWindow, err error)
|
NewWindow (bounds image.Rectangle) (MainWindow, error)
|
||||||
|
|
||||||
// SetTheme sets the theme of all open windows.
|
// SetTheme sets the theme of all open windows.
|
||||||
SetTheme (Theme)
|
SetTheme (Theme)
|
||||||
|
@ -52,6 +52,12 @@ func (window *window) handleExpose (
|
|||||||
window.pushRegion(region)
|
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 (
|
func (window *window) handleConfigureNotify (
|
||||||
connection *xgbutil.XUtil,
|
connection *xgbutil.XUtil,
|
||||||
event xevent.ConfigureNotifyEvent,
|
event xevent.ConfigureNotifyEvent,
|
||||||
@ -63,15 +69,18 @@ func (window *window) handleConfigureNotify (
|
|||||||
newWidth := int(configureEvent.Width)
|
newWidth := int(configureEvent.Width)
|
||||||
newHeight := int(configureEvent.Height)
|
newHeight := int(configureEvent.Height)
|
||||||
sizeChanged :=
|
sizeChanged :=
|
||||||
window.metrics.width != newWidth ||
|
window.metrics.bounds.Dx() != newWidth ||
|
||||||
window.metrics.height != newHeight
|
window.metrics.bounds.Dy() != newHeight
|
||||||
window.metrics.width = newWidth
|
window.updateBounds (
|
||||||
window.metrics.height = newHeight
|
configureEvent.X, configureEvent.Y,
|
||||||
|
configureEvent.Width, configureEvent.Height)
|
||||||
|
|
||||||
|
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
configureEvent = window.compressConfigureNotify(configureEvent)
|
configureEvent = window.compressConfigureNotify(configureEvent)
|
||||||
window.metrics.width = int(configureEvent.Width)
|
window.updateBounds (
|
||||||
window.metrics.height = int(configureEvent.Height)
|
configureEvent.X, configureEvent.Y,
|
||||||
|
configureEvent.Width, configureEvent.Height)
|
||||||
window.reallocateCanvas()
|
window.reallocateCanvas()
|
||||||
window.resizeChildToFit()
|
window.resizeChildToFit()
|
||||||
|
|
||||||
|
@ -37,36 +37,37 @@ type window struct {
|
|||||||
selectionClaim *selectionClaim
|
selectionClaim *selectionClaim
|
||||||
|
|
||||||
metrics struct {
|
metrics struct {
|
||||||
width int
|
bounds image.Rectangle
|
||||||
height int
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (backend *Backend) NewWindow (
|
func (backend *Backend) NewWindow (
|
||||||
width, height int,
|
bounds image.Rectangle,
|
||||||
) (
|
) (
|
||||||
output tomo.MainWindow,
|
output tomo.MainWindow,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
if backend == nil { panic("nil backend") }
|
if backend == nil { panic("nil backend") }
|
||||||
window, err := backend.newWindow(width, height)
|
window, err := backend.newWindow(bounds)
|
||||||
output = mainWindow { window }
|
output = mainWindow { window }
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (backend *Backend) newWindow (
|
func (backend *Backend) newWindow (
|
||||||
width, height int,
|
bounds image.Rectangle,
|
||||||
) (
|
) (
|
||||||
output *window,
|
output *window,
|
||||||
err error,
|
err error,
|
||||||
) {
|
) {
|
||||||
|
// TODO: take position flag into account
|
||||||
|
|
||||||
window := &window { backend: backend }
|
window := &window { backend: backend }
|
||||||
|
|
||||||
window.xWindow, err = xwindow.Generate(backend.connection)
|
window.xWindow, err = xwindow.Generate(backend.connection)
|
||||||
if err != nil { return }
|
if err != nil { return }
|
||||||
window.xWindow.Create (
|
window.xWindow.Create (
|
||||||
backend.connection.RootWin(),
|
backend.connection.RootWin(),
|
||||||
0, 0, width, height, 0)
|
bounds.Min.X, bounds.Min.Y, bounds.Dx(), bounds.Dy(), 0)
|
||||||
err = window.xWindow.Listen (
|
err = window.xWindow.Listen (
|
||||||
xproto.EventMaskExposure,
|
xproto.EventMaskExposure,
|
||||||
xproto.EventMaskStructureNotify,
|
xproto.EventMaskStructureNotify,
|
||||||
@ -108,8 +109,7 @@ func (backend *Backend) newWindow (
|
|||||||
window.SetTheme(backend.theme)
|
window.SetTheme(backend.theme)
|
||||||
window.SetConfig(backend.config)
|
window.SetConfig(backend.config)
|
||||||
|
|
||||||
window.metrics.width = width
|
window.metrics.bounds = bounds
|
||||||
window.metrics.height = height
|
|
||||||
window.childMinimumSizeChangeCallback(8, 8)
|
window.childMinimumSizeChangeCallback(8, 8)
|
||||||
|
|
||||||
window.reallocateCanvas()
|
window.reallocateCanvas()
|
||||||
@ -249,8 +249,8 @@ func (window *window) SetIcon (sizes []image.Image) {
|
|||||||
wmIcons)
|
wmIcons)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) NewModal (width, height int) (tomo.Window, error) {
|
func (window *window) NewModal (bounds image.Rectangle) (tomo.Window, error) {
|
||||||
modal, err := window.backend.newWindow(width, height)
|
modal, err := window.backend.newWindow(bounds.Add(window.metrics.bounds.Min))
|
||||||
icccm.WmTransientForSet (
|
icccm.WmTransientForSet (
|
||||||
window.backend.connection,
|
window.backend.connection,
|
||||||
modal.xWindow.Id,
|
modal.xWindow.Id,
|
||||||
@ -265,8 +265,8 @@ func (window *window) NewModal (width, height int) (tomo.Window, error) {
|
|||||||
return modal, err
|
return modal, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (window mainWindow) NewPanel (width, height int) (tomo.Window, error) {
|
func (window mainWindow) NewPanel (bounds image.Rectangle) (tomo.Window, error) {
|
||||||
panel, err := window.backend.newWindow(width, height)
|
panel, err := window.backend.newWindow(bounds.Add(window.metrics.bounds.Min))
|
||||||
if err != nil { return nil, err }
|
if err != nil { return nil, err }
|
||||||
panel.setClientLeader(window.window)
|
panel.setClientLeader(window.window)
|
||||||
window.setClientLeader(window.window)
|
window.setClientLeader(window.window)
|
||||||
@ -379,7 +379,9 @@ func (window *window) SetConfig (config tomo.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (window *window) reallocateCanvas () {
|
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
|
previousWidth, previousHeight := 0, 0
|
||||||
if window.xCanvas != nil {
|
if window.xCanvas != nil {
|
||||||
@ -387,8 +389,8 @@ func (window *window) reallocateCanvas () {
|
|||||||
previousHeight = window.xCanvas.Bounds().Dy()
|
previousHeight = window.xCanvas.Bounds().Dy()
|
||||||
}
|
}
|
||||||
|
|
||||||
newWidth := window.metrics.width
|
newWidth := window.metrics.bounds.Dx()
|
||||||
newHeight := window.metrics.height
|
newHeight := window.metrics.bounds.Dy()
|
||||||
larger := newWidth > previousWidth || newHeight > previousHeight
|
larger := newWidth > previousWidth || newHeight > previousHeight
|
||||||
smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2
|
smaller := newWidth < previousWidth / 2 || newHeight < previousHeight / 2
|
||||||
|
|
||||||
@ -461,12 +463,12 @@ func (window *window) childMinimumSizeChangeCallback (width, height int) (resize
|
|||||||
MinWidth: uint(width),
|
MinWidth: uint(width),
|
||||||
MinHeight: uint(height),
|
MinHeight: uint(height),
|
||||||
})
|
})
|
||||||
newWidth := window.metrics.width
|
newWidth := window.metrics.bounds.Dx()
|
||||||
newHeight := window.metrics.height
|
newHeight := window.metrics.bounds.Dy()
|
||||||
if newWidth < width { newWidth = width }
|
if newWidth < width { newWidth = width }
|
||||||
if newHeight < height { newHeight = height }
|
if newHeight < height { newHeight = height }
|
||||||
if newWidth != window.metrics.width ||
|
if newWidth != window.metrics.bounds.Dx() ||
|
||||||
newHeight != window.metrics.height {
|
newHeight != window.metrics.bounds.Dy() {
|
||||||
window.xWindow.Resize(newWidth, newHeight)
|
window.xWindow.Resize(newWidth, newHeight)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
6
tomo.go
6
tomo.go
@ -1,5 +1,7 @@
|
|||||||
package tomo
|
package tomo
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
|
||||||
var backend Backend
|
var backend Backend
|
||||||
|
|
||||||
// Run initializes a backend, calls the callback function, and begins the event
|
// 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
|
// 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
|
// MainWindow. If the window could not be created, an error is returned
|
||||||
// explaining why.
|
// explaining why.
|
||||||
func NewWindow (width, height int) (window MainWindow, err error) {
|
func NewWindow (bounds image.Rectangle) (window MainWindow, err error) {
|
||||||
assertBackend()
|
assertBackend()
|
||||||
return backend.NewWindow(width, height)
|
return backend.NewWindow(bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTheme sets the theme of all open windows.
|
// SetTheme sets the theme of all open windows.
|
||||||
|
19
window.go
19
window.go
@ -3,6 +3,10 @@ package tomo
|
|||||||
import "image"
|
import "image"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/data"
|
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
|
// 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
|
// backend. It can contain a single element. It is hidden by default, and must
|
||||||
// be explicitly shown with the Show() method.
|
// 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
|
// NewModal creates a new modal dialog window. The resulting window will
|
||||||
// inherit this window's application name and icon, but these can be
|
// inherit this window's application name and icon, but these can be
|
||||||
// manually overridden.
|
// manually overridden. The modal will be placed relative to the parent
|
||||||
NewModal (width, height int) (window Window, err error)
|
// 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 puts data into the clipboard.
|
||||||
Copy (data.Data)
|
Copy (data.Data)
|
||||||
@ -61,8 +67,9 @@ type MainWindow interface {
|
|||||||
Window
|
Window
|
||||||
|
|
||||||
// NewPanel creates a panel window that is semantically tied to this
|
// NewPanel creates a panel window that is semantically tied to this
|
||||||
// window. This is intended to be used for utility windows, tool bars,
|
// window, positioned relative to it. This is intended to be used for
|
||||||
// torn-off menus, etc. The resulting window will inherit this window's
|
// utility windows, tool bars, torn-off menus, etc. The resulting window
|
||||||
// application name and icon, but these can be manually overridden.
|
// will inherit this window's application name and icon, but these can
|
||||||
NewPanel (width, height int) (window Window, err error)
|
// be manually overridden.
|
||||||
|
NewPanel (bounds image.Rectangle) (Window, error)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user