Rework auto focus behavior, closes #222

This commit is contained in:
Eduardo Bart 2013-01-26 17:06:25 -02:00
parent 7e01306fc6
commit 4f8f02acad
13 changed files with 126 additions and 55 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -19,6 +19,10 @@ KeyboardFocusReason = 1
ActiveFocusReason = 2
OtherFocusReason = 3
AutoFocusNone = 0
AutoFocusFirst = 1
AutoFocusLast = 2
KeyboardNoModifier = 0
KeyboardCtrlModifier = 1
KeyboardAltModifier = 2

View File

@ -5,6 +5,7 @@ function UIWindow.create()
local window = UIWindow.internalCreate()
window:setTextAlign(AlignTopCenter)
window:setDraggable(true)
window:setAutoFocusPolicy(AutoFocusFirst)
return window
end

View File

@ -226,6 +226,12 @@ namespace Fw
OtherFocusReason
};
enum AutoFocusPolicy {
AutoFocusNone = 0,
AutoFocusFirst,
AutoFocusLast
};
enum InputEventType {
NoInputEvent = 0,
KeyTextInputEvent,

View File

@ -377,6 +377,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setFixedSize", &UIWidget::setFixedSize);
g_lua.bindClassMemberFunction<UIWidget>("setClipping", &UIWidget::setClipping);
g_lua.bindClassMemberFunction<UIWidget>("setLastFocusReason", &UIWidget::setLastFocusReason);
g_lua.bindClassMemberFunction<UIWidget>("setAutoFocusPolicy", &UIWidget::setAutoFocusPolicy);
g_lua.bindClassMemberFunction<UIWidget>("setAutoRepeatDelay", &UIWidget::setAutoRepeatDelay);
g_lua.bindClassMemberFunction<UIWidget>("setVirtualOffset", &UIWidget::setVirtualOffset);
g_lua.bindClassMemberFunction<UIWidget>("isVisible", &UIWidget::isVisible);
@ -442,6 +443,7 @@ void Application::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIWidget>("getStyle", &UIWidget::getStyle);
g_lua.bindClassMemberFunction<UIWidget>("getChildCount", &UIWidget::getChildCount);
g_lua.bindClassMemberFunction<UIWidget>("getLastFocusReason", &UIWidget::getLastFocusReason);
g_lua.bindClassMemberFunction<UIWidget>("getAutoFocusPolicy", &UIWidget::getAutoFocusPolicy);
g_lua.bindClassMemberFunction<UIWidget>("getAutoRepeatDelay", &UIWidget::getAutoRepeatDelay);
g_lua.bindClassMemberFunction<UIWidget>("getVirtualOffset", &UIWidget::getVirtualOffset);
g_lua.bindClassMemberFunction<UIWidget>("getStyleName", &UIWidget::getStyleName);

View File

@ -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) {

View File

@ -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;
}
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;
}

View File

@ -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);
};

View File

@ -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,12 +284,14 @@ 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;
if(rotate) {
UIWidgetList rotatedChildren(m_children);
if(m_focusedChild) {
@ -306,17 +309,31 @@ void UIWidget::focusNextChild(Fw::FocusReason reason)
break;
}
}
} else {
auto it = m_children.begin();
if(m_focusedChild)
it = std::find(m_children.begin(), m_children.end(), m_focusedChild);
if(toFocus)
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 && 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;
if(rotate) {
UIWidgetList rotatedChildren(m_children);
std::reverse(rotatedChildren.begin(), rotatedChildren.end());
@ -335,8 +352,21 @@ void UIWidget::focusPreviousChild(Fw::FocusReason reason)
break;
}
}
} else {
auto it = m_children.rbegin();
if(m_focusedChild)
it = std::find(m_children.rbegin(), m_children.rend(), m_focusedChild);
if(toFocus)
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 && 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(UIWidgetPtr parent = getParent()) {
if(!focusable && isFocused()) {
if(UIWidgetPtr parent = getParent())
parent->focusPreviousChild(Fw::ActiveFocusReason);
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;

View File

@ -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(); }

View File

@ -122,6 +122,8 @@ void UIWidget::parseBaseStyle(const OTMLNodePtr& styleNode)
setOn(node->value<bool>());
else if(node->tag() == "focusable")
setFocusable(node->value<bool>());
else if(node->tag() == "auto-focus")
setAutoFocusPolicy(Fw::translateAutoFocusPolicy(node->value()));
else if(node->tag() == "phantom")
setPhantom(node->value<bool>());
else if(node->tag() == "size")