HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component ui.Panel by barry
expand copy to clipboardexpand
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()
		}

	}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n ui.Panel -m "reason for update" -u yourUsername
Version 2 by barry
Version 1 (this version) by barry
Notes for this version: Standard Library Initialisation