remake ui event system and reimplement line edit
This commit is contained in:
parent
e22e5e2918
commit
09af50c990
|
@ -18,3 +18,4 @@ Window < UIWindow
|
||||||
|
|
||||||
MainWindow < Window
|
MainWindow < Window
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
onLoad: function(self) self:lock() end
|
|
@ -41,6 +41,7 @@ MainWindow
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
margin.bottom: 9
|
margin.bottom: 9
|
||||||
margin.right: 9
|
margin.right: 9
|
||||||
|
onClick: displayErrorBox("Error", "Not implemented yet")
|
||||||
|
|
||||||
HorizontalSeparator
|
HorizontalSeparator
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
Panel
|
|
||||||
skin: mainMenuBackground
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
|
|
||||||
Panel
|
|
||||||
id: mainMenu
|
|
||||||
skin: roundedGridPanel
|
|
||||||
size: 144 162
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
margin.left: 60
|
|
||||||
margin.bottom: 70
|
|
||||||
|
|
||||||
Button
|
|
||||||
id: enterGameButton
|
|
||||||
text: Enter Game
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
margin.top: 18
|
|
||||||
onClick: rootWidget:addChild(loadUI("entergamewindow"))
|
|
||||||
|
|
||||||
Button
|
|
||||||
id: optionsButton
|
|
||||||
text: Options
|
|
||||||
anchors.top: prev.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
margin.top: 10
|
|
||||||
onClick: rootWidget:addChild(loadUI("optionswindow"))
|
|
||||||
|
|
||||||
Button
|
|
||||||
id: infoButton
|
|
||||||
text: Info
|
|
||||||
anchors.top: prev.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
margin.top: 10
|
|
||||||
onClick: rootWidget:addChild(loadUI("infowindow"))
|
|
||||||
|
|
||||||
Button
|
|
||||||
id: exitGameButton
|
|
||||||
text: Exit
|
|
||||||
anchors.top: prev.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
margin.top: 10
|
|
||||||
onClick: exit()
|
|
|
@ -28,6 +28,7 @@ MainWindow
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
margin.left: 18
|
margin.left: 18
|
||||||
margin.top: 65
|
margin.top: 65
|
||||||
|
onClick: displayErrorBox("Error", "Not implemented yet")
|
||||||
|
|
||||||
Label
|
Label
|
||||||
text: |-
|
text: |-
|
||||||
|
@ -45,6 +46,7 @@ MainWindow
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
margin.left: 18
|
margin.left: 18
|
||||||
margin.top: 98
|
margin.top: 98
|
||||||
|
onClick: displayErrorBox("Error", "Not implemented yet")
|
||||||
|
|
||||||
Label
|
Label
|
||||||
text: Customise the console
|
text: Customise the console
|
||||||
|
@ -60,6 +62,7 @@ MainWindow
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
margin.left: 18
|
margin.left: 18
|
||||||
margin.top: 131
|
margin.top: 131
|
||||||
|
onClick: displayErrorBox("Error", "Not implemented yet")
|
||||||
|
|
||||||
Label
|
Label
|
||||||
text: Edit your hotkey texts
|
text: Edit your hotkey texts
|
||||||
|
@ -83,6 +86,7 @@ MainWindow
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
margin.left: 18
|
margin.left: 18
|
||||||
margin.bottom: 60
|
margin.bottom: 60
|
||||||
|
onClick: displayErrorBox("Error", "Not implemented yet")
|
||||||
|
|
||||||
Label
|
Label
|
||||||
text: |
|
text: |
|
||||||
|
|
|
@ -31,31 +31,6 @@ enum AnchorPoint {
|
||||||
AnchorHorizontalCenter,
|
AnchorHorizontalCenter,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MouseButton {
|
|
||||||
MouseNoButton = 0,
|
|
||||||
MouseLeftButton,
|
|
||||||
MouseRightButton,
|
|
||||||
MouseMidButton
|
|
||||||
};
|
|
||||||
|
|
||||||
enum MouseWheelDirection {
|
|
||||||
MouseNoWheel = 0,
|
|
||||||
MouseWheelUp,
|
|
||||||
MouseWheelDown
|
|
||||||
};
|
|
||||||
|
|
||||||
enum KeyboardModifier {
|
|
||||||
KeyboardNoModifier = 0,
|
|
||||||
KeyboardCtrlModifier = 1,
|
|
||||||
KeyboardAltModifier = 2,
|
|
||||||
KeyboardShiftModifier = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ButtonState {
|
|
||||||
ButtonUp = 0,
|
|
||||||
ButtonDown,
|
|
||||||
ButtonHover
|
|
||||||
};
|
|
||||||
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
|
|
@ -1,334 +0,0 @@
|
||||||
#include "textarea.h"
|
|
||||||
#include "font.h"
|
|
||||||
#include "graphics.h"
|
|
||||||
|
|
||||||
#include <core/platform.h>
|
|
||||||
|
|
||||||
TextArea::TextArea() :
|
|
||||||
m_align(AlignLeftCenter),
|
|
||||||
m_color(Color::white),
|
|
||||||
m_cursorPos(-1),
|
|
||||||
m_startRenderPos(0),
|
|
||||||
m_cursorVisible(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TextArea::TextArea(FontPtr font,
|
|
||||||
const std::string& text,
|
|
||||||
const Rect& screenCoords,
|
|
||||||
AlignmentFlag align,
|
|
||||||
const Color& color) :
|
|
||||||
m_font(font),
|
|
||||||
m_text(text),
|
|
||||||
m_screenCoords(screenCoords),
|
|
||||||
m_align(align),
|
|
||||||
m_color(color),
|
|
||||||
m_cursorPos(-1),
|
|
||||||
m_startRenderPos(0),
|
|
||||||
m_cursorVisible(false)
|
|
||||||
{
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::draw()
|
|
||||||
{
|
|
||||||
//TODO: text rendering could be much optimized by using vertex buffer or caching the render into a texture
|
|
||||||
|
|
||||||
int textLength = m_text.length();
|
|
||||||
const TexturePtr& texture = m_font->getTexture();
|
|
||||||
for(int i=0;i<textLength;++i) {
|
|
||||||
g_graphics.drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i], m_color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// render cursor
|
|
||||||
if(m_cursorVisible && m_cursorPos >= 0) {
|
|
||||||
assert(m_cursorPos <= textLength);
|
|
||||||
const int delay = 500;
|
|
||||||
int ticks = g_platform.getTicks();
|
|
||||||
// draw every 500ms
|
|
||||||
if(ticks - m_cursorTicks <= delay) {
|
|
||||||
Rect cursorRect;
|
|
||||||
// when cursor is at 0 or is the first visible element
|
|
||||||
if(m_cursorPos == 0 || m_cursorPos == m_startRenderPos)
|
|
||||||
cursorRect = Rect(m_drawArea.left()-1, m_drawArea.top(), 1, m_font->getGlyphHeight());
|
|
||||||
else
|
|
||||||
cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight());
|
|
||||||
g_graphics.drawFilledRect(cursorRect, m_color);
|
|
||||||
} else if(ticks - m_cursorTicks >= 2*delay) {
|
|
||||||
m_cursorTicks = ticks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::recalculate()
|
|
||||||
{
|
|
||||||
int textLength = m_text.length();
|
|
||||||
|
|
||||||
// prevent glitches
|
|
||||||
if(!m_screenCoords.isValid() || !m_font)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// map glyphs positions
|
|
||||||
Size textBoxSize;
|
|
||||||
const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize);
|
|
||||||
const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords();
|
|
||||||
const Size *glyphsSize = m_font->getGlyphsSize();
|
|
||||||
int glyph;
|
|
||||||
|
|
||||||
// resize just on demand
|
|
||||||
if(textLength > (int)m_glyphsCoords.size()) {
|
|
||||||
m_glyphsCoords.resize(textLength);
|
|
||||||
m_glyphsTexCoords.resize(textLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
// readjust start view area based on cursor position
|
|
||||||
if(m_cursorPos >= 0 && textLength > 0) {
|
|
||||||
assert(m_cursorPos <= textLength);
|
|
||||||
if(m_cursorPos < m_startRenderPos) // cursor is before the previuos first rendered glyph, so we need to update
|
|
||||||
{
|
|
||||||
m_startInternalPos.x = glyphsPositions[m_cursorPos].x;
|
|
||||||
m_startInternalPos.y = glyphsPositions[m_cursorPos].y - m_font->getTopMargin();
|
|
||||||
m_startRenderPos = m_cursorPos;
|
|
||||||
} else if(m_cursorPos > m_startRenderPos || // cursor is after the previuos first rendered glyph
|
|
||||||
(m_cursorPos == m_startRenderPos && textLength == m_cursorPos)) // cursor is at the previuos rendered element, and is the last text element
|
|
||||||
{
|
|
||||||
Rect virtualRect(m_startInternalPos, m_screenCoords.size()); // previous rendered virtual rect
|
|
||||||
int pos = m_cursorPos - 1; // element before cursor
|
|
||||||
glyph = (uchar)m_text[pos]; // glyph of the element before cursor
|
|
||||||
Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
|
|
||||||
|
|
||||||
// if the cursor is not on the previous rendered virtual rect we need to update it
|
|
||||||
if(!virtualRect.contains(glyphRect.topLeft()) || !virtualRect.contains(glyphRect.bottomRight())) {
|
|
||||||
// calculate where is the first glyph visible
|
|
||||||
Point startGlyphPos;
|
|
||||||
startGlyphPos.y = std::max(glyphRect.bottom() - virtualRect.height(), 0);
|
|
||||||
startGlyphPos.x = std::max(glyphRect.right() - virtualRect.width(), 0);
|
|
||||||
|
|
||||||
// find that glyph
|
|
||||||
for(pos = 0; pos < textLength; ++pos) {
|
|
||||||
glyph = (uchar)m_text[pos];
|
|
||||||
glyphRect = Rect(glyphsPositions[pos], glyphsSize[glyph]);
|
|
||||||
glyphRect.setTop(std::max(glyphRect.top() - m_font->getTopMargin() - m_font->getGlyphSpacing().height(), 0));
|
|
||||||
glyphRect.setLeft(std::max(glyphRect.left() - m_font->getGlyphSpacing().width(), 0));
|
|
||||||
|
|
||||||
// first glyph entirely visible found
|
|
||||||
if(glyphRect.topLeft() >= startGlyphPos) {
|
|
||||||
m_startInternalPos.x = glyphsPositions[pos].x;
|
|
||||||
m_startInternalPos.y = glyphsPositions[pos].y - m_font->getTopMargin();
|
|
||||||
m_startRenderPos = pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_startInternalPos = Point(0,0);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_drawArea = m_screenCoords;
|
|
||||||
|
|
||||||
if(m_align & AlignBottom) {
|
|
||||||
m_drawArea.translate(0, m_screenCoords.height() - textBoxSize.height());
|
|
||||||
} else if(m_align & AlignVerticalCenter) {
|
|
||||||
m_drawArea.translate(0, (m_screenCoords.height() - textBoxSize.height()) / 2);
|
|
||||||
} else { // AlignTop
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_align & AlignRight) {
|
|
||||||
m_drawArea.translate(m_screenCoords.width() - textBoxSize.width(), 0);
|
|
||||||
} else if(m_align & AlignHorizontalCenter) {
|
|
||||||
m_drawArea.translate((m_screenCoords.width() - textBoxSize.width()) / 2, 0);
|
|
||||||
} else { // AlignLeft
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i < textLength; ++i) {
|
|
||||||
glyph = (uchar)m_text[i];
|
|
||||||
m_glyphsCoords[i].clear();
|
|
||||||
|
|
||||||
// skip invalid glyphs
|
|
||||||
if(glyph < 32)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// calculate initial glyph rect and texture coords
|
|
||||||
Rect glyphScreenCoords(glyphsPositions[i], glyphsSize[glyph]);
|
|
||||||
Rect glyphTextureCoords = glyphsTextureCoords[glyph];
|
|
||||||
|
|
||||||
// first translate to align position
|
|
||||||
if(m_align & AlignBottom) {
|
|
||||||
glyphScreenCoords.translate(0, m_screenCoords.height() - textBoxSize.height());
|
|
||||||
} else if(m_align & AlignVerticalCenter) {
|
|
||||||
glyphScreenCoords.translate(0, (m_screenCoords.height() - textBoxSize.height()) / 2);
|
|
||||||
} else { // AlignTop
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
if(m_align & AlignRight) {
|
|
||||||
glyphScreenCoords.translate(m_screenCoords.width() - textBoxSize.width(), 0);
|
|
||||||
} else if(m_align & AlignHorizontalCenter) {
|
|
||||||
glyphScreenCoords.translate((m_screenCoords.width() - textBoxSize.width()) / 2, 0);
|
|
||||||
} else { // AlignLeft
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
// only render glyphs that are after startRenderPosition
|
|
||||||
if(glyphScreenCoords.bottom() < m_startInternalPos.y || glyphScreenCoords.right() < m_startInternalPos.x)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// bound glyph topLeft to startRenderPosition
|
|
||||||
if(glyphScreenCoords.top() < m_startInternalPos.y) {
|
|
||||||
glyphTextureCoords.setTop(glyphTextureCoords.top() + (m_startInternalPos.y - glyphScreenCoords.top()));
|
|
||||||
glyphScreenCoords.setTop(m_startInternalPos.y);
|
|
||||||
}
|
|
||||||
if(glyphScreenCoords.left() < m_startInternalPos.x) {
|
|
||||||
glyphTextureCoords.setLeft(glyphTextureCoords.left() + (m_startInternalPos.x - glyphScreenCoords.left()));
|
|
||||||
glyphScreenCoords.setLeft(m_startInternalPos.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// subtract startInternalPos
|
|
||||||
glyphScreenCoords.translate(-m_startInternalPos);
|
|
||||||
|
|
||||||
// translate rect to screen coords
|
|
||||||
glyphScreenCoords.translate(m_screenCoords.topLeft());
|
|
||||||
|
|
||||||
// only render if glyph rect is visible on screenCoords
|
|
||||||
if(!m_screenCoords.intersects(glyphScreenCoords))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// bound glyph bottomRight to screenCoords bottomRight
|
|
||||||
if(glyphScreenCoords.bottom() > m_screenCoords.bottom()) {
|
|
||||||
glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (m_screenCoords.bottom() - glyphScreenCoords.bottom()));
|
|
||||||
glyphScreenCoords.setBottom(m_screenCoords.bottom());
|
|
||||||
}
|
|
||||||
if(glyphScreenCoords.right() > m_screenCoords.right()) {
|
|
||||||
glyphTextureCoords.setRight(glyphTextureCoords.right() + (m_screenCoords.right() - glyphScreenCoords.right()));
|
|
||||||
glyphScreenCoords.setRight(m_screenCoords.right());
|
|
||||||
}
|
|
||||||
|
|
||||||
// render glyph
|
|
||||||
m_glyphsCoords[i] = glyphScreenCoords;
|
|
||||||
m_glyphsTexCoords[i] = glyphTextureCoords;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::setFont(FontPtr font)
|
|
||||||
{
|
|
||||||
if(m_font != font) {
|
|
||||||
m_font = font;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::setText(const std::string& text)
|
|
||||||
{
|
|
||||||
if(m_text != text) {
|
|
||||||
m_text = text;
|
|
||||||
if(m_cursorPos >= 0) {
|
|
||||||
m_cursorPos = 0;
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
}
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::setScreenCoords(const Rect& screenCoords)
|
|
||||||
{
|
|
||||||
if(screenCoords != m_screenCoords) {
|
|
||||||
m_screenCoords = screenCoords;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::setAlign(AlignmentFlag align)
|
|
||||||
{
|
|
||||||
if(m_align != align) {
|
|
||||||
m_align = align;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::setCursorPos(int pos)
|
|
||||||
{
|
|
||||||
if(pos != m_cursorPos) {
|
|
||||||
if(pos < 0)
|
|
||||||
m_cursorPos = 0;
|
|
||||||
else if((uint)pos >= m_text.length())
|
|
||||||
m_cursorPos = m_text.length();
|
|
||||||
else
|
|
||||||
m_cursorPos = pos;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::enableCursor(bool enable)
|
|
||||||
{
|
|
||||||
if(enable) {
|
|
||||||
m_cursorPos = 0;
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
} else
|
|
||||||
m_cursorPos = -1;
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::appendCharacter(char c)
|
|
||||||
{
|
|
||||||
if(m_cursorPos >= 0) {
|
|
||||||
std::string tmp;
|
|
||||||
tmp = c;
|
|
||||||
m_text.insert(m_cursorPos, tmp);
|
|
||||||
m_cursorPos++;
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::removeCharacter(bool right)
|
|
||||||
{
|
|
||||||
if(m_cursorPos >= 0 && m_text.length() > 0) {
|
|
||||||
if(right && (uint)m_cursorPos < m_text.length())
|
|
||||||
m_text.erase(m_text.begin() + m_cursorPos);
|
|
||||||
else if((uint)m_cursorPos == m_text.length()) {
|
|
||||||
m_text.erase(m_text.begin() + (--m_cursorPos));
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
}
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextArea::moveCursor(bool right)
|
|
||||||
{
|
|
||||||
if(right) {
|
|
||||||
if((uint)m_cursorPos+1 <= m_text.length()) {
|
|
||||||
m_cursorPos++;
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(m_cursorPos-1 >= 0) {
|
|
||||||
m_cursorPos--;
|
|
||||||
m_cursorTicks = g_platform.getTicks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
recalculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
int TextArea::getTextPos(Point pos)
|
|
||||||
{
|
|
||||||
int textLength = m_text.length();
|
|
||||||
|
|
||||||
// find any glyph that is actually on the
|
|
||||||
int candidatePos = -1;
|
|
||||||
for(int i=0;i<textLength;++i) {
|
|
||||||
Rect clickGlyphRect = m_glyphsCoords[i];
|
|
||||||
clickGlyphRect.addTop(m_font->getTopMargin() + m_font->getGlyphSpacing().height());
|
|
||||||
clickGlyphRect.addLeft(m_font->getGlyphSpacing().width()+1);
|
|
||||||
if(clickGlyphRect.contains(pos))
|
|
||||||
return i;
|
|
||||||
else if(pos.y >= clickGlyphRect.top() && pos.y <= clickGlyphRect.bottom()) {
|
|
||||||
if(pos.x <= clickGlyphRect.left())
|
|
||||||
candidatePos = i;
|
|
||||||
else if(pos.x >= clickGlyphRect.right())
|
|
||||||
candidatePos = i+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return candidatePos;
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
#ifndef TEXTAREA_H
|
|
||||||
#define TEXTAREA_H
|
|
||||||
|
|
||||||
#include "graphicsdeclarations.h"
|
|
||||||
|
|
||||||
class TextArea
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextArea();
|
|
||||||
TextArea(FontPtr font,
|
|
||||||
const std::string& text,
|
|
||||||
const Rect& screenCoords,
|
|
||||||
AlignmentFlag align = AlignTopLeft,
|
|
||||||
const Color& color = Color::white);
|
|
||||||
|
|
||||||
void draw();
|
|
||||||
|
|
||||||
void setFont(FontPtr font);
|
|
||||||
void setText(const std::string& text);
|
|
||||||
void setScreenCoords(const Rect& screenCoords);
|
|
||||||
void setAlign(AlignmentFlag align);
|
|
||||||
void setColor(const Color& color) { m_color = color; }
|
|
||||||
void setCursorPos(int pos);
|
|
||||||
void enableCursor(bool enable = true);
|
|
||||||
void setCursorVisible(bool visible = true) { m_cursorVisible = visible; }
|
|
||||||
|
|
||||||
void moveCursor(bool right);
|
|
||||||
void appendCharacter(char c);
|
|
||||||
void removeCharacter(bool right);
|
|
||||||
|
|
||||||
std::string getText() const { return m_text; }
|
|
||||||
|
|
||||||
FontPtr getFont() const { return m_font; }
|
|
||||||
int getTextPos(Point pos);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void recalculate();
|
|
||||||
|
|
||||||
FontPtr m_font;
|
|
||||||
std::string m_text;
|
|
||||||
Rect m_screenCoords;
|
|
||||||
Rect m_drawArea;
|
|
||||||
AlignmentFlag m_align;
|
|
||||||
Color m_color;
|
|
||||||
int m_cursorPos;
|
|
||||||
Point m_startInternalPos;
|
|
||||||
int m_startRenderPos;
|
|
||||||
int m_cursorTicks;
|
|
||||||
bool m_cursorVisible;
|
|
||||||
|
|
||||||
std::vector<Rect> m_glyphsCoords;
|
|
||||||
std::vector<Rect> m_glyphsTexCoords;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -29,6 +29,7 @@ void LuaInterface::registerFunctions()
|
||||||
g_lua.bindClassMemberFunction<UIWidget>("addAnchor", &UIWidget::addAnchor);
|
g_lua.bindClassMemberFunction<UIWidget>("addAnchor", &UIWidget::addAnchor);
|
||||||
g_lua.bindClassMemberFunction<UIWidget>("getChild", &UIWidget::getChildById);
|
g_lua.bindClassMemberFunction<UIWidget>("getChild", &UIWidget::getChildById);
|
||||||
g_lua.bindClassMemberFunction<UIWidget>("addChild", &UIWidget::addChild);
|
g_lua.bindClassMemberFunction<UIWidget>("addChild", &UIWidget::addChild);
|
||||||
|
g_lua.bindClassMemberFunction<UIWidget>("lock", &UIWidget::lock);
|
||||||
|
|
||||||
// UILabel
|
// UILabel
|
||||||
g_lua.registerClass<UILabel, UIWidget>();
|
g_lua.registerClass<UILabel, UIWidget>();
|
||||||
|
|
|
@ -53,6 +53,7 @@ private:
|
||||||
|
|
||||||
template<typename... T>
|
template<typename... T>
|
||||||
int LuaObject::callLuaField(const std::string& field, const T&... args) {
|
int LuaObject::callLuaField(const std::string& field, const T&... args) {
|
||||||
|
if(m_fieldsTableRef != -1) {
|
||||||
// note that the field must be retrieved from this object lua value
|
// note that the field must be retrieved from this object lua value
|
||||||
// to force using the __index metamethod of it's metatable
|
// to force using the __index metamethod of it's metatable
|
||||||
// so cannot use LuaObject::getField here
|
// so cannot use LuaObject::getField here
|
||||||
|
@ -64,6 +65,8 @@ int LuaObject::callLuaField(const std::string& field, const T&... args) {
|
||||||
g_lua.insert(-2);
|
g_lua.insert(-2);
|
||||||
g_lua.polymorphicPush(args...);
|
g_lua.polymorphicPush(args...);
|
||||||
return g_lua.protectedCall(1 + sizeof...(args));
|
return g_lua.protectedCall(1 + sizeof...(args));
|
||||||
|
} else
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -6,8 +6,9 @@ UIAnchor::UIAnchor(const UIWidgetPtr& anchoredWidget, AnchorPoint anchoredEdge,
|
||||||
}
|
}
|
||||||
|
|
||||||
UIWidgetPtr UIAnchor::getAnchorLineWidget() const {
|
UIWidgetPtr UIAnchor::getAnchorLineWidget() const {
|
||||||
if(!m_anchoredWidget.expired() && !m_anchoredWidget.lock()->isDestroyed())
|
UIWidgetPtr anchoredWidget = m_anchoredWidget.lock();
|
||||||
return m_anchoredWidget.lock()->backwardsGetWidgetById(m_anchorLine.widgetId);
|
if(anchoredWidget && !anchoredWidget->isDestroyed())
|
||||||
|
return anchoredWidget->backwardsGetWidgetById(m_anchorLine.widgetId);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ void UIAnchorLayout::updateWidget(const UIWidgetPtr& widget)
|
||||||
bool verticalMoved = false;
|
bool verticalMoved = false;
|
||||||
bool horizontalMoved = false;
|
bool horizontalMoved = false;
|
||||||
|
|
||||||
// TODO: remove expired anchors
|
// FIXME: release expired anchors
|
||||||
for(const UIAnchor& anchor : m_anchors) {
|
for(const UIAnchor& anchor : m_anchors) {
|
||||||
if(anchor.getAnchoredWidget() == widget && anchor.getAnchorLineWidget()) {
|
if(anchor.getAnchoredWidget() == widget && anchor.getAnchorLineWidget()) {
|
||||||
int point = anchor.getAnchorLinePoint();
|
int point = anchor.getAnchorLinePoint();
|
||||||
|
|
|
@ -66,25 +66,28 @@ void UIButton::render()
|
||||||
getFont()->renderText(m_text, textRect, AlignCenter, currentStyle.color);
|
getFont()->renderText(m_text, textRect, AlignCenter, currentStyle.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIButton::onHoverChange(bool hovered)
|
void UIButton::onHoverChange(UIHoverEvent& event)
|
||||||
{
|
{
|
||||||
if(hovered && m_state == ButtonUp)
|
if(event.mouseEnter() && m_state == ButtonUp)
|
||||||
m_state = ButtonHover;
|
m_state = ButtonHover;
|
||||||
else if(m_state == ButtonHover)
|
else if(m_state == ButtonHover)
|
||||||
m_state = ButtonUp;
|
m_state = ButtonUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIButton::onMousePress(const UIMouseEvent& event)
|
void UIButton::onMousePress(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
if(event.button == MouseLeftButton)
|
if(event.button() == MouseLeftButton) {
|
||||||
m_state = ButtonDown;
|
m_state = ButtonDown;
|
||||||
|
} else
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIButton::onMouseRelease(const UIMouseEvent& event)
|
void UIButton::onMouseRelease(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
if(m_state == ButtonDown) {
|
if(m_state == ButtonDown) {
|
||||||
if(m_onClick && getGeometry().contains(event.mousePos))
|
if(m_onClick && getGeometry().contains(event.pos()))
|
||||||
m_onClick();
|
m_onClick();
|
||||||
m_state = isHovered() ? ButtonHover : ButtonUp;
|
m_state = (isHovered() && isEnabled()) ? ButtonHover : ButtonUp;
|
||||||
}
|
} else
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,9 @@ public:
|
||||||
UIButtonPtr asUIButton() { return std::static_pointer_cast<UIButton>(shared_from_this()); }
|
UIButtonPtr asUIButton() { return std::static_pointer_cast<UIButton>(shared_from_this()); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onHoverChange(bool hovered);
|
virtual void onHoverChange(UIHoverEvent& event);
|
||||||
virtual void onMousePress(const UIMouseEvent& event);
|
virtual void onMousePress(UIMouseEvent& event);
|
||||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
virtual void onMouseRelease(UIMouseEvent& event);
|
||||||
|
|
||||||
ButtonState m_state;
|
ButtonState m_state;
|
||||||
ButtonStateStyle m_statesStyle[3];
|
ButtonStateStyle m_statesStyle[3];
|
||||||
|
|
|
@ -33,4 +33,37 @@ enum UIWidgetType {
|
||||||
UITypeWindow
|
UITypeWindow
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 ButtonState {
|
||||||
|
ButtonUp = 0,
|
||||||
|
ButtonDown,
|
||||||
|
ButtonHover
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,18 +1,113 @@
|
||||||
#ifndef UIEVENT_H
|
#ifndef UIEVENT_H
|
||||||
#define UIEVENT_H
|
#define UIEVENT_H
|
||||||
|
|
||||||
#include <global.h>
|
#include <core/inputevent.h>
|
||||||
|
#include "uideclarations.h"
|
||||||
|
|
||||||
struct UIMouseEvent {
|
class UIEvent
|
||||||
Point mousePos;
|
{
|
||||||
Point mouseMoved;
|
public:
|
||||||
MouseButton button;
|
UIEvent() : m_accepted(true) { }
|
||||||
MouseWheelDirection wheelDirection;
|
|
||||||
|
void setAccepted(bool accepted) { m_accepted = accepted; }
|
||||||
|
void accept() { m_accepted = true; }
|
||||||
|
void ignore() { m_accepted = false; }
|
||||||
|
|
||||||
|
bool isAccepted() { return m_accepted; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_accepted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UIKeyEvent {
|
class UIMouseEvent : public UIEvent
|
||||||
uchar keycode;
|
{
|
||||||
int keyboardModifiers;
|
public:
|
||||||
|
UIMouseEvent(const Point& pos, MouseButton button)
|
||||||
|
: m_pos(pos), m_button(button), m_wheelDirection(MouseNoWheel) { }
|
||||||
|
UIMouseEvent(const Point& pos, MouseWheelDirection wheelDirection)
|
||||||
|
: m_pos(pos), m_button(MouseNoButton), m_wheelDirection(wheelDirection) { }
|
||||||
|
UIMouseEvent(const Point& pos, Point moved)
|
||||||
|
: m_pos(pos), m_moved(moved), m_button(MouseNoButton), m_wheelDirection(MouseNoWheel) { }
|
||||||
|
|
||||||
|
Point pos() const { return m_pos; }
|
||||||
|
Point oldPos() const { return m_pos - m_moved; }
|
||||||
|
Point moved() const { return m_moved; }
|
||||||
|
int x() const { return m_pos.x; }
|
||||||
|
int y() const { return m_pos.y; }
|
||||||
|
MouseButton button() const { return m_button; }
|
||||||
|
MouseWheelDirection wheelDirection() const { return m_wheelDirection; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Point m_pos;
|
||||||
|
Point m_moved;
|
||||||
|
MouseButton m_button;
|
||||||
|
MouseWheelDirection m_wheelDirection;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UIKeyEvent : public UIEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UIKeyEvent(uchar keyCode, char keyChar, int keyboardModifiers)
|
||||||
|
: m_keyCode(keyCode), m_keyChar(keyChar), m_keyboardModifiers(keyboardModifiers) { }
|
||||||
|
|
||||||
|
uchar keyCode() const { return m_keyCode; }
|
||||||
|
char keyChar() const { return m_keyChar; }
|
||||||
|
int keyboardModifiers() const { return m_keyboardModifiers; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
uchar m_keyCode;
|
||||||
|
char m_keyChar;
|
||||||
|
int m_keyboardModifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UIFocusEvent : public UIEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UIFocusEvent(FocusReason reason, bool gotFocus)
|
||||||
|
: m_reason(reason), m_gotFocus(gotFocus) { }
|
||||||
|
|
||||||
|
bool gotFocus() const { return m_gotFocus; }
|
||||||
|
bool lostFocus() const { return !m_gotFocus; }
|
||||||
|
bool reason() const { return m_reason; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FocusReason m_reason;
|
||||||
|
bool m_gotFocus;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UIHoverEvent : public UIEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UIHoverEvent(bool hovered) : m_hovered(hovered) { }
|
||||||
|
|
||||||
|
bool hovered() const { return m_hovered; }
|
||||||
|
bool mouseEnter() const { return m_hovered; }
|
||||||
|
bool mouseLeave() const { return !m_hovered; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_hovered;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UIGeometryUpdateEvent : public UIEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UIGeometryUpdateEvent(const Rect& oldRect, const Rect& rect)
|
||||||
|
: m_oldRect(oldRect), m_rect(rect) { }
|
||||||
|
|
||||||
|
Point pos() const { return m_rect.topLeft(); }
|
||||||
|
Size size() const { return m_rect.size(); }
|
||||||
|
Rect rect() const { return m_rect; }
|
||||||
|
Point oldPos() const { return m_oldRect.topLeft(); }
|
||||||
|
Size oldSize() const { return m_oldRect.size(); }
|
||||||
|
Rect oldRect() const { return m_oldRect; }
|
||||||
|
Point moved() const { return m_rect.topLeft() - m_oldRect.topLeft(); }
|
||||||
|
Size resized() const { return m_rect.size() - m_oldRect.size(); }
|
||||||
|
bool hasMoved() const { return m_rect.topLeft() != m_oldRect.topLeft(); }
|
||||||
|
bool hasResized() const { return m_rect.size() != m_oldRect.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Rect m_oldRect;
|
||||||
|
Rect m_rect;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
#include "uilineedit.h"
|
#include "uilineedit.h"
|
||||||
#include <graphics/font.h>
|
#include <graphics/font.h>
|
||||||
|
#include <graphics/graphics.h>
|
||||||
|
#include <core/platform.h>
|
||||||
#include <otml/otmlnode.h>
|
#include <otml/otmlnode.h>
|
||||||
|
|
||||||
UILineEdit::UILineEdit() : UIWidget(UITypeLabel)
|
UILineEdit::UILineEdit() : UIWidget(UITypeLabel)
|
||||||
{
|
{
|
||||||
|
m_align = AlignLeftCenter;
|
||||||
|
m_cursorPos = 0;
|
||||||
|
m_startRenderPos = 0;
|
||||||
|
m_textHorizontalMargin = 3;
|
||||||
|
m_focusable = true;
|
||||||
|
blinkCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
UILineEditPtr UILineEdit::create()
|
UILineEditPtr UILineEdit::create()
|
||||||
|
@ -17,18 +25,357 @@ void UILineEdit::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||||
{
|
{
|
||||||
UIWidget::loadStyleFromOTML(styleNode);
|
UIWidget::loadStyleFromOTML(styleNode);
|
||||||
|
|
||||||
m_textArea.setFont(getFont());
|
|
||||||
m_textArea.setColor(getColor());
|
|
||||||
setText(styleNode->readAt("text", getText()));
|
setText(styleNode->readAt("text", getText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UILineEdit::render()
|
void UILineEdit::render()
|
||||||
{
|
{
|
||||||
UIWidget::render();
|
UIWidget::render();
|
||||||
m_textArea.draw();
|
|
||||||
|
//TODO: text rendering could be much optimized by using vertex buffer or caching the render into a texture
|
||||||
|
|
||||||
|
int textLength = m_text.length();
|
||||||
|
const TexturePtr& texture = m_font->getTexture();
|
||||||
|
for(int i=0;i<textLength;++i) {
|
||||||
|
g_graphics.drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i], m_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UILineEdit::onGeometryUpdate()
|
// render cursor
|
||||||
{
|
if(isExplicitlyEnabled() && hasFocus() && m_cursorPos >= 0) {
|
||||||
m_textArea.setScreenCoords(getGeometry());
|
assert(m_cursorPos <= textLength);
|
||||||
|
// draw every 330ms
|
||||||
|
const int delay = 330;
|
||||||
|
int ticks = g_platform.getTicks();
|
||||||
|
if(ticks - m_cursorTicks <= delay) {
|
||||||
|
Rect cursorRect;
|
||||||
|
// when cursor is at 0 or is the first visible element
|
||||||
|
if(m_cursorPos == 0 || m_cursorPos == m_startRenderPos)
|
||||||
|
cursorRect = Rect(m_drawArea.left()-1, m_drawArea.top(), 1, m_font->getGlyphHeight());
|
||||||
|
else
|
||||||
|
cursorRect = Rect(m_glyphsCoords[m_cursorPos-1].right(), m_glyphsCoords[m_cursorPos-1].top(), 1, m_font->getGlyphHeight());
|
||||||
|
g_graphics.drawFilledRect(cursorRect, m_color);
|
||||||
|
} else if(ticks - m_cursorTicks >= 2*delay) {
|
||||||
|
m_cursorTicks = ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::update()
|
||||||
|
{
|
||||||
|
int textLength = m_text.length();
|
||||||
|
|
||||||
|
// prevent glitches
|
||||||
|
if(!m_rect.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// map glyphs positions
|
||||||
|
Size textBoxSize;
|
||||||
|
const std::vector<Point>& glyphsPositions = m_font->calculateGlyphsPositions(m_text, m_align, &textBoxSize);
|
||||||
|
const Rect *glyphsTextureCoords = m_font->getGlyphsTextureCoords();
|
||||||
|
const Size *glyphsSize = m_font->getGlyphsSize();
|
||||||
|
int glyph;
|
||||||
|
|
||||||
|
// resize just on demand
|
||||||
|
if(textLength > (int)m_glyphsCoords.size()) {
|
||||||
|
m_glyphsCoords.resize(textLength);
|
||||||
|
m_glyphsTexCoords.resize(textLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// readjust start view area based on cursor position
|
||||||
|
if(m_cursorPos >= 0 && textLength > 0) {
|
||||||
|
assert(m_cursorPos <= textLength);
|
||||||
|
if(m_cursorPos < m_startRenderPos) // cursor is before the previuos first rendered glyph, so we need to update
|
||||||
|
{
|
||||||
|
m_startInternalPos.x = glyphsPositions[m_cursorPos].x;
|
||||||
|
m_startInternalPos.y = glyphsPositions[m_cursorPos].y - m_font->getTopMargin();
|
||||||
|
m_startRenderPos = m_cursorPos;
|
||||||
|
} else if(m_cursorPos > m_startRenderPos || // cursor is after the previuos first rendered glyph
|
||||||
|
(m_cursorPos == m_startRenderPos && textLength == m_cursorPos)) // cursor is at the previuos rendered element, and is the last text element
|
||||||
|
{
|
||||||
|
Rect virtualRect(m_startInternalPos, m_rect.size() - Size(2*m_textHorizontalMargin, 0) ); // previous rendered virtual rect
|
||||||
|
int pos = m_cursorPos - 1; // element before cursor
|
||||||
|
glyph = (uchar)m_text[pos]; // glyph of the element before cursor
|
||||||
|
Rect glyphRect(glyphsPositions[pos], glyphsSize[glyph]);
|
||||||
|
|
||||||
|
// if the cursor is not on the previous rendered virtual rect we need to update it
|
||||||
|
if(!virtualRect.contains(glyphRect.topLeft()) || !virtualRect.contains(glyphRect.bottomRight())) {
|
||||||
|
// calculate where is the first glyph visible
|
||||||
|
Point startGlyphPos;
|
||||||
|
startGlyphPos.y = std::max(glyphRect.bottom() - virtualRect.height(), 0);
|
||||||
|
startGlyphPos.x = std::max(glyphRect.right() - virtualRect.width(), 0);
|
||||||
|
|
||||||
|
// find that glyph
|
||||||
|
for(pos = 0; pos < textLength; ++pos) {
|
||||||
|
glyph = (uchar)m_text[pos];
|
||||||
|
glyphRect = Rect(glyphsPositions[pos], glyphsSize[glyph]);
|
||||||
|
glyphRect.setTop(std::max(glyphRect.top() - m_font->getTopMargin() - m_font->getGlyphSpacing().height(), 0));
|
||||||
|
glyphRect.setLeft(std::max(glyphRect.left() - m_font->getGlyphSpacing().width(), 0));
|
||||||
|
|
||||||
|
// first glyph entirely visible found
|
||||||
|
if(glyphRect.topLeft() >= startGlyphPos) {
|
||||||
|
m_startInternalPos.x = glyphsPositions[pos].x;
|
||||||
|
m_startInternalPos.y = glyphsPositions[pos].y - m_font->getTopMargin();
|
||||||
|
m_startRenderPos = pos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_startInternalPos = Point(0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect textScreenCoords = m_rect;
|
||||||
|
textScreenCoords.addLeft(-m_textHorizontalMargin);
|
||||||
|
textScreenCoords.addRight(-m_textHorizontalMargin);
|
||||||
|
m_drawArea = textScreenCoords;
|
||||||
|
|
||||||
|
if(m_align & AlignBottom) {
|
||||||
|
m_drawArea.translate(0, textScreenCoords.height() - textBoxSize.height());
|
||||||
|
} else if(m_align & AlignVerticalCenter) {
|
||||||
|
m_drawArea.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2);
|
||||||
|
} else { // AlignTop
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_align & AlignRight) {
|
||||||
|
m_drawArea.translate(textScreenCoords.width() - textBoxSize.width(), 0);
|
||||||
|
} else if(m_align & AlignHorizontalCenter) {
|
||||||
|
m_drawArea.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0);
|
||||||
|
} else { // AlignLeft
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < textLength; ++i) {
|
||||||
|
glyph = (uchar)m_text[i];
|
||||||
|
m_glyphsCoords[i].clear();
|
||||||
|
|
||||||
|
// skip invalid glyphs
|
||||||
|
if(glyph < 32)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// calculate initial glyph rect and texture coords
|
||||||
|
Rect glyphScreenCoords(glyphsPositions[i], glyphsSize[glyph]);
|
||||||
|
Rect glyphTextureCoords = glyphsTextureCoords[glyph];
|
||||||
|
|
||||||
|
// first translate to align position
|
||||||
|
if(m_align & AlignBottom) {
|
||||||
|
glyphScreenCoords.translate(0, textScreenCoords.height() - textBoxSize.height());
|
||||||
|
} else if(m_align & AlignVerticalCenter) {
|
||||||
|
glyphScreenCoords.translate(0, (textScreenCoords.height() - textBoxSize.height()) / 2);
|
||||||
|
} else { // AlignTop
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
if(m_align & AlignRight) {
|
||||||
|
glyphScreenCoords.translate(textScreenCoords.width() - textBoxSize.width(), 0);
|
||||||
|
} else if(m_align & AlignHorizontalCenter) {
|
||||||
|
glyphScreenCoords.translate((textScreenCoords.width() - textBoxSize.width()) / 2, 0);
|
||||||
|
} else { // AlignLeft
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
// only render glyphs that are after startRenderPosition
|
||||||
|
if(glyphScreenCoords.bottom() < m_startInternalPos.y || glyphScreenCoords.right() < m_startInternalPos.x)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// bound glyph topLeft to startRenderPosition
|
||||||
|
if(glyphScreenCoords.top() < m_startInternalPos.y) {
|
||||||
|
glyphTextureCoords.setTop(glyphTextureCoords.top() + (m_startInternalPos.y - glyphScreenCoords.top()));
|
||||||
|
glyphScreenCoords.setTop(m_startInternalPos.y);
|
||||||
|
}
|
||||||
|
if(glyphScreenCoords.left() < m_startInternalPos.x) {
|
||||||
|
glyphTextureCoords.setLeft(glyphTextureCoords.left() + (m_startInternalPos.x - glyphScreenCoords.left()));
|
||||||
|
glyphScreenCoords.setLeft(m_startInternalPos.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract startInternalPos
|
||||||
|
glyphScreenCoords.translate(-m_startInternalPos);
|
||||||
|
|
||||||
|
// translate rect to screen coords
|
||||||
|
glyphScreenCoords.translate(textScreenCoords.topLeft());
|
||||||
|
|
||||||
|
// only render if glyph rect is visible on screenCoords
|
||||||
|
if(!textScreenCoords.intersects(glyphScreenCoords))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// bound glyph bottomRight to screenCoords bottomRight
|
||||||
|
if(glyphScreenCoords.bottom() > textScreenCoords.bottom()) {
|
||||||
|
glyphTextureCoords.setBottom(glyphTextureCoords.bottom() + (textScreenCoords.bottom() - glyphScreenCoords.bottom()));
|
||||||
|
glyphScreenCoords.setBottom(textScreenCoords.bottom());
|
||||||
|
}
|
||||||
|
if(glyphScreenCoords.right() > textScreenCoords.right()) {
|
||||||
|
glyphTextureCoords.setRight(glyphTextureCoords.right() + (textScreenCoords.right() - glyphScreenCoords.right()));
|
||||||
|
glyphScreenCoords.setRight(textScreenCoords.right());
|
||||||
|
}
|
||||||
|
|
||||||
|
// render glyph
|
||||||
|
m_glyphsCoords[i] = glyphScreenCoords;
|
||||||
|
m_glyphsTexCoords[i] = glyphTextureCoords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::setFont(const FontPtr& font)
|
||||||
|
{
|
||||||
|
if(m_font != font) {
|
||||||
|
m_font = font;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::setText(const std::string& text)
|
||||||
|
{
|
||||||
|
if(m_text != text) {
|
||||||
|
m_text = text;
|
||||||
|
if(m_cursorPos >= 0) {
|
||||||
|
m_cursorPos = 0;
|
||||||
|
blinkCursor();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::setAlign(AlignmentFlag align)
|
||||||
|
{
|
||||||
|
if(m_align != align) {
|
||||||
|
m_align = align;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::setCursorPos(int pos)
|
||||||
|
{
|
||||||
|
if(pos != m_cursorPos) {
|
||||||
|
if(pos < 0)
|
||||||
|
m_cursorPos = 0;
|
||||||
|
else if((uint)pos >= m_text.length())
|
||||||
|
m_cursorPos = m_text.length();
|
||||||
|
else
|
||||||
|
m_cursorPos = pos;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::enableCursor(bool enable)
|
||||||
|
{
|
||||||
|
if(enable) {
|
||||||
|
m_cursorPos = 0;
|
||||||
|
blinkCursor();
|
||||||
|
} else
|
||||||
|
m_cursorPos = -1;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::appendCharacter(char c)
|
||||||
|
{
|
||||||
|
if(m_cursorPos >= 0) {
|
||||||
|
std::string tmp;
|
||||||
|
tmp = c;
|
||||||
|
m_text.insert(m_cursorPos, tmp);
|
||||||
|
m_cursorPos++;
|
||||||
|
blinkCursor();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::removeCharacter(bool right)
|
||||||
|
{
|
||||||
|
if(m_cursorPos >= 0 && m_text.length() > 0) {
|
||||||
|
if(right && (uint)m_cursorPos < m_text.length())
|
||||||
|
m_text.erase(m_text.begin() + m_cursorPos);
|
||||||
|
else if((uint)m_cursorPos == m_text.length()) {
|
||||||
|
m_text.erase(m_text.begin() + (--m_cursorPos));
|
||||||
|
blinkCursor();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::moveCursor(bool right)
|
||||||
|
{
|
||||||
|
if(right) {
|
||||||
|
if((uint)m_cursorPos+1 <= m_text.length()) {
|
||||||
|
m_cursorPos++;
|
||||||
|
blinkCursor();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(m_cursorPos-1 >= 0) {
|
||||||
|
m_cursorPos--;
|
||||||
|
blinkCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int UILineEdit::getTextPos(Point pos)
|
||||||
|
{
|
||||||
|
int textLength = m_text.length();
|
||||||
|
|
||||||
|
// find any glyph that is actually on the
|
||||||
|
int candidatePos = -1;
|
||||||
|
for(int i=0;i<textLength;++i) {
|
||||||
|
Rect clickGlyphRect = m_glyphsCoords[i];
|
||||||
|
clickGlyphRect.addTop(m_font->getTopMargin() + m_font->getGlyphSpacing().height());
|
||||||
|
clickGlyphRect.addLeft(m_font->getGlyphSpacing().width()+1);
|
||||||
|
if(clickGlyphRect.contains(pos))
|
||||||
|
return i;
|
||||||
|
else if(pos.y >= clickGlyphRect.top() && pos.y <= clickGlyphRect.bottom()) {
|
||||||
|
if(pos.x <= clickGlyphRect.left())
|
||||||
|
candidatePos = i;
|
||||||
|
else if(pos.x >= clickGlyphRect.right())
|
||||||
|
candidatePos = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidatePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::onGeometryUpdate(UIGeometryUpdateEvent& event)
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::onFocusChange(UIFocusEvent& event)
|
||||||
|
{
|
||||||
|
if(event.gotFocus()) {
|
||||||
|
if(event.reason() == TabFocusReason)
|
||||||
|
setCursorPos(0);
|
||||||
|
else
|
||||||
|
blinkCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::onKeyPress(UIKeyEvent& event)
|
||||||
|
{
|
||||||
|
if(event.keyCode() == KC_DELETE) // erase right character
|
||||||
|
removeCharacter(true);
|
||||||
|
else if(event.keyCode() == KC_BACK) // erase left character
|
||||||
|
removeCharacter(false);
|
||||||
|
else if(event.keyCode() == KC_RIGHT) // move cursor right
|
||||||
|
moveCursor(true);
|
||||||
|
else if(event.keyCode() == KC_LEFT) // move cursor left
|
||||||
|
moveCursor(false);
|
||||||
|
else if(event.keyCode() == KC_HOME) // move cursor to first character
|
||||||
|
setCursorPos(0);
|
||||||
|
else if(event.keyCode() == KC_END) // move cursor to last character
|
||||||
|
setCursorPos(m_text.length());
|
||||||
|
else if(event.keyChar() != 0) {
|
||||||
|
if(event.keyCode() != KC_TAB && event.keyCode() != KC_RETURN)
|
||||||
|
appendCharacter(event.keyChar());
|
||||||
|
else
|
||||||
|
event.ignore();
|
||||||
|
} else
|
||||||
|
event.ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::onMousePress(UIMouseEvent& event)
|
||||||
|
{
|
||||||
|
if(event.button() == MouseLeftButton) {
|
||||||
|
int pos = getTextPos(event.pos());
|
||||||
|
if(pos >= 0)
|
||||||
|
setCursorPos(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UILineEdit::blinkCursor()
|
||||||
|
{
|
||||||
|
m_cursorTicks = g_platform.getTicks();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#define UILINEEDIT_H
|
#define UILINEEDIT_H
|
||||||
|
|
||||||
#include "uiwidget.h"
|
#include "uiwidget.h"
|
||||||
#include <graphics/textarea.h>
|
|
||||||
|
|
||||||
class UILineEdit : public UIWidget
|
class UILineEdit : public UIWidget
|
||||||
{
|
{
|
||||||
|
@ -14,15 +13,41 @@ public:
|
||||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||||
virtual void render();
|
virtual void render();
|
||||||
|
|
||||||
void setText(const std::string& text) { m_textArea.setText(text); }
|
void update();
|
||||||
std::string getText() const { return m_textArea.getText(); }
|
|
||||||
|
void setText(const std::string& text);
|
||||||
|
void setAlign(AlignmentFlag align);
|
||||||
|
void setCursorPos(int pos);
|
||||||
|
void enableCursor(bool enable = true);
|
||||||
|
|
||||||
|
void moveCursor(bool right);
|
||||||
|
void appendCharacter(char c);
|
||||||
|
void removeCharacter(bool right);
|
||||||
|
|
||||||
|
void setFont(const FontPtr& font);
|
||||||
|
std::string getText() const { return m_text; }
|
||||||
|
int getTextPos(Point pos);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onGeometryUpdate();
|
virtual void onGeometryUpdate(UIGeometryUpdateEvent& event);
|
||||||
|
virtual void onFocusChange(UIFocusEvent& event);
|
||||||
|
virtual void onKeyPress(UIKeyEvent& event);
|
||||||
|
virtual void onMousePress(UIMouseEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//TODO: move textarea to here
|
void blinkCursor();
|
||||||
TextArea m_textArea;
|
|
||||||
|
std::string m_text;
|
||||||
|
Rect m_drawArea;
|
||||||
|
AlignmentFlag m_align;
|
||||||
|
int m_cursorPos;
|
||||||
|
Point m_startInternalPos;
|
||||||
|
int m_startRenderPos;
|
||||||
|
int m_cursorTicks;
|
||||||
|
int m_textHorizontalMargin;
|
||||||
|
|
||||||
|
std::vector<Rect> m_glyphsCoords;
|
||||||
|
std::vector<Rect> m_glyphsTexCoords;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,6 +12,7 @@ void UIManager::init()
|
||||||
// creates root widget
|
// creates root widget
|
||||||
m_rootWidget = UIWidgetPtr(new UIWidget);
|
m_rootWidget = UIWidgetPtr(new UIWidget);
|
||||||
m_rootWidget->setId("root");
|
m_rootWidget->setId("root");
|
||||||
|
m_rootWidget->setHovered(true);
|
||||||
|
|
||||||
UIAnchorLayoutPtr anchorLayout(new UIAnchorLayout);
|
UIAnchorLayoutPtr anchorLayout(new UIAnchorLayout);
|
||||||
m_rootWidget->setLayout(anchorLayout);
|
m_rootWidget->setLayout(anchorLayout);
|
||||||
|
@ -27,7 +28,6 @@ void UIManager::terminate()
|
||||||
|
|
||||||
void UIManager::render()
|
void UIManager::render()
|
||||||
{
|
{
|
||||||
if(m_rootWidget)
|
|
||||||
m_rootWidget->render();
|
m_rootWidget->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,47 +41,44 @@ void UIManager::inputEvent(const InputEvent& event)
|
||||||
{
|
{
|
||||||
// translate input event to ui events
|
// translate input event to ui events
|
||||||
if(m_rootWidget) {
|
if(m_rootWidget) {
|
||||||
if(event.type & EventTextEnter) {
|
if(event.type & EventKeyboardAction) {
|
||||||
m_rootWidget->onKeyboardText(std::string(1, event.keychar));
|
int keyboardModifiers = KeyboardNoModifier;
|
||||||
} else if(event.type & EventKeyboardAction) {
|
|
||||||
UIKeyEvent e;
|
|
||||||
e.keycode = event.keycode;
|
|
||||||
e.keyboardModifiers = KeyboardNoModifier;
|
|
||||||
if(event.ctrl)
|
if(event.ctrl)
|
||||||
e.keyboardModifiers |= KeyboardCtrlModifier;
|
keyboardModifiers |= KeyboardCtrlModifier;
|
||||||
if(event.shift)
|
if(event.shift)
|
||||||
e.keyboardModifiers |= KeyboardShiftModifier;
|
keyboardModifiers |= KeyboardShiftModifier;
|
||||||
if(event.alt)
|
if(event.alt)
|
||||||
e.keyboardModifiers |= KeyboardAltModifier;
|
keyboardModifiers |= KeyboardAltModifier;
|
||||||
|
|
||||||
if(event.type & EventKeyDown)
|
UIKeyEvent e(event.keycode, event.keychar, keyboardModifiers);
|
||||||
|
if(event.type == EventKeyDown)
|
||||||
m_rootWidget->onKeyPress(e);
|
m_rootWidget->onKeyPress(e);
|
||||||
else
|
else
|
||||||
m_rootWidget->onKeyRelease(e);
|
m_rootWidget->onKeyRelease(e);
|
||||||
} else if(event.type & EventMouseAction) {
|
} else if(event.type & EventMouseAction) {
|
||||||
UIMouseEvent e;
|
|
||||||
e.mouseMoved = event.mouseMoved;
|
|
||||||
e.mousePos = event.mousePos;
|
|
||||||
e.button = MouseNoButton;
|
|
||||||
e.wheelDirection = MouseNoWheel;
|
|
||||||
|
|
||||||
if(event.type == EventMouseMove) {
|
if(event.type == EventMouseMove) {
|
||||||
|
UIMouseEvent e(event.mousePos, event.mousePos);
|
||||||
m_rootWidget->onMouseMove(e);
|
m_rootWidget->onMouseMove(e);
|
||||||
}
|
}
|
||||||
else if(event.type & EventMouseWheel) {
|
else if(event.type & EventMouseWheel) {
|
||||||
|
MouseWheelDirection dir;
|
||||||
if(event.type & EventDown)
|
if(event.type & EventDown)
|
||||||
e.wheelDirection = MouseWheelDown;
|
dir = MouseWheelDown;
|
||||||
else if(event.type & EventUp)
|
else if(event.type & EventUp)
|
||||||
e.wheelDirection = MouseWheelUp;
|
dir = MouseWheelUp;
|
||||||
|
|
||||||
|
UIMouseEvent e(event.mousePos, dir);
|
||||||
m_rootWidget->onMouseWheel(e);
|
m_rootWidget->onMouseWheel(e);
|
||||||
} else {
|
} else {
|
||||||
|
MouseButton button;
|
||||||
if(event.type & EventMouseLeftButton)
|
if(event.type & EventMouseLeftButton)
|
||||||
e.button = MouseLeftButton;
|
button = MouseLeftButton;
|
||||||
else if(event.type & EventMouseMidButton)
|
else if(event.type & EventMouseMidButton)
|
||||||
e.button = MouseMidButton;
|
button = MouseMidButton;
|
||||||
else if(event.type & EventMouseRightButton)
|
else if(event.type & EventMouseRightButton)
|
||||||
e.button = MouseRightButton;
|
button = MouseRightButton;
|
||||||
|
|
||||||
|
UIMouseEvent e(event.mousePos, button);
|
||||||
if(event.type & EventDown)
|
if(event.type & EventDown)
|
||||||
m_rootWidget->onMousePress(e);
|
m_rootWidget->onMousePress(e);
|
||||||
else if(event.type & EventUp)
|
else if(event.type & EventUp)
|
||||||
|
@ -91,43 +88,6 @@ void UIManager::inputEvent(const InputEvent& event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIManager::lockWidget(const UIWidgetPtr& widgetToLock)
|
|
||||||
{
|
|
||||||
assert(m_rootWidget->hasChild(widgetToLock));
|
|
||||||
|
|
||||||
// disable all other widgets
|
|
||||||
for(const UIWidgetPtr& widget : m_rootWidget->getChildren()) {
|
|
||||||
if(widget == widgetToLock)
|
|
||||||
widget->setEnabled(true);
|
|
||||||
else
|
|
||||||
widget->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lockedWidgets.push_front(widgetToLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void UIManager::unlockWidget(const UIWidgetPtr& widgetToUnlock)
|
|
||||||
{
|
|
||||||
assert(m_rootWidget->hasChild(widgetToUnlock));
|
|
||||||
|
|
||||||
auto it = std::find(m_lockedWidgets.begin(), m_lockedWidgets.end(), widgetToUnlock);
|
|
||||||
if(it != m_lockedWidgets.end()) {
|
|
||||||
m_lockedWidgets.erase(it);
|
|
||||||
UIWidgetPtr newLockedWidget;
|
|
||||||
if(m_lockedWidgets.size() > 0)
|
|
||||||
newLockedWidget = m_lockedWidgets.front();
|
|
||||||
for(const UIWidgetPtr& child : m_rootWidget->getChildren()) {
|
|
||||||
if(newLockedWidget) {
|
|
||||||
if(child == newLockedWidget)
|
|
||||||
child->setEnabled(true);
|
|
||||||
else
|
|
||||||
child->setEnabled(false);
|
|
||||||
} else
|
|
||||||
child->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool UIManager::importStyles(const std::string& file)
|
bool UIManager::importStyles(const std::string& file)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -197,6 +157,9 @@ UIWidgetPtr UIManager::loadUI(const std::string& file)
|
||||||
widget = loadWidgetFromOTML(node);
|
widget = loadWidgetFromOTML(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// schedule onLoad events
|
||||||
|
widget->load();
|
||||||
return widget;
|
return widget;
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
logError("ERROR: failed to load ui from '", file, "':\n", e.what());
|
logError("ERROR: failed to load ui from '", file, "':\n", e.what());
|
||||||
|
|
|
@ -15,9 +15,6 @@ public:
|
||||||
void resize(const Size& size);
|
void resize(const Size& size);
|
||||||
void inputEvent(const InputEvent& event);
|
void inputEvent(const InputEvent& event);
|
||||||
|
|
||||||
void lockWidget(const UIWidgetPtr& widgetToLock);
|
|
||||||
void unlockWidget(const UIWidgetPtr& widgetToUnlock);
|
|
||||||
|
|
||||||
bool importStyles(const std::string& file);
|
bool importStyles(const std::string& file);
|
||||||
void importStyleFromOTML(const OTMLNodePtr& styleNode);
|
void importStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||||
OTMLNodePtr getStyle(const std::string& styleName);
|
OTMLNodePtr getStyle(const std::string& styleName);
|
||||||
|
@ -29,7 +26,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UIWidgetPtr m_rootWidget;
|
UIWidgetPtr m_rootWidget;
|
||||||
UIWidgetList m_lockedWidgets;
|
|
||||||
std::map<std::string, OTMLNodePtr> m_styles;
|
std::map<std::string, OTMLNodePtr> m_styles;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ void UIWidget::destroy()
|
||||||
// destroy only once
|
// destroy only once
|
||||||
if(!m_destroyed) {
|
if(!m_destroyed) {
|
||||||
// clear additional reference
|
// clear additional reference
|
||||||
|
m_lockedWidgets.clear();
|
||||||
m_focusedChild.reset();
|
m_focusedChild.reset();
|
||||||
|
|
||||||
// destroy children
|
// destroy children
|
||||||
|
@ -80,6 +81,20 @@ void UIWidget::destroyCheck()
|
||||||
logWarning("WARNING: destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left");
|
logWarning("WARNING: destroyed widget with id '",m_id,"', but it still have ",realUseCount," references left");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UIWidget::load()
|
||||||
|
{
|
||||||
|
for(const UIWidgetPtr& child : m_children)
|
||||||
|
child->load();
|
||||||
|
|
||||||
|
// schedule onLoad
|
||||||
|
UIWidgetPtr self = asUIWidget();
|
||||||
|
g_dispatcher.addEvent([self]() {
|
||||||
|
// this widget could be destroyed before the event happens
|
||||||
|
if(!self->isDestroyed())
|
||||||
|
self->callLuaField("onLoad");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void UIWidget::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
void UIWidget::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
@ -166,6 +181,10 @@ void UIWidget::loadStyleFromOTML(const OTMLNodePtr& styleNode)
|
||||||
addAnchor(myEdge, target, targetEdge);
|
addAnchor(myEdge, target, targetEdge);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(node->tag() == "onLoad") {
|
||||||
|
g_lua.loadFunction(node->read<std::string>(), "@" + node->source() + "[" + node->tag() + "]");
|
||||||
|
luaSetField("onLoad");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!m_font)
|
if(!m_font)
|
||||||
|
@ -238,43 +257,34 @@ void UIWidget::setGeometry(const Rect& rect)
|
||||||
// avoid massive updates
|
// avoid massive updates
|
||||||
if(!m_updateScheduled) {
|
if(!m_updateScheduled) {
|
||||||
UIWidgetPtr self = asUIWidget();
|
UIWidgetPtr self = asUIWidget();
|
||||||
g_dispatcher.addEvent([self]() {
|
g_dispatcher.addEvent([self, oldRect]() {
|
||||||
self->m_updateScheduled = false;
|
self->m_updateScheduled = false;
|
||||||
|
UIGeometryUpdateEvent e(oldRect, self->getGeometry());
|
||||||
// this widget could be destroyed before the event happens
|
// this widget could be destroyed before the event happens
|
||||||
if(!self->isDestroyed())
|
if(!self->isDestroyed())
|
||||||
self->onGeometryUpdate();
|
self->onGeometryUpdate(e);
|
||||||
});
|
});
|
||||||
m_updateScheduled = true;
|
m_updateScheduled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::focusChild(const UIWidgetPtr& focusedChild)
|
void UIWidget::lock()
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
if(focusedChild != m_focusedChild) {
|
UIWidgetPtr parent = getParent();
|
||||||
UIWidgetPtr oldFocused = m_focusedChild;
|
if(parent)
|
||||||
m_focusedChild = focusedChild;
|
parent->lockChild(asUIWidget());
|
||||||
|
|
||||||
if(oldFocused) {
|
|
||||||
g_dispatcher.addEvent([oldFocused]() {
|
|
||||||
// the widget can be destroyed before the event happens
|
|
||||||
if(!oldFocused->isDestroyed())
|
|
||||||
oldFocused->onFocusChange(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(focusedChild) {
|
|
||||||
g_dispatcher.addEvent([focusedChild]() {
|
|
||||||
// the widget can be destroyed before the event happens
|
|
||||||
if(!focusedChild->isDestroyed())
|
|
||||||
focusedChild->onFocusChange(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// when containers are focused they go to the top
|
bool UIWidget::isEnabled()
|
||||||
if(focusedChild && focusedChild->hasChildren())
|
{
|
||||||
moveChildToTop(focusedChild);
|
if(!m_enabled)
|
||||||
|
return false;
|
||||||
|
else if(UIWidgetPtr parent = getParent())
|
||||||
|
return parent->isEnabled();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UIWidget::hasFocus()
|
bool UIWidget::hasFocus()
|
||||||
|
@ -458,6 +468,37 @@ UIWidgetPtr UIWidget::backwardsGetWidgetById(const std::string& id)
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UIWidget::focusChild(const UIWidgetPtr& focusedChild, FocusReason reason)
|
||||||
|
{
|
||||||
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
if(focusedChild != m_focusedChild) {
|
||||||
|
UIWidgetPtr oldFocused = m_focusedChild;
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// when containers are focused they go to the top
|
||||||
|
if(focusedChild && focusedChild->hasChildren())
|
||||||
|
moveChildToTop(focusedChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UIWidget::addChild(const UIWidgetPtr& childToAdd)
|
void UIWidget::addChild(const UIWidgetPtr& childToAdd)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
@ -471,7 +512,7 @@ void UIWidget::addChild(const UIWidgetPtr& childToAdd)
|
||||||
|
|
||||||
// focus it if there is no focused child yet
|
// focus it if there is no focused child yet
|
||||||
if(!m_focusedChild && childToAdd->isFocusable())
|
if(!m_focusedChild && childToAdd->isFocusable())
|
||||||
focusChild(childToAdd);
|
focusChild(childToAdd, ActiveFocusReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
|
void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
|
||||||
|
@ -480,7 +521,10 @@ void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
|
||||||
|
|
||||||
// defocus if needed
|
// defocus if needed
|
||||||
if(m_focusedChild == childToRemove)
|
if(m_focusedChild == childToRemove)
|
||||||
focusChild(nullptr);
|
focusChild(nullptr, ActiveFocusReason);
|
||||||
|
|
||||||
|
// try to unlock
|
||||||
|
unlockChild(childToRemove);
|
||||||
|
|
||||||
// remove from children list
|
// remove from children list
|
||||||
auto it = std::find(m_children.begin(), m_children.end(), childToRemove);
|
auto it = std::find(m_children.begin(), m_children.end(), childToRemove);
|
||||||
|
@ -492,7 +536,7 @@ void UIWidget::removeChild(const UIWidgetPtr& childToRemove)
|
||||||
childToRemove->setParent(nullptr);
|
childToRemove->setParent(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::focusNextChild()
|
void UIWidget::focusNextChild(FocusReason reason)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
@ -512,7 +556,7 @@ void UIWidget::focusNextChild()
|
||||||
toFocus = m_children.back();
|
toFocus = m_children.back();
|
||||||
|
|
||||||
if(toFocus)
|
if(toFocus)
|
||||||
focusChild(toFocus);
|
focusChild(toFocus, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::moveChildToTop(const UIWidgetPtr& childToMove)
|
void UIWidget::moveChildToTop(const UIWidgetPtr& childToMove)
|
||||||
|
@ -526,6 +570,44 @@ void UIWidget::moveChildToTop(const UIWidgetPtr& childToMove)
|
||||||
m_children.push_back(childToMove);
|
m_children.push_back(childToMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UIWidget::lockChild(const UIWidgetPtr& childToLock)
|
||||||
|
{
|
||||||
|
assert(hasChild(childToLock));
|
||||||
|
|
||||||
|
// disable all other widgets
|
||||||
|
for(const UIWidgetPtr& widget : m_children) {
|
||||||
|
if(widget == childToLock)
|
||||||
|
widget->setEnabled(true);
|
||||||
|
else
|
||||||
|
widget->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_lockedWidgets.push_front(childToLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIWidget::unlockChild(const UIWidgetPtr& childToUnlock)
|
||||||
|
{
|
||||||
|
assert(hasChild(childToUnlock));
|
||||||
|
|
||||||
|
auto it = std::find(m_lockedWidgets.begin(), m_lockedWidgets.end(), childToUnlock);
|
||||||
|
if(it != m_lockedWidgets.end()) {
|
||||||
|
m_lockedWidgets.erase(it);
|
||||||
|
UIWidgetPtr newLockedWidget;
|
||||||
|
if(m_lockedWidgets.size() > 0)
|
||||||
|
newLockedWidget = m_lockedWidgets.front();
|
||||||
|
|
||||||
|
for(const UIWidgetPtr& child : m_children) {
|
||||||
|
if(newLockedWidget) {
|
||||||
|
if(child == newLockedWidget)
|
||||||
|
child->setEnabled(true);
|
||||||
|
else
|
||||||
|
child->setEnabled(false);
|
||||||
|
} else
|
||||||
|
child->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void UIWidget::addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge)
|
void UIWidget::addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
@ -554,117 +636,175 @@ void UIWidget::fill(const std::string& targetId)
|
||||||
addAnchor(AnchorBottom, targetId, AnchorBottom);
|
addAnchor(AnchorBottom, targetId, AnchorBottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onKeyboardText(const std::string& text)
|
void UIWidget::onKeyPress(UIKeyEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
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
|
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
continue;
|
continue;
|
||||||
// key events go only to containers or focused child
|
|
||||||
if(child->hasChildren() || child->hasFocus())
|
|
||||||
child->onKeyboardText(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UIWidget::onKeyPress(const UIKeyEvent& event)
|
|
||||||
{
|
|
||||||
assert(!m_destroyed);
|
|
||||||
|
|
||||||
// do a backup of children list, because it may change while looping it
|
|
||||||
UIWidgetList children = m_children;
|
|
||||||
for(const UIWidgetPtr& child : children) {
|
|
||||||
if(!child->isEnabled() || !child->isVisible())
|
|
||||||
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->hasFocus()) {
|
||||||
|
event.accept();
|
||||||
child->onKeyPress(event);
|
child->onKeyPress(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onKeyPress(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onKeyRelease(const UIKeyEvent& event)
|
void UIWidget::onKeyRelease(UIKeyEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
event.ignore();
|
||||||
|
|
||||||
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
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->hasFocus()) {
|
||||||
|
event.accept();
|
||||||
child->onKeyRelease(event);
|
child->onKeyRelease(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onKeyRelease(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onMousePress(const UIMouseEvent& event)
|
void UIWidget::onMousePress(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
event.ignore();
|
||||||
|
|
||||||
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
continue;
|
continue;
|
||||||
// mouse press events only go to children that contains the mouse position
|
|
||||||
if(child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos)) {
|
|
||||||
child->onMousePress(event);
|
|
||||||
|
|
||||||
|
// mouse press events only go to children that contains the mouse position
|
||||||
|
if(child->getGeometry().contains(event.pos()) && child == getChildByPos(event.pos())) {
|
||||||
// focus it
|
// focus it
|
||||||
if(child->isFocusable())
|
if(child->isFocusable())
|
||||||
focusChild(child);
|
focusChild(child, MouseFocusReason);
|
||||||
|
|
||||||
|
event.accept();
|
||||||
|
child->onMousePress(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onMousePress(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onMouseRelease(const UIMouseEvent& event)
|
void UIWidget::onMouseRelease(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
event.ignore();
|
||||||
|
|
||||||
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// mouse release events go to all children
|
// mouse release events go to all children
|
||||||
|
event.accept();
|
||||||
child->onMouseRelease(event);
|
child->onMouseRelease(event);
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onMouseRelease(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onMouseMove(const UIMouseEvent& event)
|
void UIWidget::onMouseMove(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
event.ignore();
|
||||||
|
|
||||||
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// update child over status
|
// check if the mouse is relally over this child
|
||||||
bool overChild = (child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos));
|
bool overChild = (isHovered() &&
|
||||||
|
child->getGeometry().contains(event.pos()) &&
|
||||||
|
child == getChildByPos(event.pos()));
|
||||||
if(overChild != child->isHovered()) {
|
if(overChild != child->isHovered()) {
|
||||||
child->setHovered(overChild);
|
child->setHovered(overChild);
|
||||||
child->onHoverChange(overChild);
|
|
||||||
|
UIHoverEvent e(overChild);
|
||||||
|
child->onHoverChange(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mouse move events go to all children
|
// mouse move events go to all children
|
||||||
|
event.accept();
|
||||||
child->onMouseMove(event);
|
child->onMouseMove(event);
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onMouseMove(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWidget::onMouseWheel(const UIMouseEvent& event)
|
void UIWidget::onMouseWheel(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
assert(!m_destroyed);
|
assert(!m_destroyed);
|
||||||
|
|
||||||
|
event.ignore();
|
||||||
|
|
||||||
// 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->isEnabled() || !child->isVisible())
|
if(!child->isExplicitlyEnabled() || !child->isVisible())
|
||||||
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
|
||||||
if(child->getGeometry().contains(event.mousePos) && child == getChildByPos(event.mousePos))
|
if(child->getGeometry().contains(event.pos()) && child == getChildByPos(event.pos())) {
|
||||||
|
event.accept();
|
||||||
child->onMouseWheel(event);
|
child->onMouseWheel(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(event.isAccepted())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
child->UIWidget::onMouseWheel(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@ public:
|
||||||
/// Remove this widget from parent then destroy it and its children
|
/// Remove this widget from parent then destroy it and its children
|
||||||
virtual void destroy();
|
virtual void destroy();
|
||||||
|
|
||||||
|
/// Called after the widget is loaded from OTML file, to execute onLoad event
|
||||||
|
virtual void load();
|
||||||
|
|
||||||
/// Load style from otml node
|
/// Load style from otml node
|
||||||
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
virtual void loadStyleFromOTML(const OTMLNodePtr& styleNode);
|
||||||
|
|
||||||
|
@ -36,15 +39,16 @@ 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())); }
|
||||||
void setHeight(int height) { resize(Size(getWidth(), height)); }
|
void setHeight(int height) { resize(Size(getWidth(), height)); }
|
||||||
void resize(const Size& size) { setGeometry(Rect(getPosition(), size)); }
|
void resize(const Size& size) { setGeometry(Rect(getPosition(), size)); updateGeometry(); }
|
||||||
void move(const Point& pos) { setGeometry(Rect(pos, getSize())); }
|
void move(const Point& pos) { setGeometry(Rect(pos, getSize())); }
|
||||||
|
|
||||||
void setImage(const ImagePtr& image) { m_image = image; }
|
void setImage(const ImagePtr& image) { m_image = image; }
|
||||||
void setFont(const FontPtr& font) { m_font = font; }
|
virtual void setFont(const FontPtr& font) { m_font = font; }
|
||||||
void setColor(const Color& color) { m_color = color; }
|
void setColor(const Color& color) { m_color = color; }
|
||||||
void setMarginLeft(int margin) { m_marginLeft = margin; updateGeometry(); }
|
void setMarginLeft(int margin) { m_marginLeft = margin; updateGeometry(); }
|
||||||
void setMarginRight(int margin) { m_marginRight = margin; updateGeometry(); }
|
void setMarginRight(int margin) { m_marginRight = margin; updateGeometry(); }
|
||||||
|
@ -55,8 +59,10 @@ public:
|
||||||
void show() { setVisible(true); }
|
void show() { setVisible(true); }
|
||||||
void disable() { setEnabled(false); }
|
void disable() { setEnabled(false); }
|
||||||
void enable() { setEnabled(true); }
|
void enable() { setEnabled(true); }
|
||||||
|
void lock();
|
||||||
|
|
||||||
bool isEnabled() const { return m_enabled; }
|
bool isEnabled();
|
||||||
|
bool isExplicitlyEnabled() const { return m_enabled; }
|
||||||
bool isVisible() const { return m_visible; }
|
bool isVisible() 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; }
|
||||||
|
@ -99,9 +105,11 @@ public:
|
||||||
|
|
||||||
void addChild(const UIWidgetPtr& childToAdd);
|
void addChild(const UIWidgetPtr& childToAdd);
|
||||||
void removeChild(const UIWidgetPtr& childToRemove);
|
void removeChild(const UIWidgetPtr& childToRemove);
|
||||||
void focusChild(const UIWidgetPtr& childToFocus);
|
void focusChild(const UIWidgetPtr& childToFocus, FocusReason reason);
|
||||||
void focusNextChild();
|
void focusNextChild(FocusReason reason);
|
||||||
void moveChildToTop(const UIWidgetPtr& childToMove);
|
void moveChildToTop(const UIWidgetPtr& childToMove);
|
||||||
|
void lockChild(const UIWidgetPtr& childToLock);
|
||||||
|
void unlockChild(const UIWidgetPtr& childToUnlock);
|
||||||
|
|
||||||
// for using only with anchor layouts
|
// for using only with anchor layouts
|
||||||
void addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge);
|
void addAnchor(AnchorPoint edge, const std::string& targetId, AnchorPoint targetEdge);
|
||||||
|
@ -112,33 +120,32 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Triggered when widget is moved or resized
|
/// Triggered when widget is moved or resized
|
||||||
virtual void onGeometryUpdate() { }
|
virtual void onGeometryUpdate(UIGeometryUpdateEvent& event) { }
|
||||||
/// Triggered when widget is shown or hidden
|
// Triggered when widget change visibility/enabled/style/children/parent/layout/...
|
||||||
//virtual void onVisibilityChange(bool visible);
|
//virtual void onChange(const UIEvent& event);
|
||||||
/// Triggered when widget gets or loses focus
|
/// Triggered when widget gets or loses focus
|
||||||
virtual void onFocusChange(bool focused) { }
|
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(bool hovered) { }
|
virtual void onHoverChange(UIHoverEvent& event) { }
|
||||||
/// Triggered when user generates a text from keyboard
|
|
||||||
virtual void onKeyboardText(const std::string& text);
|
|
||||||
/// Triggered when user presses key while widget has focus
|
/// Triggered when user presses key while widget has focus
|
||||||
virtual void onKeyPress(const UIKeyEvent& event);
|
virtual void onKeyPress(UIKeyEvent& event);
|
||||||
/// Triggered when user releases key while widget has focus
|
/// Triggered when user releases key while widget has focus
|
||||||
virtual void onKeyRelease(const UIKeyEvent& event);
|
virtual void onKeyRelease(UIKeyEvent& event);
|
||||||
/// Triggered when a mouse button is pressed down while mouse pointer is inside widget area
|
/// Triggered when a mouse button is pressed down while mouse pointer is inside widget area
|
||||||
virtual void onMousePress(const UIMouseEvent& event);
|
virtual void onMousePress(UIMouseEvent& event);
|
||||||
/// Triggered when a mouse button is released
|
/// Triggered when a mouse button is released
|
||||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
virtual void onMouseRelease(UIMouseEvent& event);
|
||||||
/// Triggered when mouse moves (even when the mouse is outside widget area)
|
/// Triggered when mouse moves (even when the mouse is outside widget area)
|
||||||
virtual void onMouseMove(const UIMouseEvent& event);
|
virtual void onMouseMove(UIMouseEvent& event);
|
||||||
/// Triggered when mouse middle button wheels inside widget area
|
/// Triggered when mouse middle button wheels inside widget area
|
||||||
virtual void onMouseWheel(const UIMouseEvent& event);
|
virtual void onMouseWheel(UIMouseEvent& event);
|
||||||
|
|
||||||
friend class UIManager;
|
friend class UIManager;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void destroyCheck();
|
void destroyCheck();
|
||||||
|
|
||||||
|
protected:
|
||||||
UIWidgetType m_type;
|
UIWidgetType m_type;
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
bool m_visible;
|
bool m_visible;
|
||||||
|
@ -150,6 +157,7 @@ private:
|
||||||
UILayoutPtr m_layout;
|
UILayoutPtr m_layout;
|
||||||
UIWidgetWeakPtr m_parent;
|
UIWidgetWeakPtr m_parent;
|
||||||
UIWidgetList m_children;
|
UIWidgetList m_children;
|
||||||
|
UIWidgetList m_lockedWidgets;
|
||||||
UIWidgetPtr m_focusedChild;
|
UIWidgetPtr m_focusedChild;
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
|
|
||||||
|
|
|
@ -68,12 +68,10 @@ void UIWindow::render()
|
||||||
UIWidget::render();
|
UIWidget::render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWindow::onGeometryUpdate()
|
void UIWindow::onGeometryUpdate(UIGeometryUpdateEvent& event)
|
||||||
{
|
{
|
||||||
UIWidget::onGeometryUpdate();
|
|
||||||
|
|
||||||
// bind window rect to parent rect
|
// bind window rect to parent rect
|
||||||
Rect boundRect = getGeometry();
|
Rect boundRect = event.rect();
|
||||||
UIWidgetPtr parent = getParent();
|
UIWidgetPtr parent = getParent();
|
||||||
if(parent) {
|
if(parent) {
|
||||||
Rect parentRect = parent->getGeometry();
|
Rect parentRect = parent->getGeometry();
|
||||||
|
@ -86,31 +84,34 @@ void UIWindow::onGeometryUpdate()
|
||||||
if(boundRect.right() > parentRect.right())
|
if(boundRect.right() > parentRect.right())
|
||||||
boundRect.moveRight(parentRect.right());
|
boundRect.moveRight(parentRect.right());
|
||||||
}
|
}
|
||||||
if(boundRect != getGeometry())
|
|
||||||
|
if(boundRect != event.rect())
|
||||||
setGeometry(boundRect);
|
setGeometry(boundRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWindow::onMousePress(const UIMouseEvent& event)
|
void UIWindow::onMousePress(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
UIWidget::onMousePress(event);
|
|
||||||
Rect headRect = getGeometry();
|
Rect headRect = getGeometry();
|
||||||
headRect.setHeight(m_headHeight);
|
headRect.setHeight(m_headHeight);
|
||||||
if(headRect.contains(event.mousePos)) {
|
if(headRect.contains(event.pos())) {
|
||||||
m_moving = true;
|
m_moving = true;
|
||||||
m_movingReference = event.mousePos - getGeometry().topLeft();
|
m_movingReference = event.pos() - getGeometry().topLeft();
|
||||||
}
|
} else
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWindow::onMouseRelease(const UIMouseEvent& event)
|
void UIWindow::onMouseRelease(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
UIWidget::onMouseRelease(event);
|
|
||||||
if(m_moving)
|
if(m_moving)
|
||||||
m_moving = false;
|
m_moving = false;
|
||||||
|
else
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIWindow::onMouseMove(const UIMouseEvent& event)
|
void UIWindow::onMouseMove(UIMouseEvent& event)
|
||||||
{
|
{
|
||||||
UIWidget::onMouseMove(event);
|
|
||||||
if(m_moving)
|
if(m_moving)
|
||||||
move(event.mousePos - m_movingReference);
|
move(event.pos() - m_movingReference);
|
||||||
|
else
|
||||||
|
event.ignore();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,10 @@ public:
|
||||||
std::string getTitle() const { return m_title; }
|
std::string getTitle() const { return m_title; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onGeometryUpdate();
|
virtual void onGeometryUpdate(UIGeometryUpdateEvent& event);
|
||||||
virtual void onMousePress(const UIMouseEvent& event);
|
virtual void onMousePress(UIMouseEvent& event);
|
||||||
virtual void onMouseRelease(const UIMouseEvent& event);
|
virtual void onMouseRelease(UIMouseEvent& event);
|
||||||
virtual void onMouseMove(const UIMouseEvent& event);
|
virtual void onMouseMove(UIMouseEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string m_title;
|
std::string m_title;
|
||||||
|
|
|
@ -263,7 +263,7 @@ void OTClient::onInputEvent(const InputEvent& event)
|
||||||
|
|
||||||
ProtocolGame *protocol = g_game.getProtocol();
|
ProtocolGame *protocol = g_game.getProtocol();
|
||||||
if(protocol) {
|
if(protocol) {
|
||||||
if(event.type == EventKeyUp) {
|
if(event.type == EventKeyDown) {
|
||||||
if(event.keycode == KC_UP)
|
if(event.keycode == KC_UP)
|
||||||
protocol->sendWalkNorth();
|
protocol->sendWalkNorth();
|
||||||
if(event.keycode == KC_RIGHT)
|
if(event.keycode == KC_RIGHT)
|
||||||
|
|
Loading…
Reference in New Issue