HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component ui.ScrollPane by barry
expand copy to clipboardexpand
const int SCR_BUTTON_HEIGHT = 20
const int SCR_BUTTON_WIDTH = 20

const int SLIDER_MIN_HEIGHT = 20

const int SCROLL_TICK = 5

component provides ScrollPane requires io.Output out, data.IntUtil iu, ScrollBar, ScrollBar:h {

	int width
	int height
	
	GraphicsObject clickDown
	
	GraphicsObject content
	
	Color gapColor = new Color(200, 200, 215, 255)
	Color backgroundColor
	
	bool showScrollV = true
	ScrollBar scrollV
	
	bool showScrollH = true
	ScrollBar scrollH
	
	int scrollY = 0
	int scrollX = 0

	void updateScrollBarV()
		{
		if (content != null && showScrollV)
			{
			int contentH = content.getPreferredSize().height
			
			int effectiveHeight = height
			if (showScrollH) effectiveHeight -= SCR_BUTTON_HEIGHT
			
			if (contentH > effectiveHeight)
				contentH -= effectiveHeight
				else
				contentH = 0
			
			scrollV.setMaxValue(contentH)
			}
		}
	
	void updateScrollBarH()
		{
		if (content != null && showScrollH)
			{
			int contentW = content.getPreferredSize().width
			
			int effectiveWidth = width
			if (showScrollV) effectiveWidth -= SCR_BUTTON_WIDTH
			
			if (contentW > effectiveWidth)
				contentW -= effectiveWidth
				else
				contentW = 0
			
			scrollH.setMaxValue(contentW)
			}
		}

	ScrollPane:ScrollPane(opt store GraphicsObject g)
		{
		scrollV = new ScrollBar()
		scrollV.setPosition(width - SCR_BUTTON_WIDTH, 0)
		
		scrollH = new ScrollBar:h()
		scrollH.setPosition(0, height - SCR_BUTTON_WIDTH)
		
		sinkevent ScrollEvents(scrollV)
		sinkevent ScrollEvents(scrollH)
		
		if (g != null)
			{
			setContent(g)
			}
		}
	
	eventsink ContentPaintEvents(EventData ed)
		{
		updateScrollBarV()
		updateScrollBarH()

		postRepaint()
		}
	
	eventsink ScrollEvents(EventData ed)
		{
		if (ed.source === scrollV && ed.type == ScrollBar.[scrollMoved])
			{
			scrollY = scrollV.getScrollPos()
			}
			else if (ed.source === scrollV && ed.type == ScrollBar.[repaint])
			{
			postRepaint()
			}
			else if (ed.source === scrollH && ed.type == ScrollBar.[scrollMoved])
			{
			scrollX = scrollH.getScrollPos()
			}
			else if (ed.source === scrollH && ed.type == ScrollBar.[repaint])
			{
			postRepaint()
			}
		}
	
	void ScrollPane:setBackground(store Color c)
		{
		backgroundColor = c
		}
	
	void ScrollPane:setContent(store GraphicsObject g)
		{
		if (content != null)
			stopevent ContentPaintEvents(g)

		content = g

		updateScrollBarV()
		updateScrollBarH()

		sinkevent ContentPaintEvents(g)
		}

	void ScrollPane:setSize(int w, int h)
		{
		width = w
		height = h
		
		if (showScrollV)
			{
			scrollV.setPosition(width - SCR_BUTTON_WIDTH, 0)
			if (!showScrollH)
				scrollV.setLength(height)
				else
				scrollV.setLength(height - SCR_BUTTON_HEIGHT)
			}
		
		if (showScrollH)
			{
			scrollH.setPosition(0, height - SCR_BUTTON_WIDTH)
			if (!showScrollV)
				scrollH.setLength(width)
				else
				scrollH.setLength(width - SCR_BUTTON_WIDTH)
			}
		
		updateScrollBarV()
		updateScrollBarH()
		}
	
	void ScrollPane:showScroll(bool v, bool h)
		{
		showScrollV = v
		showScrollH = h
		
		if (!showScrollH)
			scrollV.setLength(height)
			else
			scrollV.setLength(height - SCR_BUTTON_HEIGHT)
		
		if (!showScrollV)
			scrollH.setLength(width)
			else
			scrollH.setLength(width - SCR_BUTTON_WIDTH)
		
		updateScrollBarV()
		updateScrollBarH()
		
		postRepaint()
		}

	void ScrollPane:setFocus()
		{
		//pass-through to content
		}

	bool ScrollPane:recvFocus()
		{
		//pass-through to content
		return false
		}

	void ScrollPane:loseFocus()
		{
		//pass-through to content
		}

	bool ScrollPane:keyDown(int keyID)
		{
		//pass-through to content
		return false
		}

	bool ScrollPane:keyUp(int keyID)
		{
		//pass-through to content
		return false
		}

	Rect ScrollPane:getBounds()
		{
		return new Rect(xPosition, yPosition, width, height)
		}
	
	void ScrollPane:mouseDown(int x, int y, int button)
		{
		if (showScrollV && (x >= width-SCR_BUTTON_WIDTH && x <= width))
			{
			scrollV.mouseDown(x - (width-SCR_BUTTON_WIDTH), y, button)
			clickDown = scrollV
			}
			else if (showScrollH && (y >= height-SCR_BUTTON_WIDTH && y <= height))
			{
			scrollH.mouseDown(x, y - (height-SCR_BUTTON_HEIGHT), button)
			clickDown = scrollH
			}
			else
			{
			clickDown = content
			
			if (content hastype XYMouseObject)
				{
				XYMouseObject cco = content
				cco.mouseDown(x+scrollX, y+scrollY, button)
				}
			}
		}
	
	void ScrollPane:mouseMove(int x, int y)
		{
		if (showScrollV && clickDown === scrollV)
			{
			scrollV.mouseMove(x, y)
			}
			else if (showScrollH && clickDown === scrollH)
			{
			scrollH.mouseMove(x, y)
			}
			else
			{
			if (content hastype XYMouseObject)
				{
				XYMouseObject cco = content
				cco.mouseMove(x+scrollX, y+scrollY)
				}
			}
		}
	
	void ScrollPane:mouseUp(int x, int y, int button)
		{
		if (showScrollV && clickDown === scrollV)
			{
			scrollV.mouseUp(x - (width-SCR_BUTTON_WIDTH), y, button)
			}
			else if (showScrollH && clickDown === scrollH)
			{
			scrollH.mouseUp(x, y - (height-SCR_BUTTON_HEIGHT), button)
			}
			else
			{
			if (content hastype XYMouseObject)
				{
				XYMouseObject cco = content
				cco.mouseUp(x+scrollX, y+scrollY, button)
				}
			
			if (content hastype ClickableObject && clickDown === content)
				{
				ClickableObject cco = content
				cco.click(x+scrollX, y+scrollY, button)
				}
			
			clickDown = null
			}
		}
	
	void ScrollPane:mouseOut()
		{
		if (content != null && content hastype XYMouseObject)
			{
			XYMouseObject cco = content
			cco.mouseOut()
			}
		}
	
	void ScrollPane:mouseWheel(int xAdd, int xSub, int yAdd, int ySub)
		{
		int maxScrollY = content.getPreferredSize().height - height

		if (ySub != 0)
			{
			if ((scrollY + (ySub * SCROLL_TICK)) > maxScrollY)
				{
				scrollY = maxScrollY
				}
				else
				{
				scrollY += (ySub * SCROLL_TICK)
				}
			
			scrollV.setScrollPos(scrollY)
			}
		
		if (yAdd != 0)
			{
			if (scrollY <= (yAdd * SCROLL_TICK))
				{
				scrollY = 0
				}
				else
				{
				scrollY -= (yAdd * SCROLL_TICK)
				}
			
			scrollV.setScrollPos(scrollY)
			}
		
		postRepaint()
		}
	
	void ScrollPane:dropFile(int x, int y, char path[])
		{
		if (showScrollV && (x >= width-SCR_BUTTON_WIDTH && x <= width))
			{
			}
			else if (showScrollH && (y >= height-SCR_BUTTON_HEIGHT && y <= height))
			{
			}
			else
			{
			if (content hastype XYMouseObject)
				{
				XYMouseObject cco = content
				cco.dropFile(x+scrollX, y+scrollY, path)
				}
			}
		}
	
	void ScrollPane:click(int x, int y, int button)
		{
		//check if the click location is on a scroll widget, otherwise pass-through to content
		if (showScrollV && x >= width-SCR_BUTTON_WIDTH && x <= width)
			{
			if (clickDown === scrollV)
				scrollV.click(x, y, button)
			}
			else if (showScrollH && y >= height-SCR_BUTTON_WIDTH && y <= height)
			{
			if (clickDown === scrollH)
				scrollH.click(x, y, button)
			}
			else
			{

			}
		}

	void ScrollPane:postRepaint()
		{
		emitevent repaint()
		}

	void ScrollPane:paint(Canvas c)
		{
		c.pushSurface(new Rect(xPosition, yPosition, width, height), 0, 0, 255)
		
		if (backgroundColor != null) c.rect(new Rect2D(0, 0, width, height, backgroundColor))
		
		c.pushSurface(new Rect(0, 0, width, height), scrollX, scrollY, 255)

		content.paint(c)

		c.popSurface()
		
		if (showScrollV)
			{
			scrollV.paint(c)
			}
		
		if (showScrollH)
			{
			scrollH.paint(c)
			}
		
		if (showScrollV && showScrollH)
			{
			//fill the square with something...
			c.rect(new Rect2D(width-SCR_BUTTON_WIDTH, height-SCR_BUTTON_HEIGHT, SCR_BUTTON_WIDTH, SCR_BUTTON_HEIGHT, gapColor))
			}
		
		c.popSurface()
		}

	void ScrollPane:setPosition(int x, int y)
		{
		xPosition = x
		yPosition = y
		}

	Point ScrollPane:getPosition()
		{
		return new Point(xPosition, yPosition)
		}

	WH ScrollPane:getPreferredSize()
		{
		return new WH(width, height)
		}
	
	void ScrollPane:setDisabled(bool d)
		{
		disabled = d
		postRepaint()
		}

}
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n ui.ScrollPane -m "reason for update" -u yourUsername
Version 2 (this version) by barry
Notes for this version: Corrections to prepare for upcoming compiler strictness changes in function parameter qualifier equivalence
Version 1 by barry