uses IOWindow
data GraphicsItem{
GraphicsObject object
GraphicsItem next
GraphicsItem prev
}
data ClickableItem{
ClickableObject object
ClickableItem next
ClickableItem prev
}
data XYListener{
XYMouseObject object
XYListener next
XYListener prev
}
data GlobalHotKeyItem{
ClickableObject object
HotKey hotKeys[]
GlobalHotKeyItem next
GlobalHotKeyItem prev
}
component provides Panel requires io.Output out, data.IntUtil iu {
GraphicsItem objects
GraphicsItem lastObject
ClickableItem clickObjects
ClickableItem lastClickObject
GlobalHotKeyItem hotKeyItems
GlobalHotKeyItem hotKeyItemsEnd
XYListener mouseListeners
XYListener mouseListenersEnd
XYMouseObject mouseOver
//we store the item on which a mouse button was pressed down, to know if the button is release on the same item (triggering a click)
GraphicsObject clickDown
bool backgroundSet
Color backgroundColor = new Color(220, 220, 230, 255)
HotKey focusHotKeys[]
KeyEventObject keyboardFocus
eventsink Events(EventData ed)
{
if (ed.type == GraphicsObject.[repaint])
{
postRepaint()
}
}
eventsink FocusEvents(EventData ed)
{
if (ed.type == ClickableObject.[requestFocus])
{
changeFocusTo(ed.source)
emitevent requestFocus()
}
else if (ed.type == ClickableObject.[contextMenuOn])
{
ClickableObject co = ed.source
ContextMenuSpec spec = ed.details
ClickableObject owner = spec.owner
if (owner == null) owner = co
emitevent contextMenuOn(new ContextMenuSpec(spec.xAnchor + co.getPosition().x, spec.yAnchor + co.getPosition().y, spec.items, owner))
}
else if (ed.type == ClickableObject.[contextMenuOff])
{
emitevent contextMenuOff()
}
}
eventsink CursorEvents(EventData ed)
{
if (ed.type == XYMouseObject.[setCursor])
{
CursorSetEvent cse = ed.details
emitevent setCursor(cse)
}
}
Panel:Panel()
{
repaintOn = true
}
void Panel:setFocus()
{
emitevent requestFocus()
}
bool Panel:recvFocus()
{
if (keyboardFocus != null)
{
return keyboardFocus.recvFocus()
}
return false
}
HotKey[] Panel:getHotKeys()
{
if (keyboardFocus != null)
{
return keyboardFocus.getHotKeys()
}
return null
}
void Panel:loseFocus()
{
if (keyboardFocus != null)
keyboardFocus.loseFocus()
}
void Panel:setPosition(int x, int y)
{
xPosition = x
yPosition = y
}
WH Panel:getPreferredSize()
{
return new WH(width, height)
}
void Panel:paint(Canvas c)
{
if (repaintOn)
{
//occlude contents at panel boundary
c.pushSurface(new Rect(xPosition, yPosition, width, height), 0, 0, 255)
//background
c.rect(new Rect2D(0, 0, width, height, backgroundColor))
for (GraphicsItem gi = objects; gi != null; gi = gi.next)
{
gi.object.paint(c)
}
c.popSurface()
}
}
void Panel:addObject(GraphicsObject go, opt bool globalHotKeys)
{
GraphicsItem newItem = new GraphicsItem(go)
if (objects == null)
objects = newItem
else
lastObject.next = newItem
newItem.prev = lastObject
lastObject = newItem
if (go hastype ClickableObject)
{
ClickableObject ko = go
addClickTarget(go, globalHotKeys)
sinkevent FocusEvents(ko)
}
if (go hastype XYMouseObject)
{
addMouseEventTarget(go)
XYMouseObject mo = go
sinkevent CursorEvents(mo)
}
sinkevent Events(go)
postRepaint()
}
void Panel:remObject(GraphicsObject go)
{
for (GraphicsItem bi = objects; bi != null; bi = bi.next)
{
if (bi.object === go)
{
GraphicsItem td = bi
if (td.prev == null)
objects = td.next
else
td.prev.next = td.next
if (td.next == null)
lastObject = td.prev
else
td.next.prev = td.prev
break
}
}
if (go hastype ClickableObject)
remClickTarget(go)
if (go hastype XYMouseObject)
{
remMouseEventTarget(go)
XYMouseObject mo = go
stopevent CursorEvents(mo)
}
postRepaint()
}
void Panel:clearObjects()
{
GraphicsItem iw = objects
while (iw != null)
{
GraphicsItem td = iw
iw = iw.next
td.next = null
td.prev = null
}
objects = null
lastObject = null
ClickableItem ci = clickObjects
while (ci != null)
{
ClickableItem td = ci
ci = ci.next
td.next = null
td.prev = null
}
clickObjects = null
lastClickObject = null
XYListener mi = mouseListeners
while (mi != null)
{
XYListener td = mi
mi = mi.next
td.next = null
td.prev = null
}
mouseListeners = null
mouseListenersEnd = null
postRepaint()
}
GraphicsObjectItem[] Panel:getObjects()
{
return null
}
Rect Panel:getBounds()
{
return new Rect(xPosition, yPosition, width, height)
}
Point Panel:getPosition()
{
return new Point(xPosition, yPosition)
}
void Panel:postRepaint()
{
emitevent repaint()
}
void Panel:setBackground(Color c)
{
backgroundSet = true
backgroundColor = c
}
Color Panel:getBackground()
{
return backgroundColor
}
void Panel:setSize(int w, int h)
{
width = w
height = h
}
WH Panel:getSize()
{
return new WH(width, height)
}
void changeFocusTo(store KeyEventObject ko)
{
if (keyboardFocus !== ko)
{
if (keyboardFocus != null) keyboardFocus.loseFocus()
}
ko.recvFocus()
focusHotKeys = ko.getHotKeys()
keyboardFocus = ko
}
void Panel:click(int x, int y, int button)
{
}
void Panel:clickMulti(int x, int y, int button, int clicks)
{
for (ClickableItem ci = clickObjects; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
ci.object.clickMulti(x - r.x, y - r.y, button, clicks)
break
}
}
}
void Panel:hotKeyClick(HotKey h)
{
if (keyboardFocus != null)
{
keyboardFocus.hotKeyClick(h)
}
}
void clickOn(int x, int y, int button)
{
for (ClickableItem ci = clickObjects; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
if (clickDown === ci.object)
{
ci.object.click(x - r.x, y - r.y, button)
if (ci.object hastype KeyEventObject)
{
changeFocusTo(ci.object)
}
}
break
}
}
}
void addClickTarget(store ClickableObject co, bool globalHotKeys)
{
ClickableItem nci = new ClickableItem(co)
if (clickObjects == null)
clickObjects = nci
else
lastClickObject.next = nci
nci.prev = lastClickObject
lastClickObject = nci
if (globalHotKeys)
{
GlobalHotKeyItem gci = new GlobalHotKeyItem(co, co.getHotKeys())
if (hotKeyItems == null)
hotKeyItems = gci
else
hotKeyItemsEnd.next = gci
gci.prev = hotKeyItemsEnd
hotKeyItemsEnd = gci
}
}
void remClickTarget(ClickableObject o)
{
for (ClickableItem ci = clickObjects; ci != null; ci = ci.next)
{
if (ci.object === o)
{
ClickableItem td = ci
if (td.prev == null)
clickObjects = td.next
else
td.prev.next = td.next
if (td.next == null)
lastClickObject = td.prev
else
td.next.prev = td.prev
break
}
}
for (GlobalHotKeyItem ci = hotKeyItems; ci != null; ci = ci.next)
{
if (ci.object === o)
{
GlobalHotKeyItem td = ci
if (td.prev == null)
hotKeyItems = td.next
else
td.prev.next = td.next
if (td.next == null)
hotKeyItemsEnd = td.prev
else
td.next.prev = td.prev
break
}
}
}
void addMouseEventTarget(store XYMouseObject m)
{
XYListener nci = new XYListener(m)
if (mouseListeners == null)
mouseListeners = nci
else
mouseListenersEnd.next = nci
nci.prev = mouseListenersEnd
mouseListenersEnd = nci
}
void remMouseEventTarget(XYMouseObject m)
{
for (XYListener ci = mouseListeners; ci != null; ci = ci.next)
{
if (ci.object === m)
{
XYListener td = ci
if (td.prev == null)
mouseListeners = td.next
else
td.prev.next = td.next
if (td.next == null)
mouseListenersEnd = td.prev
else
td.next.prev = td.prev
break
}
}
}
bool Panel:keyDown(int keyCode)
{
if (keyboardFocus != null)
{
return keyboardFocus.keyDown(keyCode)
}
return false
}
bool Panel:keyUp(int keyCode)
{
if (keyboardFocus != null)
{
return keyboardFocus.keyUp(keyCode)
}
return false
}
void Panel:mouseDown(int x, int y, int button)
{
if (clickDown == null)
{
for (ClickableItem ci = clickObjects; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
clickDown = ci.object
break
}
}
}
for (XYListener ci = mouseListeners; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
clickDown = ci.object
ci.object.mouseDown(x - r.x, y - r.y, button)
break
}
}
}
void Panel:mouseUp(int x, int y, int button)
{
if (clickDown !== null)
{
if (clickDown hastype XYMouseObject)
{
XYMouseObject co = clickDown
Rect r = co.getBounds()
co.mouseUp(x - r.x, y - r.y, button)
}
}
else
{
for (XYListener ci = mouseListeners; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
//we only register a mouse-up if this was the thing that got the mouse-down
if (ci === clickDown)
{
ci.object.mouseUp(x - r.x, y - r.y, button)
}
else if (clickDown hastype XYMouseObject)
{
XYMouseObject co = clickDown
r = co.getBounds()
co.mouseUp(x - r.x, y - r.y, button)
}
break
}
}
}
clickOn(x, y, button)
clickDown = null
}
void Panel:mouseMove(int x, int y)
{
if (clickDown !== null)
{
if (clickDown hastype XYMouseObject)
{
XYMouseObject co = clickDown
Rect r = co.getBounds()
if (x < r.x) x = r.x
if (y < r.y) y = r.y
co.mouseMove(x - r.x, y - r.y)
}
}
else
{
bool overSomething
for (XYListener ci = mouseListeners; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
if (mouseOver !== ci.object)
{
if (mouseOver != null)
{
mouseOver.mouseOut()
emitevent setCursor(new CursorSetEvent(IOWindow.CURSOR_DEFAULT))
}
mouseOver = ci.object
CursorSetEvent cse = mouseOver.mouseOver(x - r.x, y - r.y)
if (cse != null)
emitevent setCursor(cse)
else
emitevent setCursor(new CursorSetEvent(IOWindow.CURSOR_DEFAULT))
}
else
{
ci.object.mouseMove(x - r.x, y - r.y)
}
overSomething = true
break
}
}
if (!overSomething && mouseOver != null)
{
mouseOver.mouseOut()
emitevent setCursor(new CursorSetEvent(IOWindow.CURSOR_DEFAULT))
mouseOver = null
}
}
}
void Panel:mouseWheel(int xAdd, int xSub, int yAdd, int ySub)
{
if (mouseOver != null)
{
mouseOver.mouseWheel(xAdd, xSub, yAdd, ySub)
}
}
void Panel:mouseOut()
{
if (mouseOver != null)
{
mouseOver.mouseOut()
mouseOver = null
}
}
void Panel:dropFile(int x, int y, char path[])
{
for (XYListener ci = mouseListeners; ci != null; ci = ci.next)
{
Rect r = ci.object.getBounds()
if (x >= r.x && x <= r.x + r.width && y >= r.y && y <= r.y + r.height)
{
XYMouseObject co = ci.object
co.dropFile(x - r.x, y - r.y, path)
break
}
}
}
void Panel:setDisabled(bool d)
{
disabled = d
postRepaint()
}
void Panel:setRepaint(bool b)
{
repaintOn = b
if (repaintOn)
postRepaint()
}
}