new layout system, new UI state/styles system

This commit is contained in:
Eduardo Bart 2011-08-26 12:06:52 -03:00
parent d31d32bf82
commit 7359370251
57 changed files with 1097 additions and 1099 deletions

View File

@ -128,9 +128,9 @@ SET(SOURCES
src/framework/ui/uibutton.cpp
src/framework/ui/uilineedit.cpp
src/framework/ui/uiwindow.cpp
src/framework/ui/uianchor.cpp
src/framework/ui/uilist.cpp
#src/framework/ui/uianchorable.cpp
src/framework/ui/uianchorlayout.cpp
src/framework/ui/uiverticallayout.cpp
src/framework/ui/uilayout.cpp
)
IF(WIN32)

View File

@ -34,26 +34,21 @@ function Console.addLine(text, color)
-- create new label
local label = UILabel.create()
label:setStyle('ConsoleLabel')
console:insertChild(-2, label)
label:setId('consoleLabel' .. numLines)
label:setText(text)
label:setForegroundColor(color)
console:insertChild(3, label)
local lastLabel = console:getChildByIndex(4)
if lastLabel then
lastLabel:addAnchor(AnchorBottom, "prev", AnchorTop)
end
label:setStyle('ConsoleLabel')
numLines = numLines + 1
if numLines > maxLines then
local firstLabel = console:getChildByIndex(-1)
local firstLabel = console:getChildByIndex(1)
firstLabel:destroy()
end
end
function Console.create()
console = loadUI("/console/console.otui")
rootWidget:addChild(console)
console = UI.loadAndDisplay("/console/console.otui")
console:hide()
Logger.setOnLog(Console.onLog)

View File

@ -1,7 +1,7 @@
ConsoleLabel < UILabel
font: terminus-14px-bold
height: 16
anchors.bottom: commandBox.top
anchors.bottom: next.top
anchors.left: parent.left
anchors.right: parent.right
margin.left: 2
@ -14,7 +14,8 @@ RectPanel
UILabel
id: commandSymbolLabel
size: 18 20
size: 20 16
size fixed: true
anchors.bottom: parent.bottom
anchors.left: parent.left
margin.left: 2
@ -23,11 +24,10 @@ RectPanel
UILineEdit
id: commandBox
height: 20
height: 16
anchors.bottom: parent.bottom
anchors.left: prev.right
anchors.left: commandSymbolLabel.right
anchors.right: parent.right
margin.left: 4
font: terminus-14px-bold
onAction: |
function(self)

View File

@ -13,10 +13,10 @@ Module
require 'constants'
require 'util'
require 'widget'
require 'messagebox'
require 'ui'
require 'messagebox/messagebox'
require 'dispatcher'
rootWidget = getRootWidget()
return true
onUnload: |

View File

@ -10,50 +10,33 @@ function MessageBox.create(title, text, flags)
setmetatable(box, MessageBox)
-- create messagebox window
local window = UIWindow.create()
window:setStyle('Window')
window:setId("messageBoxWindow")
local window = UI.loadAndDisplayLocked('/core/messagebox/messagebox.otui')
window:setTitle(title)
window:centerIn("parent")
rootWidget:addChild(window)
rootWidget:lockChild(window)
-- create messagebox label
local label = UILabel.create()
local label = window:getChildById('messageBoxLabel')
label:setStyle('Label')
label:setId("messageBoxLabel")
label:setText(text)
label:addAnchor(AnchorHorizontalCenter, window:getId(), AnchorHorizontalCenter)
label:addAnchor(AnchorTop, window:getId(), AnchorTop)
label:setMargin(27, 0)
label:resizeToText()
window:addChild(label)
-- set window size based on label size
window:setWidth(label:getWidth() + 60)
window:setHeight(label:getHeight() + 64)
window:updateParentLayout()
-- setup messagebox first button
local button1 = UIButton.create()
button1:setStyle('Button')
button1:setId("messageBoxButton1")
button1:addAnchor(AnchorBottom, window:getId(), AnchorBottom)
button1:addAnchor(AnchorRight, window:getId(), AnchorRight)
button1:setMargin(10)
button1:setWidth(64)
window:addChild(button1)
local buttonRight = window:getChildById('messageBoxRightButton')
if flags == MessageBoxOk then
button1:setText("Ok")
buttonRight:setText("Ok")
box.onOk = EmptyFunction
button1.onClick = function()
buttonRight.onClick = function()
box.onOk()
box:destroy()
end
elseif flags == MessageBoxCancel then
button1:setText("Cancel")
buttonRight:setText("Cancel")
box.onCancel = EmptyFunction
button1.onClick = function()
buttonRight.onClick = function()
box.onCancel()
box:destroy()
end

View File

@ -0,0 +1,21 @@
Window
id: messageBoxWindow
anchors.centerIn: parent
height: 80
width: 120
Label
id: messageBoxLabel
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
margin.top: 27
margin.bottom : 27
Button
id: messageBoxRightButton
anchors.bottom: parent.bottom
anchors.right: parent.right
margin.right: 10
margin.bottom: 10
width: 64
visible: true

View File

@ -8,12 +8,6 @@ Module
dependencies:
- core_fonts
onLoad: |
importStyles('buttons.otui')
importStyles('labels.otui')
importStyles('panels.otui')
importStyles('separators.otui')
importStyles('lineedits.otui')
importStyles('windows.otui')
importStyles('listboxes.otui')
require 'ui'
return true

View File

@ -11,7 +11,7 @@ Button < UIButton
source: /core_ui/images/button_hover.png
border: 5
state.down:
state.pressed:
text-translate: 1 1
border-image:
source: /core_ui/images/button_down.png

View File

@ -0,0 +1,5 @@
TextList < UIWidget
size: 200 200
border-image:
source: /core_ui/images/panel_flat.png
border: 4

View File

@ -2,3 +2,4 @@ HorizontalSeparator < UIWidget
border-image:
source: /core_ui/images/horizontal_separator.png
border.top: 2
height: 2

View File

@ -1,6 +1,8 @@
Window < UIWindow
font: helvetica-12px-bold
size: 200 200
opacity: 255
background-color: #ffffff
head:
height: 20
border-image:
@ -17,5 +19,8 @@ Window < UIWindow
border: 4
border.top: 0
state.pressed:
opacity: 192
MainWindow < Window
anchors.centerIn: parent

30
modules/core_ui/ui.lua Normal file
View File

@ -0,0 +1,30 @@
UI = { }
UI.root = getRootWidget()
function UI.loadAndDisplayLocked(otuiFile)
local widget = loadUI(otuiFile, UI.root)
UI.root:lockChild(widget)
return widget
end
function UI.loadAndDisplay(otuiFile)
local widget = loadUI(otuiFile, UI.root)
return widget
end
function UI.display(widget)
UI.root:addChild(widget)
end
function UI.displayLocked(widget)
UI.root:addChild(widget)
UI.root:lockChild(widget)
end
importStyles('styles/buttons.otui')
importStyles('styles/labels.otui')
importStyles('styles/panels.otui')
importStyles('styles/separators.otui')
importStyles('styles/lineedits.otui')
importStyles('styles/windows.otui')
importStyles('styles/listboxes.otui')

View File

@ -2,7 +2,7 @@ function EnterGame_connectToLoginServer()
local protocolLogin = ProtocolLogin.create()
local recreateEnterGame = function()
rootWidget:addChild(loadUI("/mainmenu/ui/entergamewindow.otui"))
UI.loadAndDisplayLocked("/mainmenu/ui/entergamewindow.otui")
end
local loadBox = displayCancelBox("Please wait", "Connecting..")
@ -30,7 +30,7 @@ function EnterGame_connectToLoginServer()
mainMenu:hide()
end
local enterGameWindow = rootWidget:getChildById("enterGameWindow")
local enterGameWindow = UI.root:getChildById("enterGameWindow")
local account = enterGameWindow:getChildById("accountNameLineEdit"):getText()
local password = enterGameWindow:getChildById("accountPasswordLineEdit"):getText()
protocolLogin:login(account, password)

View File

@ -12,8 +12,7 @@ Module
require('entergame')
if not initialized then
mainMenu = loadUI("/mainmenu/ui/mainmenu.otui")
getRootWidget():addChild(mainMenu)
mainMenu = UI.loadAndDisplay("/mainmenu/ui/mainmenu.otui")
initialized = true
end

View File

@ -0,0 +1,34 @@
MainWindow
id: charactersWindow
title: Charlist
size: 200 250
TextList
id: charactersList
anchors.fill: parent
margin.top: 30
margin.bottom: 50
margin.left: 16
margin.right: 16
Button
id: buttonOk
text: Ok
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin.bottom: 16
margin.right: 16
Button
id: buttonCancel
text: Cancel
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
margin.bottom: 16
margin.right: 16
onClick: |
function(self)
self:getParent():destroy()
end

View File

@ -1,7 +1,8 @@
MenuButton < Button
anchors.top: prev.bottom
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 10
margin.bottom: 11
margin.left: 20
margin.right: 20
Panel
id: mainMenuBackground
@ -9,6 +10,7 @@ Panel
source: /mainmenu/ui/background.png
smooth: true
anchors.fill: parent
focusable: false
RoundedPanel
id: mainMenu
@ -17,24 +19,20 @@ Panel
anchors.bottom: parent.bottom
margin.left: 60
margin.bottom: 70
layout: verticalBox
MenuButton
text: Enter Game
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
margin.top: 18
onClick: |
local enterGameWindow = loadUI("/mainmenu/ui/entergamewindow.otui")
rootWidget:addChild(enterGameWindow)
GFX.fadeIn(enterGameWindow)
onClick: UI.loadAndDisplayLocked("/mainmenu/ui/entergamewindow.otui")
MenuButton
text: Options
onClick: rootWidget:addChild(loadUI("/mainmenu/ui/optionswindow.otui"))
onClick: UI.loadAndDisplayLocked("/mainmenu/ui/optionswindow.otui")
MenuButton
text: Info
onClick: rootWidget:addChild(loadUI("/mainmenu/ui/infowindow.otui"))
onClick: UI.loadAndDisplayLocked("/mainmenu/ui/infowindow.otui")
MenuButton
text: Exit

View File

@ -89,7 +89,7 @@ MainWindow
onClick: displayErrorBox("Error", "Not implemented yet")
Label
text: |
text: |-
Show the most recent
Message of the Day
anchors.left: prev.right

View File

@ -0,0 +1 @@
--UI.loadAndDisplayLocked('/mainmenu/ui/charlist.otui')

View File

@ -0,0 +1,10 @@
Module
name: playground
autoLoad: true
dependencies:
- core
onLoad: |
require 'playground'
return true

View File

@ -12,6 +12,7 @@ enum LogLevel {
};
enum AlignmentFlag {
AlignNone = 0,
AlignLeft = 1,
AlignRight = 2,
AlignTop = 4,
@ -39,6 +40,45 @@ enum AnchorEdge {
AnchorHorizontalCenter,
};
enum FocusReason {
MouseFocusReason = 0,
TabFocusReason,
ActiveFocusReason,
OtherFocusReason
};
enum MouseButton {
MouseNoButton = 0,
MouseLeftButton,
MouseRightButton,
MouseMidButton
};
enum MouseWheelDirection {
MouseNoWheel = 0,
MouseWheelUp,
MouseWheelDown
};
enum KeyboardModifier {
KeyboardNoModifier = 0,
KeyboardCtrlModifier = 1,
KeyboardAltModifier = 2,
KeyboardShiftModifier = 4
};
enum WidgetState {
DefaultState = 0,
ActiveState = 1,
FocusState = 2,
HoverState = 4,
PressedState = 8,
DisabledState = 16
//FirstState,
//MiddleState,
//LastState,
//AlternateState
};
//}

View File

@ -60,10 +60,3 @@ FontPtr FontManager::getFont(const std::string& fontName)
return getDefaultFont();
}
FontPtr FontManager::getDefaultFont()
{
// default font should always exists, otherwise the app may crash
if(!m_defaultFont)
logFatal("no default font to display, cannot continue to run");
return m_defaultFont;
}

View File

@ -14,7 +14,7 @@ public:
bool fontExists(const std::string& fontName);
FontPtr getFont(const std::string& fontName);
FontPtr getDefaultFont();
FontPtr getDefaultFont() { return m_defaultFont; }
void setDefaultFont(const std::string& fontName) { m_defaultFont = getFont(fontName); }

View File

@ -9,7 +9,7 @@ void LuaInterface::registerFunctions()
{
// UIWidget
g_lua.registerClass<UIWidget>();
g_lua.bindClassStaticFunction<UIWidget>("create", &UIWidget::create);
g_lua.bindClassStaticFunction<UIWidget>("create", &UIWidget::create<UIWidget>);
g_lua.bindClassMemberFunction<UIWidget>("getId", &UIWidget::getId);
g_lua.bindClassMemberFunction<UIWidget>("setId", &UIWidget::setId);
g_lua.bindClassMemberFunction<UIWidget>("isEnabled", &UIWidget::isEnabled);
@ -28,7 +28,7 @@ void LuaInterface::registerFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setForegroundColor", &UIWidget::setForegroundColor);
g_lua.bindClassMemberFunction<UIWidget>("getOpacity", &UIWidget::getOpacity);
g_lua.bindClassMemberFunction<UIWidget>("setOpacity", &UIWidget::setOpacity);
g_lua.bindClassMemberFunction<UIWidget>("setStyle", &UIWidget::applyStyle);
g_lua.bindClassMemberFunction<UIWidget>("setStyle", &UIWidget::setStyle);
g_lua.bindClassMemberFunction<UIWidget>("getMarginTop", &UIWidget::getMarginTop);
g_lua.bindClassMemberFunction<UIWidget>("setMarginTop", &UIWidget::setMarginTop);
g_lua.bindClassMemberFunction<UIWidget>("getMarginBottom", &UIWidget::getMarginBottom);
@ -39,9 +39,6 @@ void LuaInterface::registerFunctions()
g_lua.bindClassMemberFunction<UIWidget>("setMarginRight", &UIWidget::setMarginRight);
g_lua.bindClassMemberFunction<UIWidget>("hide", &UIWidget::hide);
g_lua.bindClassMemberFunction<UIWidget>("show", &UIWidget::show);
g_lua.bindClassMemberFunction<UIWidget>("fill", &UIWidget::fill);
g_lua.bindClassMemberFunction<UIWidget>("centerIn", &UIWidget::centerIn);
g_lua.bindClassMemberFunction<UIWidget>("addAnchor", &UIWidget::addAnchor);
g_lua.bindClassMemberFunction<UIWidget>("getChildById", &UIWidget::getChildById);
g_lua.bindClassMemberFunction<UIWidget>("getChildByIndex", &UIWidget::getChildByIndex);
g_lua.bindClassMemberFunction<UIWidget>("getChildCount", &UIWidget::getChildCount);
@ -49,32 +46,34 @@ void LuaInterface::registerFunctions()
g_lua.bindClassMemberFunction<UIWidget>("removeChild", &UIWidget::removeChild);
g_lua.bindClassMemberFunction<UIWidget>("addChild", &UIWidget::addChild);
g_lua.bindClassMemberFunction<UIWidget>("lockChild", &UIWidget::lockChild);
g_lua.bindClassMemberFunction<UIWidget>("updateLayout", &UIWidget::updateLayout);
g_lua.bindClassMemberFunction<UIWidget>("updateParentLayout", &UIWidget::updateParentLayout);
g_lua.bindClassMemberFunction<UIWidget>("destroy", &UIWidget::destroy);
// UILabel
g_lua.registerClass<UILabel, UIWidget>();
g_lua.bindClassStaticFunction<UILabel>("create", &UILabel::create);
g_lua.bindClassStaticFunction<UILabel>("create", &UIWidget::create<UILabel>);
g_lua.bindClassMemberFunction<UILabel>("getText", &UILabel::getText);
g_lua.bindClassMemberFunction<UILabel>("setText", &UILabel::setText);
g_lua.bindClassMemberFunction("resizeToText", &UILabel::resizeToText);
// UIButton
g_lua.registerClass<UIButton, UIWidget>();
g_lua.bindClassStaticFunction<UIButton>("create", &UIButton::create);
g_lua.bindClassStaticFunction<UIButton>("create", &UIWidget::create<UIButton>);
g_lua.bindClassMemberFunction<UIButton>("getText", &UIButton::getText);
g_lua.bindClassMemberFunction<UIButton>("setText", &UIButton::setText);
// UILineEdit
g_lua.registerClass<UILineEdit, UIWidget>();
g_lua.bindClassStaticFunction<UILineEdit>("create", &UILineEdit::create);
g_lua.bindClassStaticFunction<UILineEdit>("create", &UIWidget::create<UILineEdit>);
g_lua.bindClassMemberFunction<UILineEdit>("getText", &UILineEdit::getText);
g_lua.bindClassMemberFunction<UILineEdit>("setText", &UILineEdit::setText);
g_lua.bindClassMemberFunction<UILineEdit>("clearText", &UILineEdit::clearText);
// UIWindow
g_lua.registerClass<UIWindow, UIWidget>();
g_lua.bindClassStaticFunction<UIWindow>("create", &UIWindow::create);
g_lua.bindClassStaticFunction<UIWindow>("create", &UIWidget::create<UIWindow>);
g_lua.bindClassMemberFunction<UIWindow>("getTitle", &UIWindow::getTitle);
g_lua.bindClassMemberFunction<UIWindow>("setTitle", &UIWindow::setTitle);
@ -96,7 +95,7 @@ void LuaInterface::registerFunctions()
g_lua.bindGlobalFunction("importFont", std::bind(&FontManager::importFont, &g_fonts, _1));
g_lua.bindGlobalFunction("importStyles", std::bind(&UIManager::importStyles, &g_ui, _1));
g_lua.bindGlobalFunction("setDefaultFont", std::bind(&FontManager::setDefaultFont, &g_fonts, _1));
g_lua.bindGlobalFunction("loadUI", std::bind(&UIManager::loadUI, &g_ui, _1));
g_lua.bindGlobalFunction("loadUI", std::bind(&UIManager::loadUI, &g_ui, _1, _2));
g_lua.bindGlobalFunction("getRootWidget", std::bind(&UIManager::getRootWidget, &g_ui));
g_lua.bindGlobalFunction("addEvent", std::bind(&EventDispatcher::addEvent, &g_dispatcher, _1, false));
g_lua.bindGlobalFunction("scheduleEvent", std::bind(&EventDispatcher::scheduleEvent, &g_dispatcher, _1, _2));

View File

@ -50,6 +50,7 @@ public:
void hideMouseCursor();
void showMouseCursor();
Point getMouseCursorPos();
/// Enable or disable vertical synchronization
void setVerticalSync(bool enable);

View File

@ -41,7 +41,7 @@ struct X11PlatformPrivate {
int lastTicks;
std::string clipboardText;
std::map<int, uchar> keyMap;
PlatformListener* listener;
PlatformEvent inputEvent;
} x11;
Platform g_platform;
@ -261,7 +261,7 @@ void Platform::terminate()
void Platform::poll()
{
XEvent event, peekevent;
static PlatformEvent inputEvent;
PlatformEvent& inputEvent = x11.inputEvent;
while(XPending(x11.display) > 0) {
XNextEvent(x11.display, &event);
@ -335,7 +335,6 @@ void Platform::poll()
) {
//logDebug("char: ", buf[0], " code: ", (uint)buf[0]);
inputEvent.keychar = buf[0];
dump << int((uchar)buf[0]);
}
} else {
//event.xkey.state &= ~(ShiftMask | LockMask);
@ -734,6 +733,11 @@ void Platform::showMouseCursor()
}
}
Point Platform::getMouseCursorPos()
{
return x11.inputEvent.mousePos;
}
void Platform::setVerticalSync(bool enable)
{
typedef GLint (*glSwapIntervalProc)(GLint);

View File

@ -1,39 +0,0 @@
#ifndef FRAMEWORK_UI_CONST_H
#define FRAMEWORK_UI_CONST_H
// namespace ui {
namespace UI {
enum FocusReason {
MouseFocusReason = 0,
TabFocusReason,
ActiveFocusReason,
OtherFocusReason
};
enum MouseButton {
MouseNoButton = 0,
MouseLeftButton,
MouseRightButton,
MouseMidButton
};
enum MouseWheelDirection {
MouseNoWheel = 0,
MouseWheelUp,
MouseWheelDown
};
enum KeyboardModifier {
KeyboardNoModifier = 0,
KeyboardCtrlModifier = 1,
KeyboardAltModifier = 2,
KeyboardShiftModifier = 4
};
}
// }
#endif

View File

@ -2,16 +2,17 @@
#define FRAMEWORK_UI_DECLARATIONS_H
#include <framework/global.h>
#include "const.h"
#include <framework/platform/platformevent.h>
class UIManager;
class UIAnchor;
class UIWidget;
class UILabel;
class UIButton;
class UILineEdit;
class UIWindow;
class UILayout;
class UIVerticalLayout;
class UIAnchorLayout;
typedef std::shared_ptr<UIWidget> UIWidgetPtr;
typedef std::weak_ptr<UIWidget> UIWidgetWeakPtr;
@ -20,8 +21,10 @@ typedef std::shared_ptr<UILabel> UILabelPtr;
typedef std::shared_ptr<UIButton> UIButtonPtr;
typedef std::shared_ptr<UILineEdit> UILineEditPtr;
typedef std::shared_ptr<UIWindow> UIWindowPtr;
typedef std::shared_ptr<UILayout> UILayoutPtr;
typedef std::shared_ptr<UIVerticalLayout> UIVerticalLayoutPtr;
typedef std::shared_ptr<UIAnchorLayout> UIAnchorLayoutPtr;
typedef std::vector<UIAnchor> UIAnchorList;
typedef std::deque<UIWidgetPtr> UIWidgetList;
typedef std::deque<UIWidgetWeakPtr> UIWeakWidgetList;

View File

@ -1,31 +0,0 @@
#include "uianchor.h"
#include "uiwidget.h"
UIAnchor::UIAnchor(AnchorEdge anchoredEdge, const std::string& hookedWidgetId, AnchorEdge hookedEdge) :
m_anchoredEdge(anchoredEdge), m_hookedWidgetId(hookedWidgetId), m_hookedEdge(hookedEdge) {
}
int UIAnchor::getHookedPoint() const {
UIWidgetPtr hookedWidget = getHookedWidget();
if(hookedWidget) {
switch(m_hookedEdge) {
case AnchorLeft:
return hookedWidget->getRect().left();
case AnchorRight:
return hookedWidget->getRect().right();
case AnchorTop:
return hookedWidget->getRect().top();
case AnchorBottom:
return hookedWidget->getRect().bottom();
case AnchorHorizontalCenter:
return hookedWidget->getRect().horizontalCenter();
case AnchorVerticalCenter:
return hookedWidget->getRect().verticalCenter();
default:
break;
}
}
return INVALID_POINT;
}

View File

@ -1,29 +0,0 @@
#ifndef UIANCHOR_H
#define UIANCHOR_H
#include "declarations.h"
class UIAnchor
{
public:
enum {
INVALID_POINT = -999999
};
UIAnchor(AnchorEdge anchoredEdge, const std::string& hookedWidgetId, AnchorEdge hookedEdge);
AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; }
UIWidgetPtr getHookedWidget() const { return m_hookedWidget.lock(); }
std::string getHookedWidgetId() const { return m_hookedWidgetId; }
int getHookedPoint() const;
void setHookedWidget(const UIWidgetPtr hookedWidget) { m_hookedWidget = hookedWidget; }
private:
AnchorEdge m_anchoredEdge;
UIWidgetWeakPtr m_hookedWidget;
std::string m_hookedWidgetId;
AnchorEdge m_hookedEdge;
};
#endif

View File

@ -0,0 +1,190 @@
#include "uianchorlayout.h"
#include "uiwidget.h"
void UIAnchorGroup::addAnchor(const UIAnchor& anchor)
{
// duplicated anchors must be replaced
for(UIAnchor& other : m_anchors) {
if(other.getAnchoredEdge() == anchor.getAnchoredEdge()) {
other = anchor;
return;
}
}
m_anchors.push_back(anchor);
}
void UIAnchorLayout::addAnchor(const UIWidgetPtr& anchoredWidget, AnchorEdge anchoredEdge,
const std::string& hookedWidgetId, AnchorEdge hookedEdge)
{
if(!anchoredWidget)
return;
assert(anchoredWidget != getParentWidget());
UIAnchor anchor(anchoredEdge, hookedWidgetId, hookedEdge);
UIAnchorGroup& anchorGroup = m_anchorsGroups[anchoredWidget];
anchorGroup.addAnchor(anchor);
// layout must be updated because a new anchor got in
update();
}
void UIAnchorLayout::removeAnchors(const UIWidgetPtr& anchoredWidget)
{
m_anchorsGroups.erase(anchoredWidget);
update();
}
void UIAnchorLayout::centerIn(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId)
{
addAnchor(anchoredWidget, AnchorHorizontalCenter, hookedWidgetId, AnchorHorizontalCenter);
addAnchor(anchoredWidget, AnchorVerticalCenter, hookedWidgetId, AnchorVerticalCenter);
}
void UIAnchorLayout::fill(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId)
{
addAnchor(anchoredWidget, AnchorLeft, hookedWidgetId, AnchorLeft);
addAnchor(anchoredWidget, AnchorRight, hookedWidgetId, AnchorRight);
addAnchor(anchoredWidget, AnchorTop, hookedWidgetId, AnchorTop);
addAnchor(anchoredWidget, AnchorBottom, hookedWidgetId, AnchorBottom);
}
void UIAnchorLayout::update()
{
// reset all anchors groups update state
for(auto& it : m_anchorsGroups) {
UIAnchorGroup& anchorGroup = it.second;
anchorGroup.setUpdated(false);
}
// update all anchors
for(auto& it : m_anchorsGroups) {
const UIWidgetPtr& widget = it.first;
UIAnchorGroup& anchorGroup = it.second;
if(!anchorGroup.isUpdated())
updateWidget(widget, anchorGroup);
}
}
void UIAnchorLayout::addWidget(const UIWidgetPtr& widget)
{
update();
}
void UIAnchorLayout::removeWidget(const UIWidgetPtr& widget)
{
removeAnchors(widget);
}
void UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup)
{
UIWidgetPtr parentWidget = getParentWidget();
Rect newRect = widget->getRect();
bool verticalMoved = false;
bool horizontalMoved = false;
// calculates new rect based on anchors
for(const UIAnchor& anchor : anchorGroup.getAnchors()) {
// skip invalid anchors
if(anchor.getHookedEdge() == AnchorNone)
continue;
// determine hooked widget
UIWidgetPtr hookedWidget;
if(parentWidget) {
if(anchor.getHookedWidgetId() == "parent")
hookedWidget = parentWidget;
else if(anchor.getHookedWidgetId() == "next")
hookedWidget = parentWidget->getChildAfter(widget);
else if(anchor.getHookedWidgetId() == "prev")
hookedWidget = parentWidget->getChildBefore(widget);
else
hookedWidget = parentWidget->getChildById(anchor.getHookedWidgetId());
}
// skip invalid anchors
if(!hookedWidget)
continue;
if(hookedWidget != getParentWidget()) {
// update this hooked widget anchors
auto it = m_anchorsGroups.find(hookedWidget);
if(it != m_anchorsGroups.end()) {
UIAnchorGroup& hookedAnchorGroup = it->second;
if(!hookedAnchorGroup.isUpdated())
updateWidget(hookedWidget, hookedAnchorGroup);
}
}
// determine hooked widget edge point
int point = 0;
switch(anchor.getHookedEdge()) {
case AnchorLeft:
point = hookedWidget->getRect().left();
break;
case AnchorRight:
point = hookedWidget->getRect().right();
break;
case AnchorTop:
point = hookedWidget->getRect().top();
break;
case AnchorBottom:
point = hookedWidget->getRect().bottom();
break;
case AnchorHorizontalCenter:
point = hookedWidget->getRect().horizontalCenter();
break;
case AnchorVerticalCenter:
point = hookedWidget->getRect().verticalCenter();
break;
default:
// must never happens
assert(false);
break;
}
switch(anchor.getAnchoredEdge()) {
case AnchorHorizontalCenter:
newRect.moveHorizontalCenter(point + widget->getMarginLeft() - widget->getMarginRight());
horizontalMoved = true;
break;
case AnchorLeft:
if(!horizontalMoved) {
newRect.moveLeft(point + widget->getMarginLeft());
horizontalMoved = true;
} else
newRect.setLeft(point + widget->getMarginLeft());
break;
case AnchorRight:
if(!horizontalMoved) {
newRect.moveRight(point - widget->getMarginRight());
horizontalMoved = true;
} else
newRect.setRight(point - widget->getMarginRight());
break;
case AnchorVerticalCenter:
newRect.moveVerticalCenter(point + widget->getMarginTop() - widget->getMarginBottom());
verticalMoved = true;
break;
case AnchorTop:
if(!verticalMoved) {
newRect.moveTop(point + widget->getMarginTop());
verticalMoved = true;
} else
newRect.setTop(point + widget->getMarginTop());
break;
case AnchorBottom:
if(!verticalMoved) {
newRect.moveBottom(point - widget->getMarginBottom());
verticalMoved = true;
} else
newRect.setBottom(point - widget->getMarginBottom());
break;
default:
break;
}
}
widget->setRect(newRect);
anchorGroup.setUpdated(true);
}

View File

@ -0,0 +1,59 @@
#ifndef UIANCHORLAYOUT_H
#define UIANCHORLAYOUT_H
#include "uilayout.h"
class UIAnchor
{
public:
UIAnchor(AnchorEdge anchoredEdge, const std::string& hookedWidgetId, AnchorEdge hookedEdge) :
m_anchoredEdge(anchoredEdge), m_hookedEdge(hookedEdge), m_hookedWidgetId(hookedWidgetId) { }
AnchorEdge getAnchoredEdge() const { return m_anchoredEdge; }
std::string getHookedWidgetId() const { return m_hookedWidgetId; }
AnchorEdge getHookedEdge() const { return m_hookedEdge; }
private:
AnchorEdge m_anchoredEdge;
AnchorEdge m_hookedEdge;
std::string m_hookedWidgetId;
};
class UIAnchorGroup
{
public:
UIAnchorGroup() : m_updated(true) { }
void addAnchor(const UIAnchor& anchor);
const std::vector<UIAnchor>& getAnchors() const { return m_anchors; }
bool isUpdated() const { return m_updated; }
void setUpdated(bool updated) { m_updated = updated; }
private:
std::vector<UIAnchor> m_anchors;
bool m_updated;
};
class UIAnchorLayout : public UILayout
{
public:
UIAnchorLayout(UIWidgetPtr parentWidget) : UILayout(parentWidget) { }
void addAnchor(const UIWidgetPtr& anchoredWidget, AnchorEdge anchoredEdge,
const std::string& hookedWidgetId, AnchorEdge hookedEdge);
void removeAnchors(const UIWidgetPtr& anchoredWidget);
void centerIn(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId);
void fill(const UIWidgetPtr& anchoredWidget, const std::string& hookedWidgetId);
void update();
void addWidget(const UIWidgetPtr& widget);
void removeWidget(const UIWidgetPtr& widget);
UIAnchorLayoutPtr asUIAnchorLayout() { return std::static_pointer_cast<UIAnchorLayout>(shared_from_this()); }
private:
void updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anchorGroup);
std::map<UIWidgetPtr, UIAnchorGroup> m_anchorsGroups;
};
#endif

View File

@ -5,90 +5,44 @@
#include <framework/luascript/luainterface.h>
#include <framework/graphics/graphics.h>
UIButton::UIButton()
void UIButton::setup()
{
m_state = ButtonUp;
m_focusable = false;
UIWidget::setup();
setFocusable(false);
// by default, all callbacks call lua fields
m_onClick = [this]() { this->callLuaField("onClick"); };
}
void UIButton::render()
{
UIWidget::render();
Rect textRect = m_rect;
textRect.translate(m_textTranslate);
m_font->renderText(m_text, textRect, AlignCenter, m_foregroundColor);
}
void UIButton::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
for(int i=0; i<3; ++i) {
m_statesStyle[i].image = m_image;
m_statesStyle[i].color = m_backgroundColor;
m_statesStyle[i].foregroundColor = m_foregroundColor;
m_statesStyle[i].textTranslate = Point(0,0);
}
if(OTMLNodePtr node = styleNode->get("state.up"))
loadStateStyle(m_statesStyle[ButtonUp], node);
if(OTMLNodePtr node = styleNode->get("state.hover"))
loadStateStyle(m_statesStyle[ButtonHover], node);
if(OTMLNodePtr node = styleNode->get("state.down"))
loadStateStyle(m_statesStyle[ButtonDown], node);
m_text = styleNode->valueAt("text", fw::empty_string);
if(OTMLNodePtr node = styleNode->get("onClick")) {
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
luaSetField(node->tag());
for(OTMLNodePtr node : styleNode->children()) {
if(node->tag() == "text-translate") {
m_textTranslate = node->value<Point>();
} else if(node->tag() == "text") {
m_text = node->value();
} else if(node->tag() == "onClick") {
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
luaSetField(node->tag());
}
}
}
void UIButton::loadStateStyle(ButtonStateStyle& stateStyle, const OTMLNodePtr& stateStyleNode)
bool UIButton::onMouseRelease(const Point& mousePos, MouseButton button)
{
if(OTMLNodePtr node = stateStyleNode->get("border-image"))
stateStyle.image = BorderImage::loadFromOTML(node);
if(OTMLNodePtr node = stateStyleNode->get("image"))
stateStyle.image = Image::loadFromOTML(node);
stateStyle.textTranslate = stateStyleNode->valueAt("text-translate", Point());
stateStyle.color = stateStyleNode->valueAt("font-color", m_foregroundColor);
stateStyle.color = stateStyleNode->valueAt("color", m_backgroundColor);
}
void UIButton::render()
{
UIWidget::render();
const ButtonStateStyle& currentStyle = m_statesStyle[m_state];
Rect textRect = getRect();
if(currentStyle.image) {
g_graphics.bindColor(currentStyle.color);
currentStyle.image->draw(textRect);
}
textRect.translate(currentStyle.textTranslate);
m_font->renderText(m_text, textRect, AlignCenter, currentStyle.foregroundColor);
}
void UIButton::onHoverChange(bool hovered)
{
if(hovered && m_state == ButtonUp)
m_state = ButtonHover;
else if(m_state == ButtonHover)
m_state = ButtonUp;
}
bool UIButton::onMousePress(const Point& mousePos, UI::MouseButton button)
{
if(button == UI::MouseLeftButton) {
m_state = ButtonDown;
}
return true;
}
bool UIButton::onMouseRelease(const Point& mousePos, UI::MouseButton button)
{
if(m_state == ButtonDown) {
if(isPressed()) {
if(m_onClick && getRect().contains(mousePos))
m_onClick();
m_state = (isHovered() && isEnabled()) ? ButtonHover : ButtonUp;
return true;
}
return false;

View File

@ -5,26 +5,8 @@
class UIButton : public UIWidget
{
struct ButtonStateStyle {
ImagePtr image;
Point textTranslate;
Color foregroundColor;
Color color;
};
enum ButtonState {
ButtonUp = 0,
ButtonDown,
ButtonHover
};
public:
UIButton();
static UIButtonPtr create() { return UIButtonPtr(new UIButton); }
virtual void onStyleApply(const OTMLNodePtr& styleNode);
void loadStateStyle(ButtonStateStyle& stateStyle, const OTMLNodePtr& stateStyleNode);
virtual void setup();
virtual void render();
void setOnClick(const SimpleCallback& onClick) { m_onClick = onClick; }
@ -32,18 +14,15 @@ public:
SimpleCallback getOnClick() const { return m_onClick; }
std::string getText() const { return m_text; }
ButtonState getState() const { return m_state; }
UIButtonPtr asUIButton() { return std::static_pointer_cast<UIButton>(shared_from_this()); }
protected:
virtual void onHoverChange(bool hovered);
virtual bool onMousePress(const Point& mousePos, UI::MouseButton button);
virtual bool onMouseRelease(const Point& mousePos, UI::MouseButton button);
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual bool onMouseRelease(const Point& mousePos, MouseButton button);
ButtonState m_state;
ButtonStateStyle m_statesStyle[3];
SimpleCallback m_onClick;
Point m_textTranslate;
std::string m_text;
};

View File

@ -2,22 +2,11 @@
#include <framework/graphics/font.h>
#include <framework/otml/otmlnode.h>
UILabel::UILabel()
void UILabel::setup()
{
m_align = AlignLeft;
m_focusable = false;
}
void UILabel::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "text")
setText(node->value());
else if(node->tag() == "align")
setAlign(fw::translateAlignment(styleNode->value()));
}
UIWidget::setup();
setFocusable(false);
setAlign(AlignLeft);
}
void UILabel::render()
@ -30,8 +19,10 @@ void UILabel::setText(const std::string& text)
{
m_text = text;
// auto resize if the current rect is invalid
if(!m_rect.isValid()) {
// auto resize
if(!m_fixedSize)
resizeToText();
else if(!m_rect.isValid()) {
Size textSize = m_font->calculateTextRectSize(m_text);
if(m_rect.width() <= 0)
m_rect.setWidth(textSize.width());
@ -44,3 +35,15 @@ void UILabel::resizeToText()
{
resize(m_font->calculateTextRectSize(m_text));
}
void UILabel::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "text")
setText(node->value());
else if(node->tag() == "align")
setAlign(fw::translateAlignment(node->value()));
}
}

View File

@ -6,11 +6,7 @@
class UILabel : public UIWidget
{
public:
UILabel();
static UILabelPtr create() { return UILabelPtr(new UILabel); }
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual void setup();
virtual void render();
void resizeToText();
@ -21,6 +17,9 @@ public:
std::string getText() const { return m_text; }
AlignmentFlag getAlign() const { return m_align; }
protected:
virtual void onStyleApply(const OTMLNodePtr& styleNode);
private:
std::string m_text;
AlignmentFlag m_align;

View File

@ -0,0 +1 @@
#include "uilayout.h"

View File

@ -0,0 +1,24 @@
#ifndef UILAYOUT_H
#define UILAYOUT_H
#include "declarations.h"
#include <framework/luascript/luaobject.h>
class UILayout : public LuaObject
{
public:
UILayout(UIWidgetPtr parentWidget) : m_parentWidget(parentWidget) { }
virtual void update() = 0;
virtual void addWidget(const UIWidgetPtr& widget) = 0;
virtual void removeWidget(const UIWidgetPtr& widget) = 0;
UIWidgetPtr getParentWidget() { return m_parentWidget.lock(); }
virtual UIAnchorLayoutPtr asUIAnchorLayout() { return nullptr; }
protected:
UIWidgetWeakPtr m_parentWidget;
};
#endif

View File

@ -28,7 +28,7 @@ void UILineEdit::render()
g_graphics.drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]);
// render cursor
if(isExplicitlyEnabled() && hasFocus() && m_cursorPos >= 0) {
if(isExplicitlyEnabled() && isActive() && m_cursorPos >= 0) {
assert(m_cursorPos <= textLength);
// draw every 333ms
const int delay = 333;
@ -322,11 +322,14 @@ void UILineEdit::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
setText(styleNode->valueAt("text", getText()));
if(OTMLNodePtr node = styleNode->get("onAction")) {
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
luaSetField(node->tag());
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "text") {
setText(node->value());
setCursorPos(m_text.length());
} else if(node->tag() == "onAction") {
g_lua.loadFunction(node->value(), "@" + node->source() + "[" + node->tag() + "]");
luaSetField(node->tag());
}
}
}
@ -335,11 +338,11 @@ void UILineEdit::onGeometryUpdate(const Rect& oldRect, const Rect& newRect)
update();
}
void UILineEdit::onFocusChange(bool focused, UI::FocusReason reason)
void UILineEdit::onFocusChange(bool focused, FocusReason reason)
{
if(focused) {
if(reason == UI::TabFocusReason)
setCursorPos(0);
if(reason == TabFocusReason)
setCursorPos(m_text.length());
else
blinkCursor();
}
@ -361,7 +364,7 @@ bool UILineEdit::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
setCursorPos(m_text.length());
else if(keyCode == KC_TAB) {
if(UIWidgetPtr parent = getParent())
parent->focusNextChild(UI::TabFocusReason);
parent->focusNextChild(TabFocusReason);
} else if(keyCode == KC_RETURN) {
if(m_onAction)
m_onAction();
@ -373,9 +376,9 @@ bool UILineEdit::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
return true;
}
bool UILineEdit::onMousePress(const Point& mousePos, UI::MouseButton button)
bool UILineEdit::onMousePress(const Point& mousePos, MouseButton button)
{
if(button == UI::MouseLeftButton) {
if(button == MouseLeftButton) {
int pos = getTextPos(mousePos);
if(pos >= 0)
setCursorPos(pos);

View File

@ -8,8 +8,6 @@ class UILineEdit : public UIWidget
public:
UILineEdit();
static UILineEditPtr create() { return UILineEditPtr(new UILineEdit); }
virtual void render();
void update();
@ -31,9 +29,9 @@ public:
protected:
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect);
virtual void onFocusChange(bool focused, UI::FocusReason reason);
virtual void onFocusChange(bool focused, FocusReason reason);
virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers);
virtual bool onMousePress(const Point& mousePos, UI::MouseButton button);
virtual bool onMousePress(const Point& mousePos, MouseButton button);
private:
void blinkCursor();

View File

@ -1,32 +0,0 @@
#include "uilist.h"
UIList::UIList()
{
}
void UIList::render()
{
}
void UIList::onStyleApply(const OTMLNodePtr& styleNode)
{
}
bool UIList::onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers)
{
return false;
}
bool UIList::onMousePress(const Point& mousePos, UI::MouseButton button)
{
return false;
}
bool UIList::onMouseMove(const Point& mousePos, const Point& mouseMoved)
{
return false;
}

View File

@ -1,74 +0,0 @@
#ifndef UITEXTLIST_H
#define UITEXTLIST_H
#include "uiwidget.h"
class UIListItem;
typedef std::shared_ptr<UIListItem> UIListItemPtr;
typedef std::list<UIListItemPtr> UIListItemList;
class UIListItem
{
public:
void setText(const std::string& text) { m_text = text; }
void setData(const boost::any& data) { m_data = data; }
boost::any getData() { return m_data; }
std::string getText() { return m_text; }
private:
std::string m_text;
boost::any m_data;
int height;
/*
Image m_image;
Color m_backgroundColor;
Color m_foregroundColor;
*/
};
class UIList : public UIWidget
{
public:
UIList();
virtual void render();
void insertItem(int row, const UIListItemPtr& item);
//void insertItems(int row, const UIListItemList& items);
int addItem(const UIListItemPtr& item);
//int addItems(const UIListItemList& items);
void removeItem(const UIListItemPtr& item);
void removeRow(int row);
UIListItemPtr takeRow(int row);
void setCurrentItem(const UIListItemPtr& item);
void setCurrentRow(int row);
UIListItemPtr getItem(int row);
UIListItemPtr getItemAt(const Point& point);
UIListItemList getItems();
int getItemRow(const UIListItemPtr& item);
int getItemsCount();
UIListItemPtr getCurrentItem();
int getCurrentRow();
protected:
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual bool onKeyPress(uchar keyCode, char keyChar, int keyboardModifiers);
virtual bool onMousePress(const Point& mousePos, UI::MouseButton button);
virtual bool onMouseMove(const Point& mousePos, const Point& mouseMoved);
// possible signals
//onItemActivated
//onItemChanged
//onCurrentItemChange
//onCurrenteRowChange
//onCurrentTextChange
//onItemClicked
std::deque<UIListItem> m_items;
};
#endif

View File

@ -9,9 +9,9 @@ UIManager g_ui;
void UIManager::init()
{
// creates root widget
m_rootWidget = UIWidgetPtr(new UIWidget);
m_rootWidget = UIWidget::create<UIWidget>();
m_rootWidget->setup();
m_rootWidget->setId("root");
m_rootWidget->setHovered(true);
m_rootWidget->resize(g_graphics.getScreenSize());
}
@ -38,13 +38,13 @@ void UIManager::inputEvent(const PlatformEvent& event)
// translate input event to ui events
if(m_rootWidget) {
if(event.type & EventKeyboardAction) {
int keyboardModifiers = UI::KeyboardNoModifier;
int keyboardModifiers = KeyboardNoModifier;
if(event.ctrl)
keyboardModifiers |= UI::KeyboardCtrlModifier;
keyboardModifiers |= KeyboardCtrlModifier;
if(event.shift)
keyboardModifiers |= UI::KeyboardShiftModifier;
keyboardModifiers |= KeyboardShiftModifier;
if(event.alt)
keyboardModifiers |= UI::KeyboardAltModifier;
keyboardModifiers |= KeyboardAltModifier;
if(event.type == EventKeyDown)
m_rootWidget->onKeyPress(event.keycode, event.keychar, keyboardModifiers);
@ -52,24 +52,25 @@ void UIManager::inputEvent(const PlatformEvent& event)
m_rootWidget->onKeyRelease(event.keycode, event.keychar, keyboardModifiers);
} else if(event.type & EventMouseAction) {
if(event.type == EventMouseMove) {
m_rootWidget->updateState(HoverState);
m_rootWidget->onMouseMove(event.mousePos, event.mouseMoved);
}
else if(event.type & EventMouseWheel) {
UI::MouseWheelDirection dir = UI::MouseNoWheel;
MouseWheelDirection dir = MouseNoWheel;
if(event.type & EventDown)
dir = UI::MouseWheelDown;
dir = MouseWheelDown;
else if(event.type & EventUp)
dir = UI::MouseWheelUp;
dir = MouseWheelUp;
m_rootWidget->onMouseWheel(event.mousePos, dir);
} else {
UI::MouseButton button = UI::MouseNoButton;
MouseButton button = MouseNoButton;
if(event.type & EventMouseLeftButton)
button = UI::MouseLeftButton;
button = MouseLeftButton;
else if(event.type & EventMouseMidButton)
button = UI::MouseMidButton;
button = MouseMidButton;
else if(event.type & EventMouseRightButton)
button = UI::MouseRightButton;
button = MouseRightButton;
if(event.type & EventDown)
m_rootWidget->onMousePress(event.mousePos, button);
@ -132,7 +133,7 @@ OTMLNodePtr UIManager::getStyle(const std::string& styleName)
return m_styles[styleName];
}
UIWidgetPtr UIManager::loadUI(const std::string& file)
UIWidgetPtr UIManager::loadUI(const std::string& file, const UIWidgetPtr& parent)
{
try {
OTMLDocumentPtr doc = OTMLDocument::parse(file);
@ -146,7 +147,7 @@ UIWidgetPtr UIManager::loadUI(const std::string& file)
else {
if(widget)
throw std::runtime_error("cannot have multiple main widgets in .otui files");
widget = loadWidgetFromOTML(node);
widget = loadWidgetFromOTML(node, parent);
}
}
@ -157,7 +158,7 @@ UIWidgetPtr UIManager::loadUI(const std::string& file)
}
}
UIWidgetPtr UIManager::loadWidgetFromOTML(const OTMLNodePtr& widgetNode)
UIWidgetPtr UIManager::loadWidgetFromOTML(const OTMLNodePtr& widgetNode, const UIWidgetPtr& parent)
{
OTMLNodePtr styleNode = getStyle(widgetNode->tag())->clone();
styleNode->merge(widgetNode);
@ -170,14 +171,16 @@ UIWidgetPtr UIManager::loadWidgetFromOTML(const OTMLNodePtr& widgetNode)
g_lua.getField("create");
g_lua.remove(-2);
g_lua.protectedCall(0, 1);
UIWidgetPtr widget = g_lua.polymorphicPop<UIWidgetPtr>();
widget->onStyleApply(styleNode);
widget->updateLayout();
UIWidgetPtr widget = g_lua.polymorphicPop<UIWidgetPtr>();
if(parent)
parent->addChild(widget);
widget->setStyleFromNode(styleNode);
for(const OTMLNodePtr& childNode : widgetNode->children()) {
if(!childNode->isUnique())
widget->addChild(loadWidgetFromOTML(childNode));
loadWidgetFromOTML(childNode, widget);
}
return widget;

View File

@ -19,8 +19,8 @@ public:
void importStyleFromOTML(const OTMLNodePtr& styleNode);
OTMLNodePtr getStyle(const std::string& styleName);
UIWidgetPtr loadUI(const std::string& file);
UIWidgetPtr loadWidgetFromOTML(const OTMLNodePtr& widgetNode);
UIWidgetPtr loadUI(const std::string& file, const UIWidgetPtr& parent = nullptr);
UIWidgetPtr loadWidgetFromOTML(const OTMLNodePtr& widgetNode, const UIWidgetPtr& parent);
UIWidgetPtr getRootWidget() { return m_rootWidget; }

View File

@ -0,0 +1,37 @@
#include "uiverticallayout.h"
#include "uiwidget.h"
UIVerticalLayout::UIVerticalLayout(UIWidgetPtr parentWidget)
: UILayout(parentWidget)
{
}
void UIVerticalLayout::update()
{
UIWidgetPtr parentWidget = getParentWidget();
UIWidgetList widgets = parentWidget->getChildren();
Point pos = parentWidget->getPosition();
for(const UIWidgetPtr& widget : widgets) {
Size size = widget->getSize();
pos.y += widget->getMarginTop();
if(widget->isSizeFixed()) {
pos.x = parentWidget->getX() + (parentWidget->getWidth() - (widget->getMarginLeft() + widget->getWidth() + widget->getMarginRight()))/2;
pos.x = std::max(pos.x, parentWidget->getX());
} else {
size.setWidth(parentWidget->getWidth() - (widget->getMarginLeft() + widget->getMarginRight()));
pos.x = std::max(pos.x, parentWidget->getX() + (parentWidget->getWidth() - size.width())/2);
}
widget->setRect(Rect(pos, size));
pos.y += widget->getHeight() + widget->getMarginBottom();
}
}
void UIVerticalLayout::addWidget(const UIWidgetPtr& widget)
{
update();
}
void UIVerticalLayout::removeWidget(const UIWidgetPtr& widget)
{
update();
}

View File

@ -0,0 +1,16 @@
#ifndef UIVERTICALLAYOUT_H
#define UIVERTICALLAYOUT_H
#include "uilayout.h"
class UIVerticalLayout : public UILayout
{
public:
UIVerticalLayout(UIWidgetPtr parentWidget);
virtual void update();
virtual void addWidget(const UIWidgetPtr& widget);
virtual void removeWidget(const UIWidgetPtr& widget);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
#include "declarations.h"
#include <framework/luascript/luaobject.h>
#include <framework/graphics/declarations.h>
#include <framework/otml/declarations.h>
class UIWidget : public LuaObject
{
@ -12,60 +11,65 @@ public:
UIWidget();
virtual ~UIWidget();
static UIWidgetPtr create() { return UIWidgetPtr(new UIWidget); }
template<class T>
static std::shared_ptr<T> create() { auto t = std::shared_ptr<T>(new T); t->setup(); return t; }
/// Must be called just after the widget creation
virtual void setup() { }
void destroy();
/// Remove this widget from parent then destroy it and its children
virtual void destroy();
/// Draw widget on screen
virtual void setup();
virtual void render();
void setEnabled(bool enable) { m_enabled = enable; }
void setEnabled(bool enabled) { m_enabled = enabled; updateState(DisabledState); }
void setVisible(bool visible) { m_visible = visible; }
void setPressed(bool pressed) { m_pressed = pressed; updateState(PressedState); }
void setId(const std::string& id) { m_id = id; }
void setFocusable(bool focusable) { m_focusable = focusable; }
void setHovered(bool hovered) { m_hovered = hovered; }
void setVisible(bool visible) { m_visible = visible; }
void setStyle(const std::string& styleName);
void setStyleFromNode(const OTMLNodePtr& styleNode);
void setLayout(const UILayoutPtr& layout) { m_layout = layout; }
void setParent(const UIWidgetPtr& parent);
void applyStyle(const std::string& styleName);
void setRect(const Rect& rect);
void setX(int x) { moveTo(Point(x, getY())); }
void setY(int y) { moveTo(Point(getX(), y)); }
void setWidth(int width) { resize(Size(width, getHeight())); }
void setHeight(int height) { resize(Size(getWidth(), height)); }
void resize(const Size& size) { setRect(Rect(getPosition(), size)); }
void moveTo(const Point& pos) { setRect(Rect(pos, getSize())); }
void setImage(const ImagePtr& image) { m_image = image; }
virtual void setFont(const FontPtr& font) { m_font = font; }
void setOpacity(int opacity) { m_opacity = opacity; }
void setBackgroundColor(const Color& color) { m_backgroundColor = color; }
void setForegroundColor(const Color& color) { m_foregroundColor = color; }
void setMarginLeft(int margin) { m_marginLeft = margin; updateLayout(); }
void setMarginRight(int margin) { m_marginRight = margin; updateLayout(); }
void setMarginTop(int margin) { m_marginTop = margin; updateLayout(); }
void setMarginBottom(int margin) { m_marginBottom = margin; updateLayout(); }
void setMarginLeft(int margin) { m_marginLeft = margin; updateParentLayout(); }
void setMarginRight(int margin) { m_marginRight = margin; updateParentLayout(); }
void setMarginTop(int margin) { m_marginTop = margin; updateParentLayout(); }
void setMarginBottom(int margin) { m_marginBottom = margin; updateParentLayout(); }
void setSizeFixed(bool fixed) { m_fixedSize = fixed; updateParentLayout(); }
void setLastFocusReason(FocusReason reason) { m_lastFocusReason = reason; }
void resize(const Size& size) { setRect(Rect(getPosition(), size)); }
void moveTo(const Point& pos) { setRect(Rect(pos, getSize())); }
void hide() { setVisible(false); }
void show() { setVisible(true); }
void disable() { setEnabled(false); }
void enable() { setEnabled(true); }
bool isEnabled();
bool isActive() const { return hasState(ActiveState); }
bool isEnabled() const { return !hasState(DisabledState); }
bool isDisabled() const { return hasState(DisabledState); }
bool isFocused() const { return hasState(FocusState); }
bool isHovered() const { return hasState(HoverState); }
bool isPressed() const { return hasState(PressedState); }
bool isVisible();
bool isExplicitlyEnabled() const { return m_enabled; }
bool isExplicitlyVisible() const { return m_visible; }
bool isHovered() const { return m_hovered; }
bool isFocusable() const { return m_focusable; }
bool isDestroyed() const { return m_destroyed; }
bool isSizeFixed() const { return m_fixedSize; }
bool hasChildren() const { return m_children.size() > 0; }
bool hasFocus();
bool hasChild(const UIWidgetPtr& child);
bool hasState(WidgetState state) const { return m_states & state; }
std::string getId() const { return m_id; }
int getChildCount() const { return m_children.size(); }
UILayoutPtr getLayout() const { return m_layout; }
UIWidgetPtr getParent() const { return m_parent.lock(); }
UIWidgetPtr getRootParent();
Point getPosition() const { return m_rect.topLeft(); }
@ -75,7 +79,6 @@ public:
int getY() const { return m_rect.y(); }
int getWidth() const { return m_rect.width(); }
int getHeight() const { return m_rect.height(); }
ImagePtr getImage() const { return m_image; }
FontPtr getFont() const { return m_font; }
Color getForegroundColor() const { return m_foregroundColor; }
@ -85,6 +88,7 @@ public:
int getMarginRight() const { return m_marginRight; }
int getMarginTop() const { return m_marginTop; }
int getMarginBottom() const { return m_marginBottom; }
FocusReason getLastFocusReason() const { return m_lastFocusReason; }
UIWidgetList getChildren() const { return m_children; }
UIWidgetPtr getFocusedChild() const { return m_focusedChild; }
@ -100,18 +104,18 @@ public:
void addChild(const UIWidgetPtr& child);
void insertChild(int index, const UIWidgetPtr& child);
void removeChild(const UIWidgetPtr& child);
void focusChild(const UIWidgetPtr& child, UI::FocusReason reason);
void focusNextChild(UI::FocusReason reason);
void focusChild(const UIWidgetPtr& child, FocusReason reason);
void focusNextChild(FocusReason reason);
void moveChildToTop(const UIWidgetPtr& child);
void lockChild(const UIWidgetPtr& child);
void unlockChild(const UIWidgetPtr& child);
void updateParentLayout();
void updateLayout();
void updateChildrenLayout();
bool addAnchor(AnchorEdge edge, const std::string& hookedWidgetId, AnchorEdge hookedEdge);
void centerIn(const std::string& hookedWidgetId);
void fill(const std::string& hookedWidgetId);
virtual void updateState(WidgetState state);
void updateStates();
virtual void updateStyle();
void applyStyle(const OTMLNodePtr& styleNode);
UIWidgetPtr asUIWidget() { return std::static_pointer_cast<UIWidget>(shared_from_this()); }
@ -119,19 +123,7 @@ private:
void internalDestroy();
void internalDestroyCheck();
void internalUpdateLayout();
void internalUpdateChildrenLayout();
void addAnchoredWidget(const UIWidgetPtr& widget);
void removeAnchoredWidget(const UIWidgetPtr& widget);
void computeHookedWidgets();
void clearHookedWidgets();
void resetLayoutUpdateState(bool resetOwn);
bool m_layoutUpdated;
bool m_updateEventScheduled;
bool m_layoutUpdateScheduled;
bool m_childrenLayoutUpdateScheduled;
protected:
/// Triggered when widget style is changed
@ -139,7 +131,7 @@ protected:
/// Triggered when widget is moved or resized
virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect);
/// Triggered when widget gets or loses focus
virtual void onFocusChange(bool focused, UI::FocusReason reason);
virtual void onFocusChange(bool focused, FocusReason reason);
/// Triggered when the mouse enters or leaves widget area
virtual void onHoverChange(bool hovered);
/// Triggered when user presses key while widget has focus
@ -147,30 +139,33 @@ protected:
/// Triggered when user releases key while widget has focus
virtual bool onKeyRelease(uchar keyCode, char keyChar, int keyboardModifiers);
/// Triggered when a mouse button is pressed down while mouse pointer is inside widget area
virtual bool onMousePress(const Point& mousePos, UI::MouseButton button);
virtual bool onMousePress(const Point& mousePos, MouseButton button);
/// Triggered when a mouse button is released
virtual bool onMouseRelease(const Point& mousePos, UI::MouseButton button);
virtual bool onMouseRelease(const Point& mousePos, MouseButton button);
/// Triggered when mouse moves (even when the mouse is outside widget area)
virtual bool onMouseMove(const Point& mousePos, const Point& mouseMoved);
/// Triggered when mouse middle button wheels inside widget area
virtual bool onMouseWheel(const Point& mousePos, UI::MouseWheelDirection direction);
virtual bool onMouseWheel(const Point& mousePos, MouseWheelDirection direction);
friend class UIManager;
protected:
std::string m_id;
FocusReason m_lastFocusReason;
bool m_enabled;
bool m_visible;
bool m_hovered;
bool m_focusable;
bool m_destroyed;
bool m_fixedSize;
bool m_pressed;
Rect m_rect;
UIAnchorList m_anchors;
UIWidgetList m_anchoredWidgets;
UILayoutPtr m_layout;
UIWidgetWeakPtr m_parent;
UIWidgetList m_children;
UIWidgetList m_lockedChildren;
UIWidgetPtr m_focusedChild;
std::string m_id;
OTMLNodePtr m_style;
OTMLNodePtr m_stateStyle;
uint m_states;
// basic style components used by all widgets
ImagePtr m_image;

View File

@ -4,39 +4,13 @@
#include <framework/graphics/graphics.h>
#include <framework/otml/otml.h>
UIWindow::UIWindow()
void UIWindow::setup()
{
UIWidget::setup();
m_moving = false;
}
UIWindowPtr UIWindow::create()
{
UIWindowPtr window(new UIWindow);
return window;
}
void UIWindow::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
if(OTMLNodePtr headNode = styleNode->get("head")) {
if(OTMLNodePtr node = headNode->get("border-image"))
m_headImage = BorderImage::loadFromOTML(node);
m_headHeight = headNode->valueAt("height", m_headImage->getDefaultSize().height());
m_headMargin = headNode->valueAt("margin", 0);
m_titleAlign = fw::translateAlignment(headNode->valueAt("text align", std::string("center")));
} else {
m_headHeight = 0;
m_headMargin = 0;
m_titleAlign = AlignCenter;
}
if(OTMLNodePtr bodyNode = styleNode->get("body")) {
if(OTMLNodePtr node = bodyNode->get("border-image"))
m_bodyImage = BorderImage::loadFromOTML(node);
}
m_title = styleNode->valueAt("title", fw::empty_string);
m_headHeight = 0;
m_headMargin = 0;
m_titleAlign = AlignCenter;
}
void UIWindow::render()
@ -61,13 +35,37 @@ void UIWindow::render()
// draw window body
Rect bodyRect = getRect();
bodyRect.setTop(headRect.bottom() + 1);
if(m_bodyImage)
if(m_bodyImage) {
g_graphics.bindColor(m_backgroundColor);
m_bodyImage->draw(bodyRect);
}
// render children
UIWidget::render();
}
void UIWindow::onStyleApply(const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleNode);
for(OTMLNodePtr node : styleNode->children()) {
if(node->tag() == "head") {
if(OTMLNodePtr cnode = node->get("border-image"))
m_headImage = BorderImage::loadFromOTML(cnode);
m_headHeight = node->valueAt("height", m_headImage->getDefaultSize().height());
m_headMargin = node->valueAt("margin", 0);
m_titleAlign = fw::translateAlignment(node->valueAt("text align", std::string("center")));
}
else if(node->tag() == "body") {
if(OTMLNodePtr cnode = node->get("border-image"))
m_bodyImage = BorderImage::loadFromOTML(cnode);
}
else if(node->tag() == "title") {
setTitle(node->value());
}
}
}
void UIWindow::onGeometryUpdate(const Rect& oldRect, const Rect& newRect)
{
// bind window rect to parent rect
@ -89,18 +87,18 @@ void UIWindow::onGeometryUpdate(const Rect& oldRect, const Rect& newRect)
setRect(boundRect);
}
void UIWindow::onFocusChange(bool focused, UI::FocusReason reason)
void UIWindow::onFocusChange(bool focused, FocusReason reason)
{
// when a window is focused it goes to the top
if(UIWidgetPtr parent = getParent())
parent->moveChildToTop(asUIWidget());
if(focused) {
if(UIWidgetPtr parent = getParent())
parent->moveChildToTop(asUIWidget());
}
}
bool UIWindow::onMousePress(const Point& mousePos, UI::MouseButton button)
bool UIWindow::onMousePress(const Point& mousePos, MouseButton button)
{
Rect headRect = getRect();
headRect.setHeight(m_headHeight);
if(headRect.contains(mousePos)) {
if(!getChildByPos(mousePos)) {
m_moving = true;
m_movingReference = mousePos - getRect().topLeft();
return true;
@ -108,7 +106,7 @@ bool UIWindow::onMousePress(const Point& mousePos, UI::MouseButton button)
return UIWidget::onMousePress(mousePos, button);
}
bool UIWindow::onMouseRelease(const Point& mousePos, UI::MouseButton button)
bool UIWindow::onMouseRelease(const Point& mousePos, MouseButton button)
{
if(m_moving) {
m_moving = false;

View File

@ -6,21 +6,18 @@
class UIWindow : public UIWidget
{
public:
UIWindow();
static UIWindowPtr create();
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual void setup();
virtual void render();
void setTitle(const std::string& title) { m_title = title; }
std::string getTitle() const { return m_title; }
protected:
virtual void onStyleApply(const OTMLNodePtr& styleNode);
virtual void onGeometryUpdate(const Rect& oldRect, const Rect& newRect);
virtual void onFocusChange(bool focused, UI::FocusReason reason);
virtual bool onMousePress(const Point& mousePos, UI::MouseButton button);
virtual bool onMouseRelease(const Point& mousePos, UI::MouseButton button);
virtual void onFocusChange(bool focused, FocusReason reason);
virtual bool onMousePress(const Point& mousePos, MouseButton button);
virtual bool onMouseRelease(const Point& mousePos, MouseButton button);
virtual bool onMouseMove(const Point& mousePos, const Point& mouseMoved);
private:

View File

@ -9,7 +9,7 @@ typedef uint32 RGBA;
class Color
{
public:
Color() : color(0xFFFFFFFF) { }
Color() : color(0x0) { }
Color(uint8 r, uint8 g, uint8 b, uint8 a = 0xFF) : color(((a & 0xff)<<24) | ((b & 0xff)<<16) | ((g & 0xff)<<8) | (r & 0xff)) { }
Color(const Color& other) : color(other.color) { }
Color(RGBA rgba) : color(rgba) { }

View File

@ -21,23 +21,26 @@ AlignmentFlag fw::translateAlignment(std::string aligment)
return AlignTopCenter;
else if(aligment == "bottom")
return AlignBottomCenter;
else
else if(aligment == "center")
return AlignCenter;
return AlignNone;
}
AnchorEdge fw::translateAnchorEdge(const std::string& anchorPoint)
AnchorEdge fw::translateAnchorEdge(std::string anchorEdge)
{
if(anchorPoint == "left")
boost::to_lower(anchorEdge);
boost::erase_all(anchorEdge, " ");
if(anchorEdge == "left")
return AnchorLeft;
else if(anchorPoint == "right")
else if(anchorEdge == "right")
return AnchorRight;
else if(anchorPoint == "top")
else if(anchorEdge == "top")
return AnchorTop;
else if(anchorPoint == "bottom")
else if(anchorEdge == "bottom")
return AnchorBottom;
else if(anchorPoint == "horizontalCenter")
else if(anchorEdge == "horizontalcenter")
return AnchorHorizontalCenter;
else if(anchorPoint == "verticalCenter")
else if(anchorEdge == "verticalcenter")
return AnchorVerticalCenter;
return AnchorNone;
}

View File

@ -7,7 +7,7 @@
namespace fw {
AlignmentFlag translateAlignment(std::string aligment);
AnchorEdge translateAnchorEdge(const std::string& anchorPoint);
AnchorEdge translateAnchorEdge(std::string anchorEdge);
};

View File

@ -1,7 +1,8 @@
#include <otclient/otclient.h>
#include <csignal>
#include <framework/core/eventdispatcher.h>
#include <csignal>
void signal_handler(int sig)
{
static bool signaled = false;

View File

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