UXApplication
UXApplication is the program. It boots the backend, owns the window list
and the menu bar, runs the event loop, and sits at the top of the responder
chain. An application’s main() is the same lines on every target: with
UXPlatform in the build, a new
application installs the driver for the backend the build links:
void main(void) { UXApplication* app = new UXApplication(); Controller* c = new Controller(); app.setDelegate(c); app.run();}#use <UXKit>Overview
Section titled “Overview”Life starts in the delegate. run boots the driver, then calls
your delegate’s applicationDidStart, where you make windows, add views and
install menus. Return 0 to enter the loop. Any other value aborts the run
with that code. A delegate may call stop during start-up (a test, a
one-shot tool), and the stop is honoured.
protocol UXApplicationDelegate { i32 applicationDidStart(UXApplication* app); optional void windowDidResize(UXApplication* app, UXWindow* win, i32 width, i32 height);}The loop has two shapes, and your code sees one. On five of the six
backends, run() is the classic blocking loop: one event from the driver, one
dispatch, one coalesced repaint, repeat. On iOS, the platform owns the main
thread’s loop, so run() detects driverOwnsRunLoop() and hands the thread to
the native loop. Your delegate still starts in applicationDidStart (fired
from the platform’s own start moment) and your actions still fire, so the
app’s source is the same. This is the toolkit’s one sanctioned loop inversion,
and the driver handles it.
Repaints are coalesced. However many views called setNeedsDisplay in
one pass of the loop, displayIfNeeded repaints once, and
each window repaints only the union of the rects its views marked.
Closing windows is deferred. An action that closes its own window would
tear down the view whose event is still on the stack. Control actions
therefore use closeWindowLater, and the loop drains the
list once no window code is running. Closing the last window ends the run.
Resizes reach you after the toolkit is done. When the optional
windowDidResize fires, the native frame has finished its drag, the tree has
reflowed (springs and struts, see
UXView.setAutoresizeMask),
and the repaint has happened. Implement it only if you lay out views by hand.
Conforms to
Section titled “Conforms to”- Inherits
UXResponder, as the chain’s last stop.
Topics
Section titled “Topics”Configuring · setDriver · setHeadless · setDelegate · setMenuBar · setIcon · setIconPixels The loop · run · stop · isRunning · pump Windows · addWindow · closeWindow · closeWindowLater Files dropped on a window · setFileDropHandler · deliverFileDrop · setItemDropHandler · deliverItemDrop · setItemHoverHandler · deliverItemHover Screen · screenWidth · screenHeight Painting · displayIfNeeded
setDriver
Section titled “setDriver”void setDriver(UXViewDriver* d)Selects a particular backend and tells the driver its application. A program
needs it only to choose a backend other than its target’s (a test of one
driver, say), because new UXApplication() installs the target’s driver when
UXPlatform is in the build. Windows are
shown unless setHeadless asks otherwise.
setHeadless
Section titled “setHeadless”void setHeadless(bool on)Call before run. Realizes and paints windows without showing them,
where the platform has that mode. On macOS the views are painted offscreen and
a GL view still gets its context. The other backends show their windows as
usual, which is headless enough under Xvfb or an emulator. It is for an app’s
own test runs, so the source stays the same.
setDelegate
Section titled “setDelegate”void setDelegate(UXApplicationDelegate* d)Sets the app’s owner. run() refuses to start without one.
setFileDropHandler
Section titled “setFileDropHandler”void setFileDropHandler(callback h void(u8* path, i32 window, i32 x, i32 y))Takes files dragged onto the app’s windows from the Finder or a file manager. The handler gets each file’s path, the window’s handle and the point in its content. The path belongs to the driver, so copy it to keep it. From 0.7 on AppKit, and on GTK and Windows from 0.71.
deliverFileDrop
Section titled “deliverFileDrop”void deliverFileDrop(u8* path, i32 window, i32 x, i32 y)What a driver calls for each dropped file; it passes the file to the handler.
setItemDropHandler
Section titled “setItemDropHandler”void setItemDropHandler(callback h void(u8* item, i32 window, i32 x, i32 y))Takes rows dragged out of the app’s own tables (see
setDragsRows) and dropped
on its windows. The handler gets the row’s first column, the window’s handle
and the point in its content. The text belongs to the driver, so copy it to
keep it. From 0.7 on AppKit, and from 0.71 on GTK, Windows and
the backends where the toolkit draws its tables (the web, GEM).
deliverItemDrop
Section titled “deliverItemDrop”void deliverItemDrop(u8* item, i32 window, i32 x, i32 y)What a driver calls for a dropped row; it passes the row to the handler.
setItemHoverHandler
Section titled “setItemHoverHandler”void setItemHoverHandler(callback h void(u8* item, i32 window, i32 x, i32 y))Follows such a row while it is dragged over the app’s windows, so the app can show what a drop would do. The point is (-1, -1) once the row has left the window or been dropped. From 0.7 on AppKit, and from 0.71 on GTK, Windows and the backends where the toolkit draws its tables.
deliverItemHover
Section titled “deliverItemHover”void deliverItemHover(u8* item, i32 window, i32 x, i32 y)What a driver calls as a row is dragged; it passes the row to the handler.
setMenuBar
Section titled “setMenuBar”void setMenuBar(UXMenuBar* mb)Installs the bar. From then on the platform owns the drawing, tracking and pull-down, and a pick arrives as a neutral menu-select event routed to the bar’s callbacks.
i32 run(void)Boots, starts the delegate, then runs the loop (or the platform’s loop on iOS,
see the overview). Returns 0 after stop on the blocking backends,
1 if boot failed, 2 with no delegate, or the delegate’s own nonzero start
code.
setIcon / setIconPixels
Section titled “setIcon / setIconPixels”bool setIcon(UXImage* img)bool setIconPixels(u8* data, i32 w, i32 h, i32 format)The application’s icon while it runs: the Dock tile on macOS, the taskbar,
Alt-Tab and title-bar icon of every window on Windows (including windows opened
later), and the page’s favicon on the web. setIconPixels takes raw pixels, in
the formats of drawPixels:
UXPIX_RGBA bytes, such as a decoded PNG, or UXPIX_ARGB32 words. The pixels
are copied, so the image can change or go away afterwards. To change the icon,
for a status variant say, set it again.
Both return true when the platform shows the icon. They return false on iOS,
Android, GTK and GEM, where only the app’s package gives it an icon: the
asset catalog, the APK’s android:icon, the .desktop entry and icon theme,
and the desktop’s resource file. That icon is also what every platform shows
before the app runs (in Finder, Explorer or a launcher), and it comes from the
packaging step, not from this call.
void stop(void)Ends the loop after the current iteration, on every backend. Where the
platform owns the loop (AppKit’s [NSApp run]), the driver ends that too, so
stop works from a turn hook, a timer or an event handler alike. On iOS a
stopped test app exits. A real iOS app does not stop, because the platform owns
the process lifetime.
With UX_AUTOQUIT=<ms> in the environment, the loop stops by itself after that
long, by the same path. Use it for an unattended run of an app that would wait
for a person. On Windows under Wine, a process killed from outside leaves its
helper processes running.
isRunning
Section titled “isRunning”bool isRunning(void)True between a successful start and stop().
everyTurn
Section titled “everyTurn”void everyTurn(turnHook_t* fn, i32 ms)bool turnIsDriven(void)The app’s frame clock: fn is called once per turn — at most every ms
milliseconds, 0 meaning “every turn the loop has” — from outside any draw, so
it is the place to step a simulation and mark what moved. Passing (0, 0)
stops it, and the app can stop its own clock from inside a turn (which is what a
client that wants to exit cleanly needs, since stop only lowers a
flag).
fn takes no arguments and must be a plain function, not a bound method: on the
backends that own their loop the driver holds it as a C function pointer.
On macOS a turn of 30 a second or more (ms from 0 to 33) is paced by the
display: it comes once per refresh of the screen the window is on, steadily,
at most every ms. A slower turn comes from a timer.
Who calls fn is the driver’s answer, and this method remembers it:
true from setTurnHook
means the driver armed its own source and calls fn (interactive AppKit, iOS,
Android — the loop owners whose native loop leaves nothing above the driver able
to get a turn); false means the loop paces itself, using ms as the wait it
hands nextEvent so that a turn comes round even when no input does.
turnIsDriven() returns that answer, so a client that needs to know whether the
clock it asked for is the clock it got asks rather than assumes:
app.everyTurn(&tick, 16);if (!app.turnIsDriven()) // the neutral loop is doing the callingvoid pump(i32 ms)Drains pending window messages (not input) and dispatches them. This is the primitive for waiting on the platform’s answer. Backends answer geometry asynchronously (report a content size, and the scrollbar that appears changes your work area), so a program that sets and then reads without a pump reads its own request back.
addWindow
Section titled “addWindow”void addWindow(UXWindow* w)Registers a window with the app. The first one becomes the key window. Call
this before open so events can route to the window.
closeWindow
Section titled “closeWindow”void closeWindow(UXWindow* w)Closes the window immediately. This is safe only when no code belonging to w
is on the stack. Key status moves to the first remaining window, and closing
the last one stops the run.
closeWindowLater
Section titled “closeWindowLater”void closeWindowLater(UXWindow* w)The safe form for control actions. It queues the close, and the loop performs it once the window’s code has unwound.
screenWidth / screenHeight
Section titled “screenWidth / screenHeight”i32 screenWidth(void)i32 screenHeight(void)The screen (or canvas, or scene) size the driver reported at boot.
displayIfNeeded
Section titled “displayIfNeeded”void displayIfNeeded(void)One repaint pass over every window that marked damage. The loop calls it each iteration, and modal driver paths call it so effects show during tracking.
Example
Section titled “Example”The smallest complete application, with the same shape on every backend:
#use <UXKit>
class App : Object <UXApplicationDelegate>{ weak:UXApplication* app;
i32 applicationDidStart(UXApplication* a) { app = a; UXView* content = new UXView(); UXWindow* win = new UXWindow(); a.addWindow(win); win.open((u8*)"Hello", UXGeom.make(20, 20, 240, 120), content);
UXButton* quit = new UXButton(); quit.setTitle((u8*)"Quit"); quit.setAction(&self.onQuit); content.addSubview(quit, UXGeom.make(80, 46, 80, 28));
win.tree.finalise(); win.displayAll(); return 0; }
void onQuit(UXControl* sender) { app.stop(); }}
void main(void) { UXApplication* app = new UXApplication(); App* a = new App(); app.setDelegate(a); app.run();}