Browse Source

Merged in TheCosmotect's arrow navigation pull request

Ivan Safrin 12 years ago
parent
commit
f76540fd9a

+ 5 - 2
Modules/Contents/UI/Include/PolyUIScrollContainer.h

@@ -104,8 +104,11 @@ namespace Polycode {
         */
         */
 		Number getVScrollWidth();
 		Number getVScrollWidth();
 
 
-	private:
-
+		UIVScrollBar *getVScrollBar() { return vScrollBar; }
+		UIHScrollBar *getHScrollBar() { return hScrollBar; }
+		
+	private:		
+		
 		Number defaultScrollSize;
 		Number defaultScrollSize;
 
 
 		Number contentWidth;
 		Number contentWidth;

+ 6 - 0
Modules/Contents/UI/Include/PolyUITree.h

@@ -68,6 +68,12 @@ namespace Polycode {
 			String getLabelText();
 			String getLabelText();
 			
 			
 			Number handleRotation;
 			Number handleRotation;
+
+			UITree *getParent();
+			bool hasTreeChildren() { return (getNumTreeChildren()); }
+			UITree *getPrevSibling();
+			UITree *getNextSibling();
+			Number getCellHeight() { return cellHeight; }
 		
 		
 		private:
 		private:
 
 

+ 19 - 2
Modules/Contents/UI/Include/PolyUITreeContainer.h

@@ -39,6 +39,24 @@ namespace Polycode {
 		
 		
 		UITree *getRootNode();
 		UITree *getRootNode();
 		ScreenEntity *scrollChild;
 		ScreenEntity *scrollChild;
+		
+		void onKeyDown(PolyKEY key, wchar_t charCode);
+		void onGainFocus();
+		
+		/**
+		 * Scrolls the container to show a specified node.
+		 * @param node The tree node to scroll to or show.
+		 * @param showAtTop If true, show the node at the top of the container. If false, show it at the bottom.
+		 * @param select If true (default), select the node. If false, don't.
+		 */
+		void scrollToNode(UITree *node, bool showAtTop, bool select=true);
+		
+	protected:
+		bool keyNavigable;
+		
+		UITree *findLastOpenNode(UITree *tree);
+		UITree *findNextParentSibling(UITree *parent);
+		
 	private:
 	private:
 		
 		
 		UIScrollContainer *mainContainer;
 		UIScrollContainer *mainContainer;
@@ -47,5 +65,4 @@ namespace Polycode {
 		UITree *rootNode;
 		UITree *rootNode;
 		UIBox *bgBox;
 		UIBox *bgBox;
 	};
 	};
-
-}
+}

+ 26 - 0
Modules/Contents/UI/Source/PolyUITree.cpp

@@ -340,3 +340,29 @@ UITree *UITree::addTreeChild(String icon, String text, void *userData) {
 	refreshTree();
 	refreshTree();
 	return newTree;
 	return newTree;
 }
 }
+
+UITree *UITree::getParent() {
+	return parent;
+}
+
+UITree *UITree::getPrevSibling() {
+	UITree *sibling = NULL;
+	if (!parent)
+		return sibling;
+	for (int i=0; i < parent->getNumTreeChildren(); i++) {
+		if (i > 0 && parent->getTreeChild(i) == this)
+			sibling = parent->getTreeChild(i-1);
+	}
+	return sibling;
+}
+
+UITree *UITree::getNextSibling() {
+	UITree *sibling = NULL;
+	if (!parent)
+		return sibling;
+	for (int i=0; i < parent->getNumTreeChildren(); i++) {
+		if (parent->getTreeChild(i) == this && i < parent->getNumTreeChildren()-1)
+			sibling = parent->getTreeChild(i+1);
+	}
+	return sibling;
+}

+ 151 - 0
Modules/Contents/UI/Source/PolyUITreeContainer.cpp

@@ -25,6 +25,7 @@
 #include "PolyInputEvent.h"
 #include "PolyInputEvent.h"
 #include "PolyLabel.h"
 #include "PolyLabel.h"
 #include "PolyCoreServices.h"
 #include "PolyCoreServices.h"
+#include "PolyCore.h"
 
 
 using namespace Polycode;
 using namespace Polycode;
 
 
@@ -64,6 +65,11 @@ UITreeContainer::UITreeContainer(String icon, String text, Number treeWidth, Num
 	setHitbox(width, height);
 	setHitbox(width, height);
 	
 	
 	Resize(width, height);
 	Resize(width, height);
+
+	CoreServices::getInstance()->getCore()->getInput()->addEventListener(this, InputEvent::EVENT_KEYDOWN);
+	this->addEventListener(this, InputEvent::EVENT_MOUSEDOWN);
+	focusable = true;
+	keyNavigable = true;
 }
 }
 
 
 void UITreeContainer::Resize(Number width, Number height) {
 void UITreeContainer::Resize(Number width, Number height) {
@@ -84,6 +90,13 @@ void UITreeContainer::handleEvent(Event *event) {
 		}
 		}
 	}
 	}
 	
 	
+	if (!hasFocus && event->getDispatcher() == this) {
+		if (event->getEventCode() == InputEvent::EVENT_MOUSEDOWN) {
+			if (parentEntity && isFocusable())
+				((ScreenEntity*)parentEntity)->focusChild(this);
+		}
+	}
+
 }
 }
 
 
 UITree *UITreeContainer::getRootNode() {
 UITree *UITreeContainer::getRootNode() {
@@ -98,3 +111,141 @@ UITreeContainer::~UITreeContainer() {
 		delete mainContainer;
 		delete mainContainer;
 	}
 	}
 }
 }
+
+void UITreeContainer::onGainFocus() {
+	if (!getRootNode()->getSelectedNode())
+		getRootNode()->setSelected();
+}
+
+// RECURSIVE HELPER FUNCTIONS
+//
+// used in KEY_UP tree nav
+UITree *UITreeContainer::findLastOpenNode(UITree *tree) {
+	if (!tree->hasTreeChildren() || tree->isCollapsed())
+		return tree;
+	UITree *t = tree->getTreeChild(tree->getNumTreeChildren()-1);
+	if (t->isCollapsed() || !t->hasTreeChildren())
+		return t;
+	else
+		return findLastOpenNode(t);
+}
+// used in KEY_DOWN tree nav
+UITree *UITreeContainer::findNextParentSibling(UITree *parent) {
+	UITree *sibling = parent->getNextSibling();
+	if (sibling)
+		return sibling;
+	else {
+		if (parent->getParent())
+			return findNextParentSibling(parent->getParent());
+		else
+			return NULL;
+	}
+}
+//
+// END RECURSIVE HELPER FUNCTIONS
+
+void UITreeContainer::onKeyDown(PolyKEY key, wchar_t charCode) {
+	if (hasFocus) {
+		
+		// KEYBOARD NAV STUFF
+		// TODO: add ability to jump to prev/next sibling via holding down some button
+		//
+		if (keyNavigable) {
+			enum { NONE, UP, DOWN } scrollDir = NONE;
+			UITree *currentSelection = getRootNode()->getSelectedNode();
+			if (!currentSelection)
+				return;
+			UITree *parent = currentSelection->getParent();
+			
+			// - select parent if currently selected node is first in the tree
+			// - else, select the last opened node in a sibling above
+			if (key == KEY_UP) {
+				if (parent) {
+					for (int i=0; i < parent->getNumTreeChildren(); i++) {
+						if (parent->getTreeChild(i) == currentSelection) {
+							if (i == 0)
+								parent->setSelected();
+							else
+								findLastOpenNode((parent->getTreeChild(i-1)))->setSelected();
+							scrollDir = UP;
+							break;
+						}
+					}
+				}
+			}
+			
+			// - select first child of currently selected if expanded and has children
+			// - or select next sibling
+			// - else, select the next sibling of some ancestor node
+			if (key == KEY_DOWN) {
+				if (currentSelection == getRootNode())
+					parent = getRootNode();
+
+				if (currentSelection->hasTreeChildren() && !currentSelection->isCollapsed()) {
+					currentSelection->getTreeChild(0)->setSelected();
+					scrollDir = DOWN;
+				}
+				else {
+					for (int i=0; i < parent->getNumTreeChildren(); i++) {
+						if (parent->getTreeChild(i) == currentSelection) {
+							if (i == parent->getNumTreeChildren()-1) {
+								UITree *psib = findNextParentSibling(parent);
+								if (psib)
+									psib->setSelected();
+							} else {
+								parent->getTreeChild(i+1)->setSelected();
+							}
+							scrollDir = DOWN;
+							break;
+						}
+					}
+				}
+			}
+			
+			if (key == KEY_LEFT) {
+				if (currentSelection->hasTreeChildren() && !currentSelection->isCollapsed())
+					currentSelection->toggleCollapsed();
+				else if (parent) {
+					parent->setSelected();
+					scrollDir = UP;
+				}
+			}
+			
+			if (key == KEY_RIGHT) {
+				if (currentSelection->hasTreeChildren()) {
+					if (currentSelection->isCollapsed())
+						currentSelection->toggleCollapsed();
+					else {
+						currentSelection->getTreeChild(0)->setSelected();
+						scrollDir = DOWN;
+					}
+				}
+			}
+			
+			if (scrollDir != NONE)
+				scrollToNode(getRootNode()->getSelectedNode(), (scrollDir == UP) ? true : false, false);
+		}
+		//
+		// END KEYBOARD NAV STUFF
+		
+	}
+}
+
+void UITreeContainer::scrollToNode(UITree *node, bool showAtTop, bool select) {
+	
+	Number nodeY = node->getScreenPosition().y - getRootNode()->getScreenPosition().y;
+	Number contentHeight = mainContainer->getContentSize().y;
+	Number scrollHeight = contentHeight - mainContainer->getHeight();
+	Number viewTop = (contentHeight - mainContainer->getHeight()) * mainContainer->getVScrollBar()->getScrollValue();
+	Number viewBottom = viewTop + mainContainer->getHeight();
+	
+	if (select)
+		node->setSelected();
+	
+	if (nodeY < viewTop || nodeY+node->getCellHeight() > viewBottom) {
+		if (showAtTop)
+			mainContainer->scrollVertical((nodeY-viewTop) / scrollHeight);
+		else
+			mainContainer->scrollVertical((nodeY+node->getCellHeight()-viewBottom) / scrollHeight);
+	}
+}