From 4f8f02acad3836fb8212e0d7898f11a6e624a56b Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 26 Jan 2013 17:06:25 -0200 Subject: [PATCH] Rework auto focus behavior, closes #222 --- data/styles/10-panels.otui | 2 + modules/client_entergame/characterlist.otui | 13 +-- modules/client_entergame/entergame.lua | 1 - modules/corelib/const.lua | 4 + modules/corelib/ui/uiwindow.lua | 1 + src/framework/const.h | 6 + src/framework/luafunctions.cpp | 2 + src/framework/ui/uitextedit.cpp | 4 +- src/framework/ui/uitranslator.cpp | 14 ++- src/framework/ui/uitranslator.h | 1 + src/framework/ui/uiwidget.cpp | 118 +++++++++++++------- src/framework/ui/uiwidget.h | 7 +- src/framework/ui/uiwidgetbasestyle.cpp | 2 + 13 files changed, 123 insertions(+), 52 deletions(-) diff --git a/data/styles/10-panels.otui b/data/styles/10-panels.otui index d4e4e5a3..15fbbc30 100644 --- a/data/styles/10-panels.otui +++ b/data/styles/10-panels.otui @@ -1,8 +1,10 @@ Panel < UIWidget phantom: true + auto-focus: first ScrollablePanel < UIScrollArea phantom: true + auto-focus: first FlatPanel < Panel image-source: /images/ui/panel_flat diff --git a/modules/client_entergame/characterlist.otui b/modules/client_entergame/characterlist.otui index 01ca8370..858d6639 100644 --- a/modules/client_entergame/characterlist.otui +++ b/modules/client_entergame/characterlist.otui @@ -13,21 +13,21 @@ CharacterWidget < UIWidget Label id: name - color: #ffffff + color: #aaaaaa anchors.top: parent.top anchors.left: parent.left font: verdana-11px-monochrome text-auto-resize: true background-color: alpha text-offset: 2 0 - on: true - $!on: - color: #aaaaaa + $on: + color: #ffffff Label id: worldName color: #ffffff + color: #aaaaaa anchors.top: parent.top anchors.right: parent.right margin-right: 5 @@ -35,10 +35,9 @@ CharacterWidget < UIWidget text-auto-resize: true background-color: alpha &baseText: '(%s)' - on: true - $!on: - color: #aaaaaa + $on: + color: #ffffff MainWindow id: charactersWindow diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index b1f901c5..98f02396 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -97,7 +97,6 @@ function EnterGame.init() enterGame:getChildById('serverPortTextEdit'):setText(port) enterGame:getChildById('autoLoginBox'):setChecked(autologin) enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0) - enterGame:getChildById('accountNameTextEdit'):focus() protocolBox = enterGame:getChildById('protocolComboBox') protocolBox.onOptionChange = onChangeProtocol diff --git a/modules/corelib/const.lua b/modules/corelib/const.lua index 8e0d76c0..d4493333 100644 --- a/modules/corelib/const.lua +++ b/modules/corelib/const.lua @@ -19,6 +19,10 @@ KeyboardFocusReason = 1 ActiveFocusReason = 2 OtherFocusReason = 3 +AutoFocusNone = 0 +AutoFocusFirst = 1 +AutoFocusLast = 2 + KeyboardNoModifier = 0 KeyboardCtrlModifier = 1 KeyboardAltModifier = 2 diff --git a/modules/corelib/ui/uiwindow.lua b/modules/corelib/ui/uiwindow.lua index 4ced7f2e..e77473a4 100644 --- a/modules/corelib/ui/uiwindow.lua +++ b/modules/corelib/ui/uiwindow.lua @@ -5,6 +5,7 @@ function UIWindow.create() local window = UIWindow.internalCreate() window:setTextAlign(AlignTopCenter) window:setDraggable(true) + window:setAutoFocusPolicy(AutoFocusFirst) return window end diff --git a/src/framework/const.h b/src/framework/const.h index b8daa9e6..de4b6740 100644 --- a/src/framework/const.h +++ b/src/framework/const.h @@ -226,6 +226,12 @@ namespace Fw OtherFocusReason }; + enum AutoFocusPolicy { + AutoFocusNone = 0, + AutoFocusFirst, + AutoFocusLast + }; + enum InputEventType { NoInputEvent = 0, KeyTextInputEvent, diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 1d0b15d1..ea0c2fa4 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -377,6 +377,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("setFixedSize", &UIWidget::setFixedSize); g_lua.bindClassMemberFunction("setClipping", &UIWidget::setClipping); g_lua.bindClassMemberFunction("setLastFocusReason", &UIWidget::setLastFocusReason); + g_lua.bindClassMemberFunction("setAutoFocusPolicy", &UIWidget::setAutoFocusPolicy); g_lua.bindClassMemberFunction("setAutoRepeatDelay", &UIWidget::setAutoRepeatDelay); g_lua.bindClassMemberFunction("setVirtualOffset", &UIWidget::setVirtualOffset); g_lua.bindClassMemberFunction("isVisible", &UIWidget::isVisible); @@ -442,6 +443,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("getStyle", &UIWidget::getStyle); g_lua.bindClassMemberFunction("getChildCount", &UIWidget::getChildCount); g_lua.bindClassMemberFunction("getLastFocusReason", &UIWidget::getLastFocusReason); + g_lua.bindClassMemberFunction("getAutoFocusPolicy", &UIWidget::getAutoFocusPolicy); g_lua.bindClassMemberFunction("getAutoRepeatDelay", &UIWidget::getAutoRepeatDelay); g_lua.bindClassMemberFunction("getVirtualOffset", &UIWidget::getVirtualOffset); g_lua.bindClassMemberFunction("getStyleName", &UIWidget::getStyleName); diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp index 124db766..00630115 100644 --- a/src/framework/ui/uitextedit.cpp +++ b/src/framework/ui/uitextedit.cpp @@ -689,7 +689,7 @@ bool UITextEdit::onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeat } else if(keyCode == Fw::KeyTab && !m_shiftNavigation) { clearSelection(); if(UIWidgetPtr parent = getParent()) - parent->focusNextChild(Fw::KeyboardFocusReason); + parent->focusNextChild(Fw::KeyboardFocusReason, true); return true; } else if(keyCode == Fw::KeyEnter && m_multiline && m_editable) { appendCharacter('\n'); @@ -724,7 +724,7 @@ bool UITextEdit::onKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeat } else if(keyboardModifiers == Fw::KeyboardShiftModifier) { if(keyCode == Fw::KeyTab && !m_shiftNavigation) { if(UIWidgetPtr parent = getParent()) - parent->focusPreviousChild(Fw::KeyboardFocusReason); + parent->focusPreviousChild(Fw::KeyboardFocusReason, true); return true; } else if(keyCode == Fw::KeyRight || keyCode == Fw::KeyLeft) { diff --git a/src/framework/ui/uitranslator.cpp b/src/framework/ui/uitranslator.cpp index 683717b8..801f86e4 100644 --- a/src/framework/ui/uitranslator.cpp +++ b/src/framework/ui/uitranslator.cpp @@ -98,6 +98,16 @@ Fw::WidgetState Fw::translateState(std::string state) return Fw::DraggingState; else if(state == "hidden") return Fw::HiddenState; - else - return Fw::InvalidState; + return Fw::InvalidState; +} + +Fw::AutoFocusPolicy Fw::translateAutoFocusPolicy(std::string policy) +{ + boost::to_lower(policy); + boost::trim(policy); + if(policy == "first") + return Fw::AutoFocusFirst; + else if(policy == "last") + return Fw::AutoFocusLast; + return Fw::AutoFocusNone; } diff --git a/src/framework/ui/uitranslator.h b/src/framework/ui/uitranslator.h index 0e19031c..f4fb54d6 100644 --- a/src/framework/ui/uitranslator.h +++ b/src/framework/ui/uitranslator.h @@ -31,6 +31,7 @@ namespace Fw { AlignmentFlag translateAlignment(std::string aligment); AnchorEdge translateAnchorEdge(std::string anchorEdge); WidgetState translateState(std::string state); +AutoFocusPolicy translateAutoFocusPolicy(std::string policy); }; diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 0e7e7aa0..8c637116 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -36,6 +36,7 @@ UIWidget::UIWidget() { m_lastFocusReason = Fw::ActiveFocusReason; m_states = Fw::DefaultState; + m_autoFocusPolicy = Fw::AutoFocusLast; m_clickTimer.stop(); m_autoRepeatDelay = 500; @@ -239,8 +240,8 @@ void UIWidget::removeChild(UIWidgetPtr child) child->updateStates(); updateChildrenIndexStates(); - if(focusAnother && !m_focusedChild) - focusPreviousChild(Fw::ActiveFocusReason); + if(m_autoFocusPolicy != Fw::AutoFocusNone && focusAnother && !m_focusedChild) + focusPreviousChild(Fw::ActiveFocusReason, true); g_ui.onWidgetDisappear(child); } else @@ -283,60 +284,89 @@ void UIWidget::focusChild(const UIWidgetPtr& child, Fw::FocusReason reason) onChildFocusChange(child, oldFocused, reason); } -void UIWidget::focusNextChild(Fw::FocusReason reason) +void UIWidget::focusNextChild(Fw::FocusReason reason, bool rotate) { if(m_destroyed) return; UIWidgetPtr toFocus; - UIWidgetList rotatedChildren(m_children); - if(m_focusedChild) { - auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild); - if(focusedIt != rotatedChildren.end()) { - std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end()); - rotatedChildren.pop_front(); + if(rotate) { + UIWidgetList rotatedChildren(m_children); + + if(m_focusedChild) { + auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild); + if(focusedIt != rotatedChildren.end()) { + std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end()); + rotatedChildren.pop_front(); + } } - } - // finds next child to focus - for(const UIWidgetPtr& child : rotatedChildren) { - if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { - toFocus = child; - break; + // finds next child to focus + for(const UIWidgetPtr& child : rotatedChildren) { + if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { + toFocus = child; + break; + } + } + } else { + auto it = m_children.begin(); + if(m_focusedChild) + it = std::find(m_children.begin(), m_children.end(), m_focusedChild); + + for(; it != m_children.end(); ++it) { + const UIWidgetPtr& child = *it; + if(child != m_focusedChild && child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { + toFocus = child; + break; + } } } - if(toFocus) + if(toFocus && toFocus != m_focusedChild) focusChild(toFocus, reason); } -void UIWidget::focusPreviousChild(Fw::FocusReason reason) +void UIWidget::focusPreviousChild(Fw::FocusReason reason, bool rotate) { if(m_destroyed) return; UIWidgetPtr toFocus; - UIWidgetList rotatedChildren(m_children); - std::reverse(rotatedChildren.begin(), rotatedChildren.end()); - - if(m_focusedChild) { - auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild); - if(focusedIt != rotatedChildren.end()) { - std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end()); - rotatedChildren.pop_front(); + if(rotate) { + UIWidgetList rotatedChildren(m_children); + std::reverse(rotatedChildren.begin(), rotatedChildren.end()); + + if(m_focusedChild) { + auto focusedIt = std::find(rotatedChildren.begin(), rotatedChildren.end(), m_focusedChild); + if(focusedIt != rotatedChildren.end()) { + std::rotate(rotatedChildren.begin(), focusedIt, rotatedChildren.end()); + rotatedChildren.pop_front(); + } } - } - // finds next child to focus - for(const UIWidgetPtr& child : rotatedChildren) { - if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { - toFocus = child; - break; + // finds next child to focus + for(const UIWidgetPtr& child : rotatedChildren) { + if(child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { + toFocus = child; + break; + } + } + } else { + auto it = m_children.rbegin(); + if(m_focusedChild) + it = std::find(m_children.rbegin(), m_children.rend(), m_focusedChild); + + for(; it != m_children.rend(); ++it) { + const UIWidgetPtr& child = *it; + if(child != m_focusedChild && child->isFocusable() && child->isExplicitlyEnabled() && child->isVisible()) { + toFocus = child; + break; + } } } - if(toFocus) + if(toFocus && toFocus != m_focusedChild) focusChild(toFocus, reason); } @@ -520,10 +550,14 @@ void UIWidget::applyStyle(const OTMLNodePtr& styleNode) callLuaField("onStyleApply", styleNode->tag(), styleNode); if(m_firstOnStyle) { - // always focus new child - if(isFocusable() && isExplicitlyVisible() && isExplicitlyEnabled()) + UIWidgetPtr parent = getParent(); + if(isFocusable() && isExplicitlyVisible() && isExplicitlyEnabled() && + parent && ((!parent->getFocusedChild() && parent->getAutoFocusPolicy() == Fw::AutoFocusFirst) || + parent->getAutoFocusPolicy() == Fw::AutoFocusLast)) { focus(); + } } + m_firstOnStyle = false; } catch(stdext::exception& e) { g_logger.traceError(stdext::format("failed to apply style to widget '%s': %s", m_id, e.what())); @@ -906,7 +940,7 @@ void UIWidget::setVisible(bool visible) // hiding a widget make it lose focus if(!visible && isFocused()) { if(UIWidgetPtr parent = getParent()) - parent->focusPreviousChild(Fw::ActiveFocusReason); + parent->focusPreviousChild(Fw::ActiveFocusReason, true); } // visibility can change change parent layout @@ -940,9 +974,12 @@ void UIWidget::setFocusable(bool focusable) m_focusable = focusable; // make parent focus another child - if(!focusable && isFocused()) { - if(UIWidgetPtr parent = getParent()) - parent->focusPreviousChild(Fw::ActiveFocusReason); + if(UIWidgetPtr parent = getParent()) { + if(!focusable && isFocused()) { + parent->focusPreviousChild(Fw::ActiveFocusReason, true); + } else if(focusable && (!parent->getFocusedChild() && parent->getAutoFocusPolicy() != Fw::AutoFocusNone)) { + focus(); + } } } } @@ -968,6 +1005,11 @@ void UIWidget::setLastFocusReason(Fw::FocusReason reason) m_lastFocusReason = reason; } +void UIWidget::setAutoFocusPolicy(Fw::AutoFocusPolicy policy) +{ + m_autoFocusPolicy = policy; +} + void UIWidget::setVirtualOffset(const Point& offset) { m_virtualOffset = offset; diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index bda1ada3..5db9c651 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -77,14 +77,15 @@ protected: OTMLNodePtr m_style; Timer m_clickTimer; Fw::FocusReason m_lastFocusReason; + Fw::AutoFocusPolicy m_autoFocusPolicy; public: void addChild(const UIWidgetPtr& child); void insertChild(int index, const UIWidgetPtr& child); void removeChild(UIWidgetPtr child); void focusChild(const UIWidgetPtr& child, Fw::FocusReason reason); - void focusNextChild(Fw::FocusReason reason); - void focusPreviousChild(Fw::FocusReason reason); + void focusNextChild(Fw::FocusReason reason, bool rotate = false); + void focusPreviousChild(Fw::FocusReason reason, bool rotate = false); void lowerChild(UIWidgetPtr child); void raiseChild(UIWidgetPtr child); void moveChildToIndex(const UIWidgetPtr& child, int index); @@ -129,6 +130,7 @@ public: void setFixedSize(bool fixed); void setClipping(bool clipping) { m_clipping = clipping; } void setLastFocusReason(Fw::FocusReason reason); + void setAutoFocusPolicy(Fw::AutoFocusPolicy policy); void setAutoRepeatDelay(int delay) { m_autoRepeatDelay = delay; } void setVirtualOffset(const Point& offset); @@ -259,6 +261,7 @@ public: OTMLNodePtr getStyle() { return m_style; } int getChildCount() { return m_children.size(); } Fw::FocusReason getLastFocusReason() { return m_lastFocusReason; } + Fw::AutoFocusPolicy getAutoFocusPolicy() { return m_autoFocusPolicy; } int getAutoRepeatDelay() { return m_autoRepeatDelay; } Point getVirtualOffset() { return m_virtualOffset; } std::string getStyleName() { return m_style->tag(); } diff --git a/src/framework/ui/uiwidgetbasestyle.cpp b/src/framework/ui/uiwidgetbasestyle.cpp index 760279e0..cb340c49 100644 --- a/src/framework/ui/uiwidgetbasestyle.cpp +++ b/src/framework/ui/uiwidgetbasestyle.cpp @@ -122,6 +122,8 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode) setOn(node->value()); else if(node->tag() == "focusable") setFocusable(node->value()); + else if(node->tag() == "auto-focus") + setAutoFocusPolicy(Fw::translateAutoFocusPolicy(node->value())); else if(node->tag() == "phantom") setPhantom(node->value()); else if(node->tag() == "size")