Class rtk.Window

Class Hierarchy

An OS-native window that is to be the root ancestor of all other widgets.

rtk.Window implements the rtk.Container interface and behaves like a regular widget, except with certain behaviors adapted for OS windows. For example, setting geometry attributes (x, y, w, h), which normally affects widget layout, here moves and resizes the actual window. Similarly, bg sets the window's overall background color.

Windows can be either docked or undocked (floating), which is controlled by the docked attribute, and which can be changed dynamically. Most of rtk.Window's attributes don't apply to docked windows: geometry (x, y, w, h), borderless, opacity, etc. are only respected when the window is undocked.

Moreover, a considerable amount of functionality (particularly the ability to modify the window after it has opened) depends on the js_ReaScriptAPI extension. rtk itself doesn't require the extension however, so just be aware when you use some functionality that depends on it (which will be clearly explained in the APIs below). You will need to be cognizant of whether you want to impose the js_ReaScriptAPI requirement on users of your script based on the dependent functionality you use.

Due to REAPER's design there can only be one rtk.Window per script.

-- Creates a window that defaults to 400x200 and is undocked (because the
-- docked attribute defaults to false).
local window = rtk.Window{w=400, h=200}
-- This works exactly like any other rtk.Container: this will add the
-- text widget and center it within the window.
window:add(rtk.Text{'Hello world!'}, {halign='center', valign='center'})
-- Now open the window which itself is top-center on the screen.
window:open{halign='center', valign='top'}

Internal vs External Geometry Changes

The geometry of the window is controlled by the x, y, w, h, minw, maxw, minh, and maxh attributes. These attributes influence how the window should be initially positioned and sized, but of course changes to the window's position and size can come from external causes, such as the user resizing the window via the OS window border, or REAPER modifying the dimensions of the window when it's docked or undocked.

When a geometry change originates from an external cause like this, the above attributes become passive and don't force any changes back onto the window's geometry. The x, y, w, and h attributes are automatically updated to reflect this externally caused change. However, minw, maxw, minh, and maxh are left the way you set them.

After the window is opened, the minw, maxw, minh, and maxh only exert any influence on the window geometry if any of the eight geometry-related attributes are changed programmatically, such as via attr(). Programmatically setting any of these attributes will cause all the min/max constraints to be reevaluated and enforced at that time, but thereafter become inert again when it comes to externally caused changes to position or size.

Closing the Window

Out of the box, undocked (i.e. floating) windows will be closed when the user hits the escape key while the window is focused. Or, to be more precise, when there is an unhandled rtk.Event.KEY event with an ESCAPE keycode and docked is false. You can override this by explicitly handling this event via onkeypresspost():

local window = rtk.Window()
window.onkeypresspost = function(self, event)
    if not event.handled and event.keycode == rtk.keycodes.ESCAPE and
       not window.docked then
        -- Prevent default behavior of escape key closing the window
        -- by marking the event as handled.
        event:set_handled(self)
    end
end

The window will of course also close when the user clicks on the window's OS-native close button (for non-borderless windows), or programmatically when you call rtk.Window:close().

Dock Constants

Used with the dock attribute, where lowercase strings of these constants can be used for convenience (e.g. 'bottom' instead of rtk.Window.DOCK_BOTTOM). These strings are automatically converted to the appropriate numeric constants.

rtk.Window.DOCK_BOTTOM

'bottom'

Find the first docker that is attached to the bottom of the main window.

rtk.Window.DOCK_LEFT

'left'

Find the first docker that is attached to the left of the main window.

rtk.Window.DOCK_TOP

'top'

Find the first docker that is attached to the top of the main window.

rtk.Window.DOCK_RIGHT

'right'

Find the first docker that is attached to the right of the main window.

rtk.Window.DOCK_FLOATING

'floating'

Find the first docker that that is not attached to the main window.

Class API

Synopsis

Attributes
x number

read/write

The x screen coordinate of the window when undocked (default nil)

y number

read/write

Like x but for the y screen coordinate (default nil)

w number or nil

read/write

The current client width of the window (default nil)

h number or nil

read/write

Like w but for the window height (default nil)

minw number

read/write

Minimum allowed width for the window when undocked (default 100)

minh number

read/write

Like minw, but is the minimum height allowed for the window when undocked (default 30)

maxw number or nil

read/write

Maximum width allowed for the undocked window when w is nil to autosize the width based on the child widgets (default 800)

maxh number or nil

read/write

Like maxw but is the maximum height allowed for the undocked window when h is nil (default 600)

visible boolean

read/write

Sets the visibility of the detached window when undocked (default true)

docked boolean

read/write

True if the window is docked and false otherwise (default false)

dock number

read/write

The id of the docker to which this window is (or, when set, will become) attached when docked is true (default 'right')

pinned boolean

read/write

If true, undocked windows will be pinned (i.e. they are always on top) and if false the window ordering works as usual (default false)

borderless boolean

read/write

If true, undocked windows will not show the OS-native window frame (default false)

title string

read/write

The title of the window shown in the OS-native window frame (default "REAPER Application")

opacity number

read/write

The opacity of the full window at the OS level, which affects how the window is composited by the OS (default 1.0)

resizable bool

read/write

Controls whether undocked windows will be provided a means of resizing the window (default true)

hwnd userdata

read-only

The handle of the rtk.Window which is set once open() is called

in_window boolean

read-only

True if the mouse is positioned within the rtk.Window and false otherwise

is_focused boolean

read-only

True if the rtk.Window currently holds keyboard focus

running boolean

read-only

True if the window's main event loop is running

Methods
rtk.Window()

Create a new window with the given attributes

focus()

Focuses the window so it receives subsequent keyboard events

open()

Opens the window and begins the main event loop

close()

Closes the window and will end the application unless there are active user-managed deferred calls in flight to prevent REAPER from terminating us

queue_blit()

Queues a full blit of the window from its backing store based on previously drawn state upon next update

queue_mouse_refresh()

Queues a simulated mousemove event on next update to cause widgets to refresh the mouse hover state

request_mouse_cursor()

Called within an event handler to ask the window to set the mouse cursor

clear()

Clears the window's backing store to the window's background color (or the theme default)

get_normalized_y()

Returns a normalized version of a screen-level y coordinate

Attributes

rtk.Window.x number read/write

The x screen coordinate of the window when undocked (default nil). When this attribute is set the window will be moved only if it's undocked, but when an undocked window is moved by the user, this attribute is also updated to reflect the current screen position.

Setting after open() is called requires the js_ReaScriptAPI extension.

If this attribute is nil (as is default) at the time open() is called, the window will be automatically horizontally centered on the primary display and this attribute will be updated to reflect the new actual screen x coordinate. If you wish to center the window on a display other than primary, then you'll need to reflect the display position in the x and y attributes and pass the center alignment option to open() instead.

Tip: you can call move() to set both x and y at the same time.

Note that while x reflects the current screen position of the window, the calculated version of this attribute is always 0. This is because the calculated value is used as the offset for drawing widgets inside the window. If you want to offset the inner contents, you can use lpadding or tpadding instead.

rtk.Window.y number read/write

Like x but for the y screen coordinate (default nil).

As with x, if this attribute is nil (as is default) at the time open() is called, the window will be automatically vertically centered on the system's primary display and this attribute will be updated to reflect the new actual screen y coordinate.

Whereas on Windows and Linux the y coordinate is relative the top of the screen (so y=0 refers to the top edge of the screen), on Mac this is inverted such that y=0 refers to the bottom edge of the window positioned at the bottom edge of the screen.

If you need a consistent representation of the y coordinate, you can use rtk.Window:get_normalized_y().

rtk.Window.w number or nil read/write

The current client width of the window (default nil). Client in this context means the inner contents of the window without the OS native window frame.

If nil (default), the window's width will be automatically sized based on its content (i.e. its child widgets). maxw and minw are respected and can be used to constrain the upper and lower bounds of the width. Once the window is opened, this attribute will be automatically updated to reflect the window's actual width, and will continue to be updated if the window is resized due to outside influence (e.g. the user resizing via the OS's window frame).

If the window is undocked, then this attribute is also settable. If set before open() then it defines the initial width of the window if undocked, but has no effect on the width of docked windows (as REAPER doesn't allow that).

Setting a value after open() is possible but only when the window is undocked, and this requires the js_ReaScriptAPI extension. Setting this attribute to nil will "shrinkwrap" the window's width to its current content size, as described above.

On Macs with Retina displays, the OS window size is actually half the size of the internal graphics buffer. The calculated versions of w and h reflect this full (double) size, but w and h themselves will be half the size. This ratio is reflected by rtk.scale.framebuffer.

Tip: you can call resize() to set both w and h at the same time.

rtk.Window.h number or nil read/write

Like w but for the window height (default nil).

As with w, nil values will automatically fit the window's height to its contents. And minh and maxh are respected.

minh is respected and the window will not be allowed a smaller height when set via attr() or when borderless is true.

rtk.Window.minw number read/write

Minimum allowed width for the window when undocked (default 100).

This attribute ensures that w is not less than this value on initial open(), or when the window's width is set via attr(), or when resizing when borderless is true.

However for bordered windows, the OS may allow the user to set a smaller width for the window than minw, and so in this case it is possible for w to be smaller than minw. This smaller size will be reflected in the w attribute, which always tracks the current window width.

As with w and h, the calculated value for minw is multiplied by rtk.scale.framebuffer. See w for more details.

rtk.Window.minh number read/write

Like minw, but is the minimum height allowed for the window when undocked (default 30).

rtk.Window.maxw number or nil read/write

Maximum width allowed for the undocked window when w is nil to autosize the width based on the child widgets (default 800).

Unlike minw, this attribute does not affect the window's maximum size when setting the window's width programmatically or when resizing. It's only used to constrain the automatic sizing ("shrinkwrap") behavior when the w attribute is nil.

This can be used to prevent widgets with the fillw container cell attribute set to true (which behave greedily and consume the maximum allowed width) from causing the window to become unmanageably large.

If this attribute is set to nil, then the display's width will be used as the upper bound. In multi-monitor systems, the display that contains point based on the x and y attributes is used.

As with w and h, the calculated value for maxw is multiplied by rtk.scale.framebuffer. See w for more details.

rtk.Window.maxh number or nil read/write

Like maxw but is the maximum height allowed for the undocked window when h is nil (default 600).

rtk.Window.visible boolean read/write

Sets the visibility of the detached window when undocked (default true). This atribute is ignored when docked.

This requires the js_ReaScriptAPI extension to be available in order to work, otherwise setting this is a no-op. This attribute can be set directly, or you can use the hide(), show(), or toggle() convenience methods.

rtk.Window.docked boolean read/write

True if the window is docked and false otherwise (default false). This is updated to reflect externally-caused changes to the dock state, but can also be set to dock or undock the window, and when set to true is is combined with dock to decide where to dock the window.

rtk.Window.dock number read/write

The id of the docker to which this window is (or, when set, will become) attached when docked is true (default 'right'). This is updated to reflect the current docker id when the user moves the window between dockers, but can also be set to cause the window to programmatically move between dockers.

The value is either a numeric docker id to target a specific docker, or one of the dock position constants which will search for a docker that's attached to the main window in the given position. If no docker can be found at that position, then then the first docker window will be used as a last resort.

rtk.Window.pinned boolean read/write

If true, undocked windows will be pinned (i.e. they are always on top) and if false the window ordering works as usual (default false). This attribute is ignored when docked.

This requires js_ReaScriptAPI extension to work and without it is always false.

UI for toggling the pin

Note that due to a limitation with js_ReaScriptAPI, the pin button will not be visible on the window title bar. It's up to you to provide some facility for the user to pin, such as a toolbar button that toggles this attribute.

(The js_ReaScriptAPI limitation is that it provides no means of removing the pin after it's attached, and therefore conflicts with the borderless attribute. You are free to call reaper.JS_Window_AttachTopmostPin() explicitly yourself, passing it the window's hwnd, just be aware that the pin will persist and render oddly if borderless is subsequently set to true, at least on Windows. Also, this will only work on 64-bit builds of REAPER.)

rtk.Window.borderless boolean read/write

If true, undocked windows will not show the OS-native window frame (default false). This attribute is ignored when docked.

When borderless, a resize grip will be shown on the bottom right corner of the window to allow the window to be resizable, and also if the user clicks and drags along the top edge of the window it can be moved.

This requires the js_ReaScriptAPI extension and without it is always false.

Tip: if you put widgets along the top edge of the window (e.g. a row of buttons acting as a toolbar) you can prevent click-dragging of these widgets from also moving the window by attaching to those widgets a custom ondragstart handler that returns false.

rtk.Window.title string read/write

The title of the window shown in the OS-native window frame (default "REAPER Application"). This attribute is ignored when docked.

Setting after open() is called requires the js_ReaScriptAPI extension, otherwise the change is ignored.

rtk.Window.opacity number read/write

The opacity of the full window at the OS level, which affects how the window is composited by the OS (default 1.0). This attribute is ignored when docked.

This is distinct from alpha, which affects how all widgets within the window are blended on top of the background color, because opacity can make the entire window translucent, including the window frame (assuming borderless is false).

Requires the js_ReaScriptAPI extension, otherwise this attribute is entirely ignored.

rtk.Window.resizable bool read/write

Controls whether undocked windows will be provided a means of resizing the window (default true). For borderless windows the resize grip on the bottom right corner is hidden, while for normal bordered windows the normal resize zones along the window frame and and minimize/maximize/restore buttons on the window title bar will not be available. This does not prevent programmatic resizing, or resizing through external means (such as AutoHotkey), and in those cases onresize() will still be called.

Requires the js_ReaScriptAPI extension, otherwise this attribute is entirely ignored.

rtk.Window.hwnd userdata read-only

The handle of the rtk.Window which is set once open() is called.

This requires the js_ReaScriptAPI extension and is nil if it's not installed.

rtk.Window.in_window boolean read-only

True if the mouse is positioned within the rtk.Window and false otherwise.

Occlusion detection

Detecting window occlusion (i.e. where another window is above the rtk.Window) requires the js_ReaScriptAPI extension. When the extension is available, if the mouse cursor is positioned in the occluding window's region, in_window will be false. However when the extension is not available, in_window will always be true when the cursor is within the window geometry, even if there are other windows placed above rtk.Window.

rtk.Window.is_focused boolean read-only

True if the rtk.Window currently holds keyboard focus.

This requires the js_ReaScriptAPI extension and if it's not installed will always be true.

rtk.Window.running boolean read-only

True if the window's main event loop is running. close() will set this to false.

Methods

rtk.Window(attrs, ...)

Create a new window with the given attributes.

rtk.Window:focus()

Focuses the window so it receives subsequent keyboard events.

This requires the js_ReaScriptAPI extension to be available in order to work. Note that likewise onfocus() and onblur() event handlers at the rtk.Window level require js_ReaScriptAPI as well.

Return Values
(bool)

returns true the the js_ReaScriptAPI extension was available, and false otherwise.

rtk.Window:open(options)

Opens the window and begins the main event loop. Once called, the application will continue running until close() is called.

The options parameter is an optional table of fields that allows you to influence the initial placement of undocked windows beyond the standard x and y attributes. The following options are currently supported:

Field Values Description
halign 'left', 'center', 'right' Controls horizontal alignment of the window. If nil, the x attribute controls the x coordinate of undocked windows, otherwise x is used to determine on which monitor the window should be horizontally aligned.
valign 'top', 'center', 'bottom' Controls vertical alignment of the window . If nil, the y attribute controls the y coordinate of undocked windows, otherwise y is used to determine on which monitor the window should be vertically aligned.
align 'center'` Convenience field to center both horizontally and vertically, which is exactly equivalent to setting both halign and valign fields to center.
constrain true, false If true, the window's initial geometry will be modified to ensure the window fits within the current display. On multi-display systems, "current display" is the display which contains most of the window's rectangle.
local window = rtk.Window()
window:open{align='center'}
Options are for one-time placement only

The fields in the options table only apply to the initial placement of the window during open, and have no further influence after that point. Notably, the halign/valign options are unrelated to the halign and valign widget attributes: the alignment options here influence position of the undocked OS-native window, while the halign and valign widget attributes affect the alignment of child widgets placed within the rtk.Window.

Parameters
options (table or nil)

an optional table of placement attributes

rtk.Window:close()

Closes the window and will end the application unless there are active user-managed deferred calls in flight to prevent REAPER from terminating us.

It is possible to call open() again after the window is closed (assuming we haven't yet terminated obviously).

rtk.Window:queue_blit()

Queues a full blit of the window from its backing store based on previously drawn state upon next update.

This normally should never need to be called -- rtk internally understands when it needs to blit -- but if you're doing some low level window trickery voodoo underneath rtk (e.g. by using the js_ReaScriptAPI directly), you may find it necessary to invoke this.

rtk.Window:queue_mouse_refresh()

Queues a simulated mousemove event on next update to cause widgets to refresh the mouse hover state.

This normally never needs to be called, but this method is needed to handle a very precise edge case:

  1. The application is blocked (e.g. because an rtk.NativeMenu is open)
  2. The user moves the mouse to hover over another widget
  3. The application becomes unblocked while the mouse button is pressed (e.g. because the user closed the popup menu by clicking the mouse button) In this case, once the window's update loop becomes unblocked, the above condition actually looks like a drag event, and the new widget's hover state doesn't get updated.

Calling this function will force the injection of a simulated rtk.Event.MOUSEMOVE event without any buttons pressed which causes the new widget under the mouse to update its hover appearance.

If you find you need to call this method when the application isn't blocked, this should be considered a bug and reported.

rtk.Window:request_mouse_cursor(cursor, force)

Called within an event handler to ask the window to set the mouse cursor.

The word "ask" is used because this works on a first-come-first-served basis: whoever is first to call this function on each update cycle wins the race. At least unless the force parameter is true, but this should only ever be needed in very rare cases (such as touch-scrolling).

If you want a particular cursor when mousing over a widget, use the cursor attribute rather than calling this function. Setting cursor on an rtk.Window will result in it being the default cursor (when no widget has requested a different cursor).

One use case for calling this function is switching cursors during a widget drag operation:

local img = container:add(rtk.ImageBox{icon='drag-handle'})
img.ondragstart = function(self, event)
    -- Accept drags for this widget.
    return true
end
img.ondragmousemove = function(self, event, dragarg)
    -- Set the mouse cursor while dragging.
    self.window:request_mouse_cursor(rtk.mouse.cursors.REAPER_DRAGDROP_COPY)
end

The above example requires either REAPER 6.24 or later (due to this bug) or the presence of the js_ReaScriptAPI extension (which enables us to work around the bug).

Parameters
cursor (cursorconst)

one of the mouse cursor constants

force (bool)

if true, force-replaces the cursor even if it was already set

Return Values
(bool)

true if the cursor was set, false if a cursor was already during this update cycle.

rtk.Window:clear()

Clears the window's backing store to the window's background color (or the theme default).

This is a low-level function that normally never needs to be called, but could be used in certain cases within an ondraw handler.

rtk.Window:get_normalized_y()

Returns a normalized version of a screen-level y coordinate.

On Mac, window y coordinates are relative to the bottom of the screen, while on Windows and Linux they are relative to the top. In other words, on Mac, y=0 represents the bottom of the screen, while on other platforms it's the top of the screen.

This method returns a normalized version of the y attribute so that it's always relative to the top of the screen, regardless of platform.

Return Values
(number or nil)

the normalized y coordinate

Event Handlers

See also handlers for rtk.Widget.

Synopsis

Methods
onupdate()

Called on each update cycle before any reflow (if applicable), animation processing, event handling, or drawing takes place

onreflow()

Called after one or more widgets have been reflowed (i.e. laid out) within the window

onmove()

Called when the window changes position

onresize()

Called when the window changes size

ondock()

Called when the window is docked or undocked, which includes when the window is first opened

onclose()

Called after the window is (gracefully) closed by clicking the OS-native close button or when close() is called

onkeypresspre()

Called when a key event occurs but before it is dispatched to any widgets

onkeypresspost()

Called when a key event occurs after all widgets have been given a chance to handle the event

rtk.Window:onupdate()

Called on each update cycle before any reflow (if applicable), animation processing, event handling, or drawing takes place. Consequently, any actions performed by the handler that affect the interface will be reflected immediately.

Return Values
(bool or nil)

if false, the normal update processing will be skipped.

rtk.Window:onreflow(widgets)

Called after one or more widgets have been reflowed (i.e. laid out) within the window.

Unlike the base class method, onreflow handlers attached to rtk.Windows receive an optional list of widgets. If a full reflow has occurred (i.e. every visible widget was reflowed), then widgets is nil, but if specific widgets were explicitly reflowed this cycle then widgets is an array containing the widgets.

Parameters
widgets (table or nil)

an array of widgets that were the subset of widgets being reflowed this cycle, or if full reflow occurred then nil

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:onmove(lastx, lasty)

Called when the window changes position.

The x and y attributes reflect the current position, while the lastx and lasty parameters hold the previous x and y values before the move occurred.

Parameters
lastx (number)

the last x value of the window before the move

lasty (number)

the last y value of the window before the move

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:onresize(lastw, lasth)

Called when the window changes size.

The w and h attributes reflect the current size, while the lastw and lasth parameters hold the previous w and h values before the resize occurred.

Parameters
lastw (number)

the last width of the window before the resize

lasth (number)

the last height of the window before the resize

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:ondock()

Called when the window is docked or undocked, which includes when the window is first opened.

When this handler is called, the dock and docked attributes reflect the current state.

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:onclose()

Called after the window is (gracefully) closed by clicking the OS-native close button or when close() is called.

When the window would ungracefully close (e.g. because an exception occurred causing program abort), the rtk.onerror handler will be called instead.

You may wish to bind rtk.quit() to this handler to ensure if the user closes the window that the script terminates.

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:onkeypresspre(event)

Called when a key event occurs but before it is dispatched to any widgets.

The caller has the opportunity to mutate the event or set it as handled to prevent any widgets from responding to it.

Parameters
event (rtk.Event)

a rtk.Event.KEY event

Return Values
(nil)

Return value has no significance. This is a notification event only.

rtk.Window:onkeypresspost(event)

Called when a key event occurs after all widgets have been given a chance to handle the event.

Parameters
event (rtk.Event)

a rtk.Event.KEY event

Return Values
(nil)

Return value has no significance. This is a notification event only.