GWEN improvement: allow to navigate TreeControl and ComboBox using the cursor keys

This commit is contained in:
erwin coumans
2013-07-01 23:43:49 -07:00
parent 898f423f95
commit 0f78c696b5
14 changed files with 458 additions and 17 deletions

View File

@@ -81,8 +81,10 @@ Base::~Base()
SetParent( NULL );
if ( Gwen::HoveredControl == this ) Gwen::HoveredControl = NULL;
if ( Gwen::KeyboardFocus == this ) Gwen::KeyboardFocus = NULL;
if ( Gwen::HoveredControl == this )
Gwen::HoveredControl = NULL;
if ( Gwen::KeyboardFocus == this )
Gwen::KeyboardFocus = NULL;
if ( Gwen::MouseFocus == this ) Gwen::MouseFocus = NULL;
DragAndDrop::ControlDeleted( this );

View File

@@ -176,11 +176,13 @@ bool ComboBox::OnKeyUp( bool bDown )
{
if ( bDown )
{
Base::List::reverse_iterator it = std::find( m_Menu->Children.rbegin(), m_Menu->Children.rend(), m_SelectedItem );
if ( it != m_Menu->Children.rend() && ( ++it != m_Menu->Children.rend() ) )
Base::List & children = m_Menu->GetChildren();
Base::List::reverse_iterator it = std::find( children.rbegin(), children.rend(), m_SelectedItem );
if ( it != children.rend() && ( ++it != children.rend() ) )
{
Base* pUpElement = *it;
OnItemSelected(pUpElement);
OnItemSelected( pUpElement );
}
}
return true;
@@ -189,11 +191,13 @@ bool ComboBox::OnKeyDown( bool bDown )
{
if ( bDown )
{
Base::List::iterator it = std::find( m_Menu->Children.begin(), m_Menu->Children.end(), m_SelectedItem );
if ( it != m_Menu->Children.end() && ( ++it != m_Menu->Children.end() ) )
Base::List & children = m_Menu->GetChildren();
Base::List::iterator it = std::find( children.begin(), children.end(), m_SelectedItem );
if ( it != children.end() && ( ++it != children.end() ) )
{
Base* pDownElement = *it;
OnItemSelected(pDownElement);
OnItemSelected( pDownElement );
}
}
return true;

View File

@@ -61,6 +61,7 @@ namespace Gwen
bool m_bAutoHideBars;
public:
Controls::BaseScrollBar* m_VerticalScrollBar;
Controls::BaseScrollBar* m_HorizontalScrollBar;
};

View File

@@ -72,3 +72,108 @@ void TreeControl::OnNodeSelection( Controls::Base* /*control*/ )
if ( !m_bAllowMultipleSelection || !Gwen::Input::IsKeyDown( Key::Control ) )
DeselectAll();
}
void TreeControl::iterate(int action, int* curIndex, int* targetIndex)
{
Base::List& children = m_InnerPanel->GetChildren();
for ( Base::List::iterator iter = children.begin(); iter != children.end(); ++iter )
{
TreeNode* pChild = (*iter)->DynamicCastTreeNode();
if ( !pChild )
continue;
pChild->iterate(action ,curIndex, targetIndex);
}
}
bool TreeControl::OnKeyUp( bool bDown )
{
if (bDown)
{
int maxIndex = 0;
int newIndex = 0;
int curIndex=0;
int targetIndex=-1;
iterate(ITERATE_ACTION_FIND_SELECTED_INDEX,&curIndex,&targetIndex);
maxIndex = curIndex;
if (targetIndex>0)
{
curIndex=0;
int deselectIndex = targetIndex;
targetIndex--;
newIndex = targetIndex;
iterate(ITERATE_ACTION_SELECT,&curIndex,&targetIndex);
if (targetIndex<0)
{
curIndex=0;
iterate(ITERATE_ACTION_DESELECT_INDEX,&curIndex,&deselectIndex);
}
float amount = float(newIndex)/float(maxIndex);
m_ScrollControl->m_VerticalScrollBar->SetScrolledAmount(amount,true);
}
}
return true;
}
bool TreeControl::OnKeyDown( bool bDown )
{
if (bDown)
{
int maxIndex = 0;
int newIndex = 0;
int curIndex=0;
int targetIndex=-1;
iterate(ITERATE_ACTION_FIND_SELECTED_INDEX,&curIndex,&targetIndex);
maxIndex = curIndex;
if (targetIndex>=0)
{
curIndex=0;
int deselectIndex = targetIndex;
targetIndex++;
newIndex = targetIndex;
iterate(ITERATE_ACTION_SELECT,&curIndex,&targetIndex);
if (targetIndex<0)
{
curIndex=0;
iterate(ITERATE_ACTION_DESELECT_INDEX,&curIndex,&deselectIndex);
}
float amount = float(newIndex)/float(maxIndex);
m_ScrollControl->m_VerticalScrollBar->SetScrolledAmount(amount,true);
}
}
return true;
}
bool TreeControl::OnKeyRight( bool bDown )
{
if (bDown)
{
iterate(ITERATE_ACTION_OPEN,0,0);
int curIndex=0;
int targetIndex=0;
iterate(ITERATE_ACTION_FIND_SELECTED_INDEX,&curIndex,&targetIndex);
float amount = float(targetIndex)/float(curIndex);
m_ScrollControl->m_VerticalScrollBar->SetScrolledAmount(amount,true);
}
return true;
}
bool TreeControl::OnKeyLeft( bool bDown )
{
if (bDown)
{
iterate(ITERATE_ACTION_CLOSE,0,0);
int curIndex=0;
int targetIndex=0;
iterate(ITERATE_ACTION_FIND_SELECTED_INDEX,&curIndex,&targetIndex);
float amount = float(targetIndex)/float(curIndex);
m_ScrollControl->m_VerticalScrollBar->SetScrolledAmount(amount,true);
}
return true;
}

View File

@@ -36,6 +36,15 @@ namespace Gwen
virtual void OnNodeAdded( TreeNode* pNode );
virtual bool OnKeyUp( bool bDown );
virtual bool OnKeyDown( bool bDown );
virtual bool OnKeyRight( bool bDown );
virtual bool OnKeyLeft( bool bDown );
virtual void iterate(int action,int* curIndex, int* resultIndex);
private:
void OnNodeSelection( Controls::Base* control );

View File

@@ -209,3 +209,123 @@ void TreeNode::DeselectAll()
pChild->DeselectAll( );
}
}
void TreeNode::iterate(int action, int* curIndex, int* targetIndex)
{
Gwen::String name = Gwen::Utility::UnicodeToString(m_Title->GetText());
int actualIndex = curIndex? *curIndex : -1;
//printf("iterated over item %d with name = %s\n", actualIndex, name.c_str());
if (action==ITERATE_ACTION_SELECT)
{
if (curIndex && targetIndex)
{
if ((*curIndex)==(*targetIndex))
{
SetSelected(true);
*targetIndex=-1;
}
}
}
if (IsSelected())
{
//printf("current selected: name = %s\n", name.c_str());
switch (action)
{
case ITERATE_ACTION_DESELECT_INDEX:
{
if (targetIndex && curIndex)
{
if (*targetIndex == *curIndex)
SetSelected(false);
}
break;
}
case ITERATE_ACTION_FIND_SELECTED_INDEX:
{
if (targetIndex && curIndex)
{
*targetIndex = *curIndex;
}
break;
}
case ITERATE_ACTION_OPEN:
{
Open();
break;
}
case ITERATE_ACTION_CLOSE:
{
//either close or select parent
if (this->GetChildren().size())
{
if (m_ToggleButton && m_ToggleButton->GetToggleState())
{
Close();
} else
{
TreeNode* pChild = (GetParent())->DynamicCastTreeNode();
TreeControl* pChild2 = (GetParent())->DynamicCastTreeControl();
if (pChild && !pChild2)
{
SetSelected(false);
pChild->SetSelected(true);
}
}
}
else
{
TreeNode* pChild = (GetParent())->DynamicCastTreeNode();
TreeControl* pChild2 = (GetParent())->DynamicCastTreeControl();
if (pChild && !pChild2)
{
SetSelected(false);
pChild->SetSelected(true);
}
}
break;
}
default:
{
}
};
}
if (curIndex)
(*curIndex)++;
bool needsRecursion = true;
if (action == ITERATE_ACTION_FIND_SELECTED_INDEX || action==ITERATE_ACTION_SELECT || action==ITERATE_ACTION_DESELECT_INDEX)
{
if (m_ToggleButton && !m_ToggleButton->GetToggleState())
{
needsRecursion=false;
}
}
if (needsRecursion)
{
Base::List& children = m_InnerPanel->GetChildren();
for ( Base::List::iterator iter = children.begin(); iter != children.end(); ++iter )
{
TreeNode* pChild = (*iter)->DynamicCastTreeNode();
if ( !pChild )
continue;
pChild->iterate(action , curIndex, targetIndex);
}
}
}

View File

@@ -12,6 +12,14 @@
#include "Gwen/Controls/Button.h"
#include "Gwen/Controls/ScrollControl.h"
enum
{
ITERATE_ACTION_OPEN=1,
ITERATE_ACTION_CLOSE,
ITERATE_ACTION_FIND_SELECTED_INDEX,
ITERATE_ACTION_DESELECT_INDEX,
ITERATE_ACTION_SELECT,
};
namespace Gwen
{
@@ -51,6 +59,9 @@ namespace Gwen
virtual void DeselectAll();
virtual void iterate(int action, int* curIndex, int* resultIndex);
Event::Caller onNamePress;
Event::Caller onSelectChange;
Event::Caller onSelect;

View File

@@ -125,8 +125,14 @@ void Gwen::Input::OnCanvasThink( Controls::Base* pControl )
if ( Gwen::MouseFocus && !Gwen::MouseFocus->Visible() )
Gwen::MouseFocus = NULL;
if ( Gwen::KeyboardFocus && ( !Gwen::KeyboardFocus->Visible() || !KeyboardFocus->GetKeyboardInputEnabled() ) )
Gwen::KeyboardFocus = NULL;
if (Gwen::KeyboardFocus )
{
bool isVisible = Gwen::KeyboardFocus->Visible();
bool isEnabled = KeyboardFocus->GetKeyboardInputEnabled();
if ( !isVisible || !isEnabled )
Gwen::KeyboardFocus = NULL;
}
if ( !KeyboardFocus ) return;
if ( KeyboardFocus->GetCanvas() != pControl ) return;

View File

@@ -12,6 +12,7 @@ class ComboBox : public GUnit
{
Gwen::Controls::ComboBox* combo = new Gwen::Controls::ComboBox( this );
combo->SetKeyboardInputEnabled(true);
combo->SetPos( 50, 50 );
combo->SetWidth( 200 );

View File

@@ -214,12 +214,95 @@ sth_stash* initFont(GLPrimitiveRenderer* primRenderer)
return stash;
}
void keyCallback(int key, int value)
{
printf("key = %d, value = %d\n", key,value);
//pCanvas->InputKey(key,value==1);
int gwenKey = -1;
switch (key)
{
case B3G_LEFT_ARROW:
{
gwenKey = Gwen::Key::Left;
break;
}
case B3G_RIGHT_ARROW:
{
gwenKey = Gwen::Key::Right;
break;
}
case B3G_UP_ARROW:
{
gwenKey = Gwen::Key::Up;
break;
}
case B3G_DOWN_ARROW:
{
gwenKey = Gwen::Key::Down;
break;
}
case B3G_BACKSPACE:
{
gwenKey = Gwen::Key::Backspace;
break;
}
case B3G_DELETE:
{
gwenKey = Gwen::Key::Delete;
break;
}
case B3G_HOME:
{
gwenKey = Gwen::Key::Home;
break;
}
case B3G_END:
{
gwenKey = Gwen::Key::End;
break;
}
case B3G_SHIFT:
{
gwenKey = Gwen::Key::Shift;
break;
}
case B3G_CONTROL:
{
gwenKey = Gwen::Key::Control;
break;
}
default:
{
}
};
if (gwenKey>=0)
{
pCanvas->InputKey(gwenKey,value==1);
} else
{
if (key<256 && value)
{
Gwen::UnicodeChar c = ( Gwen::UnicodeChar ) key;
pCanvas->InputCharacter(c);
}
}
}
int main()
{
float retinaScale = 1.f;
b3gDefaultOpenGLWindow* window = new b3gDefaultOpenGLWindow();
window->setKeyboardCallback(keyCallback);
b3gWindowConstructionInfo wci;
wci.m_width = sWidth;
wci.m_height = sHeight;
@@ -241,6 +324,7 @@ int main()
gwenRenderer = new GwenOpenGL3CoreRenderer(primRenderer,font,sWidth,sHeight,retinaScale);
//
// Create a GWEN OpenGL Renderer
//

View File

@@ -15,12 +15,17 @@ class TreeControl2 : public GUnit
{
Gwen::Controls::TreeControl* ctrl = new Gwen::Controls::TreeControl( this );
ctrl->SetKeyboardInputEnabled(true);
ctrl->AddNode( L"Node One" );
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
pNode->SetSelected(true);
pNode->AddNode( L"Brown" )->AddNode( L"Node Two Inside" )->AddNode( L"Eyes" )->AddNode( L"Brown" );
ctrl->AddNode( L"Node Three" );
ctrl->Focus();
ctrl->SetKeyboardInputEnabled(true);
ctrl->SetBounds( 30, 30, 200, 200 );
ctrl->ExpandAll();

View File

@@ -6,6 +6,7 @@
#include "UnitTest.h"
#include "Gwen/Platform.h"
#include "Gwen/Controls/TreeControl.h"
using namespace Gwen;
@@ -30,7 +31,80 @@ GWEN_CONTROL_CONSTRUCTOR( UnitTest )
//ADD_UNIT_TEST( MenuStrip );
ADD_UNIT_TEST( TreeControl2 );
Gwen::UnicodeString str1(L"testje");
Gwen::Controls::TabButton* tab = m_TabControl->AddPage(str1);
Gwen::Controls::TreeControl* ctrl=0;
{
ctrl = new Gwen::Controls::TreeControl(tab->GetPage());
ctrl->SetKeyboardInputEnabled(true);
ctrl->AddNode( L"Node One" );
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
}
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
}
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
}
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
}
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
}
{
Gwen::Controls::TreeNode* pNode = ctrl->AddNode( L"Node Two" );
pNode->AddNode( L"Node Two Inside" );
pNode->AddNode( L"Eyes" );
pNode->SetSelected(true);
pNode->AddNode( L"Brown" )->AddNode( L"Node Two Inside" )->AddNode( L"Eyes" )->AddNode( L"Brown" );
}
ctrl->AddNode( L"Node Three" );
ctrl->Focus();
ctrl->SetKeyboardInputEnabled(true);
ctrl->SetBounds( 30, 30, 200, 200 );
ctrl->ExpandAll();
}
//GUnit* u = new TreeControl2(m_TabControl);..Gwen::Controls::TreeControl2( m_TabControl );
//GUnit* RegisterUnitTest_TreeControl2( Gwen::Controls::TabControl* tab );\
//RegisterUnitTest_TreeControl2( m_TabControl )->SetUnitTest( this );
//#define DEFINE_UNIT_TEST( name, displayname )
//GUnit* RegisterUnitTest_TreeControl2( Gwen::Controls::TabControl* tab )
//{
// GUnit* u = new TreeControl2( tab );
// tab->AddPage( displayname, u );
// return u;
//}
//ADD_UNIT_TEST( TreeControl2 );
ADD_UNIT_TEST( Properties2 );
@@ -59,6 +133,7 @@ GWEN_CONTROL_CONSTRUCTOR( UnitTest )
ADD_UNIT_TEST( StatusBar );
ctrl->Focus();
PrintText( L"Unit Test Started.\n" );
m_fLastSecond = Gwen::Platform::GetTimeInSeconds();

View File

@@ -68,12 +68,13 @@ int getAsciiCodeFromVirtualKeycode(int virtualKeyCode)
case VK_F9: {keycode = B3G_F9; break;}
case VK_F10: {keycode= B3G_F10; break;}
case VK_SPACE: {keycode= ' '; break;}
//case VK_SPACE: {keycode= ' '; break;}
case VK_NEXT: {keycode= B3G_PAGE_DOWN; break;}
case VK_PRIOR: {keycode= B3G_PAGE_UP; break;}
case VK_INSERT: {keycode= B3G_INSERT; break;}
case VK_BACK: {keycode= B3G_BACKSPACE; break;}
case VK_DELETE: {keycode= B3G_DELETE; break;}
case VK_END:{keycode= B3G_END; break;}
@@ -82,9 +83,11 @@ int getAsciiCodeFromVirtualKeycode(int virtualKeyCode)
case VK_UP:{keycode= B3G_UP_ARROW; break;}
case VK_RIGHT:{keycode= B3G_RIGHT_ARROW; break;}
case VK_DOWN:{keycode= B3G_DOWN_ARROW; break;}
case VK_SHIFT:{keycode=B3G_SHIFT;break;}
case VK_CONTROL:{keycode=B3G_CONTROL;break;}
default:
{
keycode = MapVirtualKey( virtualKeyCode, MAPVK_VK_TO_CHAR ) & 0x0000FFFF;
//keycode = MapVirtualKey( virtualKeyCode, MAPVK_VK_TO_CHAR ) & 0x0000FFFF;
}
};
@@ -127,22 +130,34 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
int keycode = getAsciiCodeFromVirtualKeycode(wParam);
if (sData && sData->m_keyboardCallback )
if (keycode>=0 && sData && sData->m_keyboardCallback )
{
int state=0;
(*sData->m_keyboardCallback)(keycode,state);
}
return 0;
}
case WM_CHAR:
{
int keycode = wParam;
if (sData && sData->m_keyboardCallback && ((HIWORD(lParam) & KF_REPEAT) == 0))
{
int state = 1;
(*sData->m_keyboardCallback)(keycode,state);
}
return 0;
}
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
int keycode = getAsciiCodeFromVirtualKeycode(wParam);
if (sData && sData->m_keyboardCallback && ((HIWORD(lParam) & KF_REPEAT) == 0))
if (keycode>=0 && sData && sData->m_keyboardCallback && ((HIWORD(lParam) & KF_REPEAT) == 0))
{
int state = 1;
(*sData->m_keyboardCallback)(keycode,state);
return 1;
}
return 0;
}

View File

@@ -35,7 +35,10 @@ enum {
B3G_END,
B3G_HOME,
B3G_INSERT,
B3G_DELETE
B3G_DELETE,
B3G_BACKSPACE,
B3G_SHIFT,
B3G_CONTROL
};
struct b3gWindowConstructionInfo