fix focus bugs in UI

This commit is contained in:
Eduardo Bart 2011-08-20 22:01:46 -03:00
parent d0f47f47a4
commit b410921e32
13 changed files with 68 additions and 70 deletions

View File

@ -168,4 +168,3 @@ ENDIF(USE_PCH)
SET(DATA_INSTALL_DIR share/otclient) SET(DATA_INSTALL_DIR share/otclient)
INSTALL(TARGETS otclient RUNTIME DESTINATION bin) INSTALL(TARGETS otclient RUNTIME DESTINATION bin)
INSTALL(DIRECTORY modules DESTINATION ${DATA_INSTALL_DIR}) INSTALL(DIRECTORY modules DESTINATION ${DATA_INSTALL_DIR})

View File

@ -1,6 +1,8 @@
Console = { } Console = { }
local console local console
local logLocked = false local logLocked = false
local commandEnv = createEnvironment()
function Console.onLog(level, message, time) function Console.onLog(level, message, time)
-- avoid logging while reporting logs (would cause a infinite loop) -- avoid logging while reporting logs (would cause a infinite loop)
@ -61,11 +63,14 @@ end
function Console.executeCommand(command) function Console.executeCommand(command)
Console.addLine(">> " .. command, "#ffffff") Console.addLine(">> " .. command, "#ffffff")
local func, err = loadstring("return (" .. command .. ")", "@") local func, err = loadstring(command, "@")
if func then if func then
setfenv(func, commandEnv)
local ok, ret = pcall(func) local ok, ret = pcall(func)
if ok then if ok then
Logger.log(LogDebug, "=> " .. tostring(ret)) if ret then
print(ret)
end
else else
Logger.log(LogError, 'command failed: ' .. ret) Logger.log(LogError, 'command failed: ' .. ret)
end end

View File

@ -5,3 +5,9 @@ function print(...)
end end
Logger.log(LogInfo, msg) Logger.log(LogInfo, msg)
end end
function createEnvironment()
local env = { }
setmetatable(env, { __index = _G} )
return env
end

View File

@ -14,7 +14,7 @@ void LuaInterface::registerFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setId", &UIWidget::setId); g_lua.bindClassMemberFunction<UIWidget>("setId", &UIWidget::setId);
g_lua.bindClassMemberFunction<UIWidget>("isEnabled", &UIWidget::isEnabled); g_lua.bindClassMemberFunction<UIWidget>("isEnabled", &UIWidget::isEnabled);
g_lua.bindClassMemberFunction<UIWidget>("setEnabled", &UIWidget::setEnabled); g_lua.bindClassMemberFunction<UIWidget>("setEnabled", &UIWidget::setEnabled);
g_lua.bindClassMemberFunction<UIWidget>("isVisible", &UIWidget::isVisible); g_lua.bindClassMemberFunction<UIWidget>("isExplicitlyVisible", &UIWidget::isExplicitlyVisible);
g_lua.bindClassMemberFunction<UIWidget>("setVisible", &UIWidget::setVisible); g_lua.bindClassMemberFunction<UIWidget>("setVisible", &UIWidget::setVisible);
g_lua.bindClassMemberFunction<UIWidget>("getWidth", &UIWidget::getWidth); g_lua.bindClassMemberFunction<UIWidget>("getWidth", &UIWidget::getWidth);
g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth); g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth);

View File

@ -8,6 +8,7 @@
UIButton::UIButton(): UIWidget(UITypeButton) UIButton::UIButton(): UIWidget(UITypeButton)
{ {
m_state = ButtonUp; m_state = ButtonUp;
m_focusable = false;
// by default, all callbacks call lua fields // by default, all callbacks call lua fields
m_onClick = [this]() { this->callLuaField("onClick"); }; m_onClick = [this]() { this->callLuaField("onClick"); };

View File

@ -5,6 +5,7 @@
UILabel::UILabel() : UIWidget(UITypeLabel) UILabel::UILabel() : UIWidget(UITypeLabel)
{ {
m_align = AlignLeft; m_align = AlignLeft;
m_focusable = false;
} }
UILabelPtr UILabel::create() UILabelPtr UILabel::create()

View File

@ -10,7 +10,6 @@ UILineEdit::UILineEdit() : UIWidget(UITypeLabel)
m_cursorPos = 0; m_cursorPos = 0;
m_startRenderPos = 0; m_startRenderPos = 0;
m_textHorizontalMargin = 3; m_textHorizontalMargin = 3;
m_focusable = true;
blinkCursor(); blinkCursor();
m_onAction = [this]() { this->callLuaField("onAction"); }; m_onAction = [this]() { this->callLuaField("onAction"); };
@ -364,14 +363,14 @@ void UILineEdit::onKeyPress(UIKeyEvent& event)
setCursorPos(0); setCursorPos(0);
else if(event.keyCode() == KC_END) // move cursor to last character else if(event.keyCode() == KC_END) // move cursor to last character
setCursorPos(m_text.length()); setCursorPos(m_text.length());
else if(event.keyCode() == KC_RETURN) { else if(event.keyCode() == KC_TAB) {
if(UIWidgetPtr parent = getParent())
parent->focusNextChild(TabFocusReason);
} else if(event.keyCode() == KC_RETURN) {
if(m_onAction) if(m_onAction)
m_onAction(); m_onAction();
} else if(event.keyChar() != 0) { } else if(event.keyChar() != 0) {
if(event.keyCode() != KC_TAB && event.keyCode() != KC_RETURN) appendCharacter(event.keyChar());
appendCharacter(event.keyChar());
else
event.ignore();
} else } else
event.ignore(); event.ignore();

View File

@ -16,7 +16,7 @@ UIWidget::UIWidget(UIWidgetType type)
m_visible = true; m_visible = true;
m_enabled = true; m_enabled = true;
m_hovered = false; m_hovered = false;
m_focusable = false; m_focusable = true;
m_destroyed = false; m_destroyed = false;
m_updateScheduled = false; m_updateScheduled = false;
m_opacity = 255; m_opacity = 255;
@ -193,7 +193,7 @@ void UIWidget::render()
m_image->draw(getGeometry()); m_image->draw(getGeometry());
for(const UIWidgetPtr& child : m_children) { for(const UIWidgetPtr& child : m_children) {
if(child->isVisible()) { if(child->isExplicitlyVisible()) {
int oldOpacity = g_graphics.getOpacity(); int oldOpacity = g_graphics.getOpacity();
if(child->getOpacity() < oldOpacity) if(child->getOpacity() < oldOpacity)
g_graphics.setOpacity(child->getOpacity()); g_graphics.setOpacity(child->getOpacity());
@ -290,8 +290,10 @@ bool UIWidget::hasFocus()
{ {
assert(!m_destroyed); assert(!m_destroyed);
if(UIWidgetPtr parent = m_parent.lock()) if(UIWidgetPtr parent = getParent()) {
return (parent->getFocusedChild() == shared_from_this()); if(parent->hasFocus() && parent->getFocusedChild() == shared_from_this())
return true;
}
// root widget always has focus // root widget always has focus
else if(asUIWidget() == g_ui.getRootWidget()) else if(asUIWidget() == g_ui.getRootWidget())
return true; return true;
@ -381,7 +383,7 @@ UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) { for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
const UIWidgetPtr& widget = (*it); const UIWidgetPtr& widget = (*it);
if(widget->isVisible() && widget->getGeometry().contains(childPos)) if(widget->isExplicitlyVisible() && widget->getGeometry().contains(childPos))
return widget; return widget;
} }
@ -480,25 +482,13 @@ void UIWidget::focusChild(const UIWidgetPtr& focusedChild, FocusReason reason)
m_focusedChild = focusedChild; m_focusedChild = focusedChild;
if(oldFocused) { if(oldFocused) {
g_dispatcher.addEvent([oldFocused,reason]() { UIFocusEvent e(reason, false);
// the widget can be destroyed before the event happens oldFocused->onFocusChange(e);
UIFocusEvent e(reason, false);
if(!oldFocused->isDestroyed())
oldFocused->onFocusChange(e);
});
} }
if(focusedChild) { if(focusedChild) {
g_dispatcher.addEvent([focusedChild,reason]() { UIFocusEvent e(reason, focusedChild->hasFocus());
// the widget can be destroyed before the event happens focusedChild->onFocusChange(e);
UIFocusEvent e(reason, true);
if(!focusedChild->isDestroyed())
focusedChild->onFocusChange(e);
});
} }
// when containers are focused they go to the top
if(focusedChild && focusedChild->hasChildren())
moveChildToTop(focusedChild);
} }
} }
@ -516,8 +506,8 @@ void UIWidget::addChild(const UIWidgetPtr& childToAdd)
// updates geometry // updates geometry
updateGeometry(); updateGeometry();
// focus it if there is no focused child yet // always focus new children
if(!m_focusedChild && childToAdd->isFocusable()) if(childToAdd->isFocusable())
focusChild(childToAdd, ActiveFocusReason); focusChild(childToAdd, ActiveFocusReason);
} }
@ -541,10 +531,6 @@ void UIWidget::insertChild(const UIWidgetPtr& childToInsert, int index)
// updates geometry // updates geometry
updateGeometry(); updateGeometry();
// focus it if there is no focused child yet
if(!m_focusedChild && childToInsert->isFocusable())
focusChild(childToInsert, ActiveFocusReason);
} }
void UIWidget::removeChild(const UIWidgetPtr& childToRemove) void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
@ -619,8 +605,9 @@ void UIWidget::lockChild(const UIWidgetPtr& childToLock)
m_lockedWidgets.push_front(childToLock); m_lockedWidgets.push_front(childToLock);
// move locked child to top // lock child focus
moveChildToTop(childToLock); if(childToLock->isFocusable())
focusChild(childToLock, ActiveFocusReason);
} }
void UIWidget::unlockChild(const UIWidgetPtr& childToUnlock) void UIWidget::unlockChild(const UIWidgetPtr& childToUnlock)
@ -674,32 +661,26 @@ void UIWidget::fill(const std::string& targetId)
addAnchor(AnchorBottom, targetId, AnchorBottom); addAnchor(AnchorBottom, targetId, AnchorBottom);
} }
void UIWidget::onFocusChange(UIFocusEvent& event)
{
if(m_focusedChild)
m_focusedChild->onFocusChange(event);
}
void UIWidget::onKeyPress(UIKeyEvent& event) void UIWidget::onKeyPress(UIKeyEvent& event)
{ {
assert(!m_destroyed); assert(!m_destroyed);
event.ignore(); event.ignore();
// focus next child when pressing tab
if(isFocusable() && hasFocus() && !hasChildren() && event.keyCode() == KC_TAB) {
if(UIWidgetPtr parent = getParent()) {
g_dispatcher.addEvent([parent]{
if(!parent->isDestroyed())
parent->focusNextChild(TabFocusReason);
});
}
event.accept();
return;
}
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// key events go only to containers or focused child // key events go only to containers or focused child
if(child->hasChildren() || child->hasFocus()) { if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept(); event.accept();
child->onKeyPress(event); child->onKeyPress(event);
} }
@ -718,11 +699,11 @@ void UIWidget::onKeyRelease(UIKeyEvent& event)
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// key events go only to containers or focused child // key events go only to containers or focused child
if(child->hasChildren() || child->hasFocus()) { if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept(); event.accept();
child->onKeyRelease(event); child->onKeyRelease(event);
} }
@ -741,7 +722,7 @@ void UIWidget::onMousePress(UIMouseEvent& event)
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// mouse press events only go to children that contains the mouse position // mouse press events only go to children that contains the mouse position
@ -768,7 +749,7 @@ void UIWidget::onMouseRelease(UIMouseEvent& event)
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// mouse release events go to all children // mouse release events go to all children
@ -789,7 +770,7 @@ void UIWidget::onMouseMove(UIMouseEvent& event)
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// check if the mouse is relally over this child // check if the mouse is relally over this child
@ -821,7 +802,7 @@ void UIWidget::onMouseWheel(UIMouseEvent& event)
// do a backup of children list, because it may change while looping it // do a backup of children list, because it may change while looping it
UIWidgetList children = m_children; UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) { for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible()) if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue; continue;
// mouse wheel events only go to children that contains the mouse position // mouse wheel events only go to children that contains the mouse position

View File

@ -39,7 +39,6 @@ public:
void setParent(const UIWidgetPtr& parent); void setParent(const UIWidgetPtr& parent);
void setStyle(const std::string& styleName); void setStyle(const std::string& styleName);
void setGeometry(const Rect& rect); void setGeometry(const Rect& rect);
void setLocked(bool locked);
void setX(int x) { move(Point(x, getY())); } void setX(int x) { move(Point(x, getY())); }
void setY(int y) { move(Point(getX(), y)); } void setY(int y) { move(Point(getX(), y)); }
void setWidth(int width) { resize(Size(width, getHeight())); } void setWidth(int width) { resize(Size(width, getHeight())); }
@ -64,7 +63,7 @@ public:
bool isEnabled(); bool isEnabled();
bool isExplicitlyEnabled() const { return m_enabled; } bool isExplicitlyEnabled() const { return m_enabled; }
bool isVisible() const { return m_visible; } bool isExplicitlyVisible() const { return m_visible; }
bool isHovered() const { return m_hovered; } bool isHovered() const { return m_hovered; }
bool isFocusable() const { return m_focusable; } bool isFocusable() const { return m_focusable; }
bool isDestroyed() const { return m_destroyed; } bool isDestroyed() const { return m_destroyed; }
@ -128,7 +127,7 @@ protected:
// Triggered when widget change visibility/enabled/style/children/parent/layout/... // Triggered when widget change visibility/enabled/style/children/parent/layout/...
//virtual void onChange(const UIEvent& event); //virtual void onChange(const UIEvent& event);
/// Triggered when widget gets or loses focus /// Triggered when widget gets or loses focus
virtual void onFocusChange(UIFocusEvent& event) { } virtual void onFocusChange(UIFocusEvent& event);
/// Triggered when the mouse enters or leaves widget area /// Triggered when the mouse enters or leaves widget area
virtual void onHoverChange(UIHoverEvent& event) { } virtual void onHoverChange(UIHoverEvent& event) { }
/// Triggered when user presses key while widget has focus /// Triggered when user presses key while widget has focus

View File

@ -6,7 +6,6 @@
UIWindow::UIWindow(): UIWidget(UITypeWindow) UIWindow::UIWindow(): UIWidget(UITypeWindow)
{ {
m_moving = false; m_moving = false;
setFocusable(true);
} }
UIWindowPtr UIWindow::create() UIWindowPtr UIWindow::create()
@ -89,6 +88,13 @@ void UIWindow::onGeometryUpdate(UIGeometryUpdateEvent& event)
setGeometry(boundRect); setGeometry(boundRect);
} }
void UIWindow::onFocusChange(UIFocusEvent& event)
{
// when a window is focused it goes to the top
if(UIWidgetPtr parent = getParent())
parent->moveChildToTop(asUIWidget());
}
void UIWindow::onMousePress(UIMouseEvent& event) void UIWindow::onMousePress(UIMouseEvent& event)
{ {
Rect headRect = getGeometry(); Rect headRect = getGeometry();

View File

@ -18,6 +18,7 @@ public:
protected: protected:
virtual void onGeometryUpdate(UIGeometryUpdateEvent& event); virtual void onGeometryUpdate(UIGeometryUpdateEvent& event);
virtual void onFocusChange(UIFocusEvent& event);
virtual void onMousePress(UIMouseEvent& event); virtual void onMousePress(UIMouseEvent& event);
virtual void onMouseRelease(UIMouseEvent& event); virtual void onMouseRelease(UIMouseEvent& event);
virtual void onMouseMove(UIMouseEvent& event); virtual void onMouseMove(UIMouseEvent& event);

View File

@ -34,7 +34,7 @@ void Tile::draw(int x, int y, int step)
if(thingAttributes.alwaysOnTopOrder == 1) { if(thingAttributes.alwaysOnTopOrder == 1) {
thing->draw(x - m_drawNextOffset, y - m_drawNextOffset); thing->draw(x - m_drawNextOffset, y - m_drawNextOffset);
//font->renderText("T1", Rect(x + 5, y+5, 100, 100)); font->renderText("T1", Rect(x + 5, y+5, 100, 100));
m_drawNextOffset += thingAttributes.drawNextOffset; m_drawNextOffset += thingAttributes.drawNextOffset;
} }
@ -45,7 +45,7 @@ void Tile::draw(int x, int y, int step)
if(thingAttributes.alwaysOnTopOrder == 2) { if(thingAttributes.alwaysOnTopOrder == 2) {
thing->draw(x - m_drawNextOffset, y - m_drawNextOffset); thing->draw(x - m_drawNextOffset, y - m_drawNextOffset);
//font->renderText("T2", Rect(x + 5, y+5, 100, 100)); font->renderText("T2", Rect(x + 5, y+5, 100, 100));
m_drawNextOffset += thingAttributes.drawNextOffset; m_drawNextOffset += thingAttributes.drawNextOffset;
} }
} }
@ -53,7 +53,7 @@ void Tile::draw(int x, int y, int step)
for(const ThingPtr& thing : m_itemsBottom) { for(const ThingPtr& thing : m_itemsBottom) {
const ThingAttributes& thingAttributes = thing->getAttributes(); const ThingAttributes& thingAttributes = thing->getAttributes();
thing->draw(x - m_drawNextOffset, y - m_drawNextOffset); thing->draw(x - m_drawNextOffset, y - m_drawNextOffset);
//font->renderText("B0", Rect(x + 5, y+5, 100, 100)); font->renderText("B0", Rect(x + 5, y+5, 100, 100));
m_drawNextOffset += thingAttributes.drawNextOffset; m_drawNextOffset += thingAttributes.drawNextOffset;
} }
@ -69,7 +69,7 @@ void Tile::draw(int x, int y, int step)
if(thingAttributes.alwaysOnTopOrder == 3) { if(thingAttributes.alwaysOnTopOrder == 3) {
thing->draw(x - m_drawNextOffset, y - m_drawNextOffset); thing->draw(x - m_drawNextOffset, y - m_drawNextOffset);
//font->renderText("T3", Rect(x + 5, y+5, 100, 100)); font->renderText("T3", Rect(x + 5, y+5, 100, 100));
m_drawNextOffset += thingAttributes.drawNextOffset; m_drawNextOffset += thingAttributes.drawNextOffset;
} }
} }

View File

@ -295,11 +295,11 @@ void OTClient::onPlatformEvent(const PlatformEvent& event)
else if(event.keycode == KC_APOSTROPHE) { else if(event.keycode == KC_APOSTROPHE) {
// TODO: move these events to lua // TODO: move these events to lua
UIWidgetPtr console = g_ui.getRootWidget()->getChildById("consolePanel"); UIWidgetPtr console = g_ui.getRootWidget()->getChildById("consolePanel");
if(!console->isVisible()) { if(!console->isExplicitlyVisible()) {
g_ui.getRootWidget()->lockChild(console); g_ui.getRootWidget()->focusChild(console, ActiveFocusReason);
g_ui.getRootWidget()->moveChildToTop(console);
console->setVisible(true); console->setVisible(true);
} else { } else {
g_ui.getRootWidget()->unlockChild(console);
console->setVisible(false); console->setVisible(false);
} }
fireUi = false; fireUi = false;