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)
INSTALL(TARGETS otclient RUNTIME DESTINATION bin)
INSTALL(DIRECTORY modules DESTINATION ${DATA_INSTALL_DIR})

View File

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

View File

@ -5,3 +5,9 @@ function print(...)
end
Logger.log(LogInfo, msg)
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>("isEnabled", &UIWidget::isEnabled);
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>("getWidth", &UIWidget::getWidth);
g_lua.bindClassMemberFunction<UIWidget>("setWidth", &UIWidget::setWidth);

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ UIWidget::UIWidget(UIWidgetType type)
m_visible = true;
m_enabled = true;
m_hovered = false;
m_focusable = false;
m_focusable = true;
m_destroyed = false;
m_updateScheduled = false;
m_opacity = 255;
@ -193,7 +193,7 @@ void UIWidget::render()
m_image->draw(getGeometry());
for(const UIWidgetPtr& child : m_children) {
if(child->isVisible()) {
if(child->isExplicitlyVisible()) {
int oldOpacity = g_graphics.getOpacity();
if(child->getOpacity() < oldOpacity)
g_graphics.setOpacity(child->getOpacity());
@ -290,8 +290,10 @@ bool UIWidget::hasFocus()
{
assert(!m_destroyed);
if(UIWidgetPtr parent = m_parent.lock())
return (parent->getFocusedChild() == shared_from_this());
if(UIWidgetPtr parent = getParent()) {
if(parent->hasFocus() && parent->getFocusedChild() == shared_from_this())
return true;
}
// root widget always has focus
else if(asUIWidget() == g_ui.getRootWidget())
return true;
@ -381,7 +383,7 @@ UIWidgetPtr UIWidget::getChildByPos(const Point& childPos)
for(auto it = m_children.rbegin(); it != m_children.rend(); ++it) {
const UIWidgetPtr& widget = (*it);
if(widget->isVisible() && widget->getGeometry().contains(childPos))
if(widget->isExplicitlyVisible() && widget->getGeometry().contains(childPos))
return widget;
}
@ -480,25 +482,13 @@ void UIWidget::focusChild(const UIWidgetPtr& focusedChild, FocusReason reason)
m_focusedChild = focusedChild;
if(oldFocused) {
g_dispatcher.addEvent([oldFocused,reason]() {
// the widget can be destroyed before the event happens
UIFocusEvent e(reason, false);
if(!oldFocused->isDestroyed())
oldFocused->onFocusChange(e);
});
UIFocusEvent e(reason, false);
oldFocused->onFocusChange(e);
}
if(focusedChild) {
g_dispatcher.addEvent([focusedChild,reason]() {
// the widget can be destroyed before the event happens
UIFocusEvent e(reason, true);
if(!focusedChild->isDestroyed())
focusedChild->onFocusChange(e);
});
UIFocusEvent e(reason, focusedChild->hasFocus());
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
updateGeometry();
// focus it if there is no focused child yet
if(!m_focusedChild && childToAdd->isFocusable())
// always focus new children
if(childToAdd->isFocusable())
focusChild(childToAdd, ActiveFocusReason);
}
@ -541,10 +531,6 @@ void UIWidget::insertChild(const UIWidgetPtr& childToInsert, int index)
// updates geometry
updateGeometry();
// focus it if there is no focused child yet
if(!m_focusedChild && childToInsert->isFocusable())
focusChild(childToInsert, ActiveFocusReason);
}
void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
@ -619,8 +605,9 @@ void UIWidget::lockChild(const UIWidgetPtr& childToLock)
m_lockedWidgets.push_front(childToLock);
// move locked child to top
moveChildToTop(childToLock);
// lock child focus
if(childToLock->isFocusable())
focusChild(childToLock, ActiveFocusReason);
}
void UIWidget::unlockChild(const UIWidgetPtr& childToUnlock)
@ -674,32 +661,26 @@ void UIWidget::fill(const std::string& targetId)
addAnchor(AnchorBottom, targetId, AnchorBottom);
}
void UIWidget::onFocusChange(UIFocusEvent& event)
{
if(m_focusedChild)
m_focusedChild->onFocusChange(event);
}
void UIWidget::onKeyPress(UIKeyEvent& event)
{
assert(!m_destroyed);
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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// key events go only to containers or focused child
if(child->hasChildren() || child->hasFocus()) {
if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept();
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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// key events go only to containers or focused child
if(child->hasChildren() || child->hasFocus()) {
if(child->hasChildren() || (child->isFocusable() && child->hasFocus())) {
event.accept();
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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// 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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// 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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// 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
UIWidgetList children = m_children;
for(const UIWidgetPtr& child : children) {
if(!child->isExplicitlyEnabled() || !child->isVisible())
if(!child->isExplicitlyEnabled() || !child->isExplicitlyVisible())
continue;
// 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 setStyle(const std::string& styleName);
void setGeometry(const Rect& rect);
void setLocked(bool locked);
void setX(int x) { move(Point(x, getY())); }
void setY(int y) { move(Point(getX(), y)); }
void setWidth(int width) { resize(Size(width, getHeight())); }
@ -64,7 +63,7 @@ public:
bool isEnabled();
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 isFocusable() const { return m_focusable; }
bool isDestroyed() const { return m_destroyed; }
@ -128,7 +127,7 @@ protected:
// Triggered when widget change visibility/enabled/style/children/parent/layout/...
//virtual void onChange(const UIEvent& event);
/// 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
virtual void onHoverChange(UIHoverEvent& event) { }
/// Triggered when user presses key while widget has focus

View File

@ -6,7 +6,6 @@
UIWindow::UIWindow(): UIWidget(UITypeWindow)
{
m_moving = false;
setFocusable(true);
}
UIWindowPtr UIWindow::create()
@ -89,6 +88,13 @@ void UIWindow::onGeometryUpdate(UIGeometryUpdateEvent& event)
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)
{
Rect headRect = getGeometry();

View File

@ -18,6 +18,7 @@ public:
protected:
virtual void onGeometryUpdate(UIGeometryUpdateEvent& event);
virtual void onFocusChange(UIFocusEvent& event);
virtual void onMousePress(UIMouseEvent& event);
virtual void onMouseRelease(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) {
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;
}
@ -45,7 +45,7 @@ void Tile::draw(int x, int y, int step)
if(thingAttributes.alwaysOnTopOrder == 2) {
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;
}
}
@ -53,7 +53,7 @@ void Tile::draw(int x, int y, int step)
for(const ThingPtr& thing : m_itemsBottom) {
const ThingAttributes& thingAttributes = thing->getAttributes();
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;
}
@ -69,7 +69,7 @@ void Tile::draw(int x, int y, int step)
if(thingAttributes.alwaysOnTopOrder == 3) {
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;
}
}

View File

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