data WindowEventData {
int button_id
int x
int y
int exd1
int exd2
}
data DropEventData {
int x
int y
char path[]
}
interface UIPlaneLib {
event ready()
event mouseUp(WindowEventData wed)
event mouseDown(WindowEventData wed)
event mouseMove(WindowEventData wed)
event mouseWheel(WindowEventData wed)
event keyDown(WindowEventData wed)
event keyUp(WindowEventData wed)
event resize(WindowEventData wed)
event move(WindowEventData wed)
event drop(DropEventData ded)
event close()
event post_shutdown()
int makeWindow()
void addPoint(int phandle, int x, int y, byte r, byte g, byte b, byte a)
void addLine(int phandle, int sx, int sy, int ex, int ey, int thickness, byte r, byte g, byte b, byte a, bool antiAlias)
void addCurve(int phandle, Point points[], int isteps, int thickness, byte r, byte g, byte b, byte a, bool antiAlias)
void addRect(int phandle, int x, int y, int w, int h, byte r, byte g, byte b, byte a)
void addEllipse(int phandle, int x, int y, int w, int h, byte r, byte g, byte b, byte a)
void addEllipseOutline(int phandle, int x, int y, int w, int h, int thickness, byte r, byte g, byte b, byte a)
void addArc(int phandle, int x, int y, int w, int h, int start, int end, int lineWidth, byte r, byte g, byte b, byte a, bool antiAlias)
void addPie(int phandle, int x, int y, int w, int h, int start, int end, bool chord, byte r, byte g, byte b, byte a)
void addPolygon(int phandle, Point points[], byte r, byte g, byte b, byte a)
void addPolygonOutline(int phandle, Point points[], int thickness, byte r, byte g, byte b, byte a)
void addPolygonBezier(int phandle, Point points[], int isteps, byte r, byte g, byte b, byte a)
void addPolygonBezierOutline(int phandle, Point points[], int isteps, int thickness, byte r, byte g, byte b, byte a)
void addBitmap(int phandle, PixelMap pixels, Rect subRect, int x, int y, int scaledWidth, int scaledHeight, int rotation)
void addTextWith(int phandle, int fontHandle, int x, int y, int rotation, char text[], byte r, byte g, byte b, byte a)
void pushSurface(int phandle, int x, int y, int w, int h, int sx, int sy, byte a)
void popSurface(int phandle)
void setSize(int phandle, int x, int y)
void setPosition(int phandle, int x, int y)
void setVisible(int phandle, bool b)
void setResizable(int phandle, bool b)
void setFullScreen(int phandle, bool b)
void setTitle(int phandle, char t[])
void setIcon(int phandle, PixelMap p)
void setCursor(int phandle, byte type, Cursor c)
void commitBuffer(int phandle)
PixelMap getPixels(int phandle)
void getResolution(int phandle, WH wh)
void setBackgroundColor(int phandle, byte r, byte g, byte b, byte a)
void maximiseWindow(int phandle)
void minimiseWindow(int phandle)
void getMaximisedScreenRect(int phandle, Rect r)
void closeWindow(int phandle)
bool initMediaLayer()
void runSystemLoop()
void shutdown()
// -- fonts --
int loadFont(char path[], int size)
int getTextWidth(int phandle, char text[])
void getFontMetrics(int phandle, FontMetrics metrics)
char[] getFontName(int phandle)
bool isFontFixedWidth(int phandle)
void getTextBitmapWith(int phandle, char text[], PixelMap pixels, byte r, byte g, byte b, byte a)
void unloadFont(int fd)
}
component provides IOLayer, IOWindow(Destructor), Font(Destructor) requires native UIPlaneLib lib, io.Output out, data.IntUtil iu
{
static bool initDone = false
static Mutex qlock = new Mutex()
implementation IOWindow {
int platformHandle
Mutex windowStateLock = new Mutex()
eventsink LibEvents(EventData ed)
{
if (ed.type == UIPlaneLib.[mouseUp])
{
WindowEventData wed = ed.details
emitevent mouseUp(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y, wed.exd1))
}
else if (ed.type == UIPlaneLib.[mouseDown])
{
WindowEventData wed = ed.details
emitevent mouseDown(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y))
}
else if (ed.type == UIPlaneLib.[mouseMove])
{
WindowEventData wed = ed.details
emitevent mouseMove(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y))
}
else if (ed.type == UIPlaneLib.[mouseWheel])
{
WindowEventData wed = ed.details
emitevent mouseWheel(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y, wed.exd1, wed.exd2))
}
else if (ed.type == UIPlaneLib.[keyDown])
{
WindowEventData wed = ed.details
emitevent keyDown(new KeyEvent(wed.button_id))
}
else if (ed.type == UIPlaneLib.[keyUp])
{
WindowEventData wed = ed.details
emitevent keyUp(new KeyEvent(wed.button_id))
}
else if (ed.type == UIPlaneLib.[resize])
{
WindowEventData wed = ed.details
emitevent resizeWindow(new WH(wed.x, wed.y))
}
else if (ed.type == UIPlaneLib.[move])
{
WindowEventData wed = ed.details
emitevent moveWindow(new Point(wed.x, wed.y))
}
else if (ed.type == UIPlaneLib.[drop])
{
DropEventData wed = ed.details
emitevent fileDrop(new DropEvent(wed.x, wed.y, wed.path))
}
else if (ed.type == UIPlaneLib.[close])
{
emitevent closeWindow()
}
}
IOWindow:IOWindow()
{
mutex(qlock)
{
if (!initDone)
{
throw new Exception("IOLayer must be initialised before windows are created")
}
}
platformHandle = lib.makeWindow()
if (platformHandle == 0) throw new Exception("Platform window creation failed")
sinkevent LibEvents(lib)
}
void IOWindow:setVisible(bool b)
{
mutex(windowStateLock)
{
lib.setVisible(platformHandle, b)
}
}
void IOWindow:setResizable(bool v)
{
mutex(windowStateLock)
{
lib.setResizable(platformHandle, v)
}
}
void IOWindow:setFullScreen(bool v)
{
mutex(windowStateLock)
{
lib.setFullScreen(platformHandle, v)
}
}
WH IOWindow:getResolution()
{
mutex(windowStateLock)
{
WH wh = new WH()
lib.getResolution(platformHandle, wh)
return wh
}
}
void IOWindow:setTitle(char title[])
{
mutex(windowStateLock)
{
lib.setTitle(platformHandle, title)
}
}
void IOWindow:setIcon(store PixelMap p)
{
mutex(windowStateLock)
{
lib.setIcon(platformHandle, p)
}
}
void IOWindow:setCursor(byte cursorType, opt Cursor c)
{
mutex(windowStateLock)
{
lib.setCursor(platformHandle, cursorType, c)
}
}
void IOWindow:close()
{
mutex(windowStateLock)
{
if (platformHandle != 0)
{
lib.closeWindow(platformHandle)
platformHandle = 0
}
}
}
void IOWindow:paint()
{
mutex(windowStateLock)
{
lib.commitBuffer(platformHandle)
}
}
void IOWindow:rect(Rect2D r)
{
mutex(windowStateLock)
{
lib.addRect(platformHandle, r.x, r.y, r.width, r.height, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:rectOutline(Rect2D r, opt int thickness)
{
if (!(isset thickness)) thickness = 1
int offset = thickness / 2
line(new Line2D(r.x - offset, r.y, (r.x + r.width) + offset - 1, r.y, r.color), thickness)
line(new Line2D(r.x, r.y, r.x, (r.y + r.height) + offset - 1, r.color), thickness)
line(new Line2D((r.x + r.width) - 1, r.y, (r.x + r.width) - 1, (r.y + r.height) + offset - 1, r.color), thickness)
line(new Line2D(r.x, (r.y + r.height) - 1, (r.x + r.width) - 1, (r.y + r.height) - 1, r.color), thickness)
}
void IOWindow:roundedRect(Rect2D r, int xRadius, int yRadius)
{
mutex(windowStateLock)
{
//top-left corner
pie(new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color))
//top-right corner
pie(new Arc2D((r.x + r.width - xRadius), r.y + yRadius, xRadius, yRadius, 0, 90, r.color))
//bottom-right corner
pie(new Arc2D((r.x + r.width - xRadius), (r.y + r.height - yRadius), xRadius, yRadius, 90, 180, r.color))
//bottom-left corner
pie(new Arc2D((r.x + xRadius), (r.y + r.height - yRadius), xRadius, yRadius, 180, 270, r.color))
//middle
rect(new Rect2D(r.x + xRadius, r.y, r.width - xRadius - xRadius, r.height, r.color))
//left rect
rect(new Rect2D(r.x, r.y + yRadius, xRadius, r.height - yRadius - yRadius, r.color))
//right rect
rect(new Rect2D(r.x + r.width - xRadius, r.y + yRadius, xRadius, r.height - yRadius - yRadius, r.color))
}
}
void IOWindow:roundedRectOutline(Rect2D r, int xRadius, int yRadius, opt int thickness)
{
if (!(isset thickness)) thickness = 1
int offset = thickness / 2
int md = thickness % 2
if (thickness <= 1)
{
line(new Line2D((r.x + xRadius), r.y, (r.x + r.width - xRadius), r.y, r.color), thickness)
line(new Line2D(r.x, r.y + yRadius, r.x, (r.y + r.height - yRadius), r.color), thickness)
line(new Line2D((r.x + r.width) - 1, r.y + yRadius, (r.x + r.width) - 1, (r.y + r.height - yRadius), r.color), thickness)
line(new Line2D((r.x + xRadius), (r.y + r.height) - 2, (r.x + r.width - xRadius), (r.y + r.height) - 2, r.color), thickness)
}
else
{
//using "curves" for lines here seems to give us the best anti-alias matched position effect, relative to our corner arcs
//top
curve(new Curve2D(new Point[](new Point(r.x+xRadius, r.y), new Point(r.x+(r.width/2), r.y), new Point(r.x+r.width-xRadius, r.y)), r.color), 2, thickness)
//left
curve(new Curve2D(new Point[](new Point(r.x, r.y+yRadius), new Point(r.x, r.y+(r.height/2)), new Point(r.x, r.y + r.height - yRadius)), r.color), 2, thickness)
//right
curve(new Curve2D(new Point[](new Point(r.x+r.width-1, r.y+yRadius), new Point(r.x+r.width-1, r.y+(r.height/2)), new Point(r.x+r.width-1, r.y + r.height - yRadius)), r.color), 2, thickness)
//bottom
curve(new Curve2D(new Point[](new Point(r.x+xRadius, r.y+r.height-offset), new Point(r.x+(r.width/2), r.y+r.height-offset), new Point(r.x+r.width-xRadius, r.y+r.height-offset)), r.color), 2, thickness)
}
if (thickness <= 1)
{
Arc2D arc = null
//top-left corner
arc = new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color)
lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
//top-right corner
arc = new Arc2D((r.x + r.width - xRadius)-1, r.y + yRadius, xRadius, yRadius, 0, 90, r.color)
lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
//bottom-right corner
arc = new Arc2D((r.x + r.width - xRadius)-1, (r.y + r.height - yRadius) - 2, xRadius, yRadius, 90, 180, r.color)
lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
//bottom-left corner
arc = new Arc2D((r.x + xRadius), (r.y + r.height - yRadius) - 2, xRadius, yRadius, 180, 270, r.color)
lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
}
else
{
//top-left corner
arcOutline(new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color), thickness)
//top-right corner
arcOutline(new Arc2D((r.x + r.width - xRadius)-1, r.y + yRadius, xRadius, yRadius, 0, 90, r.color), thickness)
//bottom-right corner
arcOutline(new Arc2D((r.x + r.width - xRadius)-1, (r.y + r.height - yRadius) - 2, xRadius, yRadius, 90, 180, r.color), thickness)
//bottom-left corner
arcOutline(new Arc2D((r.x + xRadius), (r.y + r.height - yRadius) - 2, xRadius, yRadius, 180, 270, r.color), thickness)
}
}
void IOWindow:ellipse(Ellipse2D r)
{
mutex(windowStateLock)
{
lib.addEllipse(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:ellipseOutline(Ellipse2D r, opt int thickness)
{
if (!(isset thickness)) thickness = 1
mutex(windowStateLock)
{
lib.addEllipseOutline(platformHandle, r.x, r.y, r.xRadius, r.yRadius, thickness, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:arc(Arc2D r)
{
mutex(windowStateLock)
{
lib.addPie(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, true, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:arcOutline(Arc2D r, opt int lineWidth)
{
if (!(isset lineWidth)) lineWidth = 1
mutex(windowStateLock)
{
lib.addArc(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, lineWidth, r.color.r, r.color.g, r.color.b, r.color.a, true)
}
}
void IOWindow:pie(Arc2D r)
{
mutex(windowStateLock)
{
lib.addPie(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, false, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:polygon(Polygon2D r)
{
mutex(windowStateLock)
{
lib.addPolygon(platformHandle, r.points, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:polygonOutline(Polygon2D r)
{
int thickness = 1
mutex(windowStateLock)
{
lib.addPolygonOutline(platformHandle, r.points, thickness, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:polygonBezier(Polygon2D r, int isteps)
{
mutex(windowStateLock)
{
lib.addPolygonBezier(platformHandle, r.points, isteps, r.color.r, r.color.g, r.color.b, r.color.a)
}
}
void IOWindow:line(Line2D l, opt int thickness)
{
if (!(isset thickness)) thickness = 1
mutex(windowStateLock)
{
lib.addLine(platformHandle, l.sx, l.sy, l.ex, l.ey, thickness, l.color.r, l.color.g, l.color.b, l.color.a, false)
}
}
void IOWindow:curve(Curve2D l, int isteps, opt int thickness)
{
if (!(isset thickness)) thickness = 1
mutex(windowStateLock)
{
lib.addCurve(platformHandle, l.points, isteps, thickness, l.color.r, l.color.g, l.color.b, l.color.a, false)
}
}
void IOWindow:point(Point2D p)
{
mutex(windowStateLock)
{
lib.addPoint(platformHandle, p.x, p.y, p.color.r, p.color.g, p.color.b, p.color.a)
}
}
void IOWindow:pixels(PixelMap map, int x, int y, opt int scaledWidth, int scaledHeight, int rotation, Rect subRect)
{
if (!(isset scaledWidth)) scaledWidth = map.size.width
if (!(isset scaledHeight)) scaledHeight = map.size.height
mutex(windowStateLock)
{
lib.addBitmap(platformHandle, map, subRect, x, y, scaledWidth, scaledHeight, rotation)
}
}
void IOWindow:text(Point2D origin, Font f, char text[], opt int rotation)
{
//check if f is implemented by this component and if so we use its internal font handle; otherwise we fall back on Font.getBitmap() (which is slow)
mutex(windowStateLock)
{
if (implements f)
{
lib.addTextWith(platformHandle, f.platformHandle, origin.x, origin.y, rotation, text, origin.color.r, origin.color.g, origin.color.b, origin.color.a)
}
else
{
PixelMap px = f.getPixels(text, origin.color)
lib.addBitmap(platformHandle, px, null, origin.x, origin.y, px.size.width, px.size.height, rotation)
}
}
}
void IOWindow:pushSurface(Rect rect, int xscr, int yscr, byte alpha)
{
mutex(windowStateLock)
{
lib.pushSurface(platformHandle, rect.x, rect.y, rect.width, rect.height, xscr, yscr, alpha)
}
}
void IOWindow:popSurface()
{
mutex(windowStateLock)
{
lib.popSurface(platformHandle)
}
}
void IOWindow:setPosition(int x, int y)
{
mutex(windowStateLock)
{
lib.setPosition(platformHandle, x, y)
}
}
void IOWindow:setSize(int x, int y)
{
mutex(windowStateLock)
{
lib.setSize(platformHandle, x, y)
}
}
void IOWindow:setBackground(store Color c)
{
mutex(windowStateLock)
{
lib.setBackgroundColor(platformHandle, c.r, c.g, c.b, c.a)
}
}
PixelMap IOWindow:getPixels()
{
return lib.getPixels(platformHandle)
}
void Destructor:destroy()
{
//if we're being destroyed, we need to free the window handle in the UI framework, otherwise it will have a bad reference
close()
}
}
implementation IOLayer {
eventsink LibSysEvents(EventData ed)
{
if (ed.type == UIPlaneLib.[ready])
{
emitevent ready()
}
else if (ed.type == UIPlaneLib.[post_shutdown])
{
emitevent recvShutdown()
}
}
bool IOLayer:init()
{
mutex(qlock)
{
if (initDone)
{
throw new Exception("initialisation has already been done")
}
else
{
initDone = true
}
}
sinkevent LibSysEvents(lib)
return lib.initMediaLayer()
}
void IOLayer:run()
{
mutex(qlock)
{
if (!initDone)
{
throw new Exception("system must first be initialised via init()")
}
}
lib.runSystemLoop()
}
void IOLayer:shutdown()
{
lib.shutdown()
initDone = false
}
}
implementation Font{
int platformHandle
int fontSize
char source[]
Font:Font(char path[], int size)
{
fontSize = size
source = path
platformHandle = lib.loadFont(path, size)
if (platformHandle == 0) throw new Exception("Failed to load font $path")
}
int Font:getSize()
{
return fontSize
}
char[] Font:getSource()
{
return source
}
FontMetrics Font:getFontMetrics()
{
FontMetrics result = new FontMetrics()
lib.getFontMetrics(platformHandle, result)
return result
}
char[] Font:getFontName()
{
return lib.getFontName(platformHandle)
}
bool Font:isFixedCharacterWidth()
{
return lib.isFontFixedWidth(platformHandle)
}
int Font:getTextWidth(char text[])
{
return lib.getTextWidth(platformHandle, text)
}
PixelMap Font:getPixels(char string[], Color textColor)
{
PixelMap result = new PixelMap(new WH())
lib.getTextBitmapWith(platformHandle, string, result, textColor.r, textColor.g, textColor.b, textColor.a)
return result
}
void Destructor:destroy()
{
lib.unloadFont(platformHandle)
}
}
}