GUI: cursor in textbox

This commit is contained in:
Mikulas Florek 2014-01-03 18:52:25 +01:00
parent 7ab45ce6ba
commit b8465e682a
11 changed files with 246 additions and 4 deletions

View file

@ -97,6 +97,7 @@
<ClInclude Include="..\..\src\gui\controls\text_box.h" />
<ClInclude Include="..\..\src\gui\decorators\box_decorator.h" />
<ClInclude Include="..\..\src\gui\decorators\check_box_decorator.h" />
<ClInclude Include="..\..\src\gui\decorators\cursor_decorator.h" />
<ClInclude Include="..\..\src\gui\decorators\dockable_decorator.h" />
<ClInclude Include="..\..\src\gui\decorators\scrollbar_decorator.h" />
<ClInclude Include="..\..\src\gui\decorators\text_decorator.h" />
@ -121,6 +122,7 @@
<ClCompile Include="..\..\src\gui\controls\text_box.cpp" />
<ClCompile Include="..\..\src\gui\decorators\box_decorator.cpp" />
<ClCompile Include="..\..\src\gui\decorators\check_box_decorator.cpp" />
<ClCompile Include="..\..\src\gui\decorators\cursor_decorator.cpp" />
<ClCompile Include="..\..\src\gui\decorators\dockable_decorator.cpp" />
<ClCompile Include="..\..\src\gui\decorators\scrollbar_decorator.cpp" />
<ClCompile Include="..\..\src\gui\decorators\text_decorator.cpp" />

View file

@ -50,6 +50,9 @@
<ClInclude Include="..\..\src\gui\decorators\dockable_decorator.h">
<Filter>decorators</Filter>
</ClInclude>
<ClInclude Include="..\..\src\gui\decorators\cursor_decorator.h">
<Filter>decorators</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\gui\gui.cpp" />
@ -98,6 +101,9 @@
<ClCompile Include="..\..\src\gui\decorators\dockable_decorator.cpp">
<Filter>decorators</Filter>
</ClCompile>
<ClCompile Include="..\..\src\gui\decorators\cursor_decorator.cpp">
<Filter>decorators</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="decorators">

View file

@ -167,6 +167,15 @@ class base_string
return ret;
}
void erase(size_t pos)
{
if(pos >= 0 && pos < m_size)
{
base_string<T>::strcpy(m_cstr + pos, m_cstr + pos + 1);
--m_size;
}
}
public:
static const int npos = 0xffFFffFF;

View file

@ -14,6 +14,7 @@
#include "graphics/renderer.h"
#include "gui/block.h"
#include "gui/decorators/box_decorator.h"
#include "gui/decorators/cursor_decorator.h"
#include "gui/decorators/check_box_decorator.h"
#include "gui/decorators/dockable_decorator.h"
#include "gui/decorators/text_decorator.h"
@ -30,6 +31,7 @@ void initGui(Lux::EditorClient& client, Lux::EditorServer& server)
renderer->create();
renderer->loadFont("gui/font.tga", server.getEngine().getFileSystem());
renderer->setWindowHeight(600);
Lux::UI::CursorDecorator* cursor_decorator = LUX_NEW(Lux::UI::CursorDecorator)("_cursor");
Lux::UI::CheckBoxDecorator* check_box_decorator = LUX_NEW(Lux::UI::CheckBoxDecorator)("_check_box");
Lux::UI::TextDecorator* text_decorator = LUX_NEW(Lux::UI::TextDecorator)("_text");
Lux::UI::TextDecorator* text_centered_decorator = LUX_NEW(Lux::UI::TextDecorator)("_text_centered");
@ -39,6 +41,7 @@ void initGui(Lux::EditorClient& client, Lux::EditorServer& server)
Lux::UI::ScrollbarDecorator* scrollbar_decorator = LUX_NEW(Lux::UI::ScrollbarDecorator)("_scrollbar");
server.getEngine().loadPlugin("gui.dll");
Lux::UI::Gui* gui = (Lux::UI::Gui*)server.getEngine().getPluginManager().getPlugin("gui");
gui->addDecorator(*cursor_decorator);
gui->addDecorator(*text_decorator);
gui->addDecorator(*text_centered_decorator);
gui->addDecorator(*box_decorator);
@ -46,6 +49,7 @@ void initGui(Lux::EditorClient& client, Lux::EditorServer& server)
gui->addDecorator(*scrollbar_decorator);
gui->addDecorator(*check_box_decorator);
gui->setRenderer(*renderer);
cursor_decorator->create(*gui, "gui/skin.atl");
check_box_decorator->create(*gui, "gui/skin.atl");
scrollbar_decorator->create(*gui, "gui/skin.atl");
box_decorator->create(*gui, "gui/skin.atl");

View file

@ -1,6 +1,7 @@
#include "gui/controls/text_box.h"
#include "core/crc32.h"
#include "core/iserializer.h"
#include "gui/gui.h"
namespace Lux
@ -17,29 +18,77 @@ TextBox::TextBox(const char* text, Gui& gui, Block* parent)
label_ui->setBlockText(text);
label_ui->setArea(0, 3, 0, 0, 1, 0, 1, 0);
label_ui->onEvent("key_down").bind<TextBox, &TextBox::keyDown>(this);
label_ui->onEvent("focus").bind<TextBox, &TextBox::focused>(this);
label_ui->onEvent("blur").bind<TextBox, &TextBox::blurred>(this);
label_ui->setIsClipping(true);
m_cursor_pos = 0;
m_cursor = LUX_NEW(Block)(gui, label_ui, "_cursor");
m_cursor->hide();
}
void TextBox::setCursorArea()
{
Block::Area area = getGui().getRenderer().getCharArea(getChild(0)->getBlockText().c_str(), m_cursor_pos, getGlobalWidth());
m_cursor->setArea(area);
layout();
}
void TextBox::blurred(Block& block, void* user_data)
{
m_cursor->hide();
}
void TextBox::focused(Block& block, void* user_data)
{
m_cursor_pos = getChild(0)->getBlockText().length();
m_cursor->show();
setCursorArea();
}
static const int32_t KEY_RIGHT = 79 + (1 << 30);
static const int32_t KEY_LEFT = 80 + (1 << 30);
static const int32_t KEY_UP = 81 + (1 << 30);
static const int32_t KEY_DOWN = 82 + (1 << 30);
static const int32_t KEY_BACKSPACE = '\b';
static const int32_t KEY_DELETE = '\177';
void TextBox::keyDown(Block& block, void* user_data)
{
Lux::string s = block.getBlockText();
char c[2];
switch((int32_t)user_data)
{
case KEY_RIGHT:
m_cursor_pos = m_cursor_pos > s.length() - 1 ? s.length() : m_cursor_pos + 1;
break;
case KEY_LEFT:
m_cursor_pos = m_cursor_pos < 1 ? 0 : m_cursor_pos - 1;
break;
case KEY_UP:
case KEY_DOWN:
break;
case '\r':
block.emitEvent("text_accepted");
break;
case '\b':
s = s.substr(0, s.length() - 1);
case KEY_BACKSPACE:
s.erase(m_cursor_pos - 1);
if(m_cursor_pos > 0)
{
--m_cursor_pos;
}
break;
default:
default:
c[0] = (char)user_data;
c[1] = '\0';
s += c;
++m_cursor_pos;
break;
}
block.setBlockText(s.c_str());
setCursorArea();
}

View file

@ -25,6 +25,13 @@ namespace UI
private:
void keyDown(Block& block, void* user_data);
void focused(Block& block, void* user_data);
void blurred(Block& block, void* user_data);
void setCursorArea();
private:
int m_cursor_pos;
Lux::UI::Block* m_cursor;
};

View file

@ -0,0 +1,54 @@
#include "gui/decorators/cursor_decorator.h"
#include "core/crc32.h"
#include "gui/atlas.h"
#include "gui/block.h"
#include "gui/controls/dockable.h"
#include "gui/gui.h"
#include "gui/irenderer.h"
#include "gui/texture_base.h"
namespace Lux
{
namespace UI
{
static const uint32_t dockable_hash = crc32("dockable");
void CursorDecorator::setVertices(Vec3* verts, float left, float top, float right, float bottom, float z) const
{
verts[0].set(left, top, z);
verts[1].set(left, bottom, z);
verts[2].set(right, bottom, z);
verts[3].set(left, top, z);
verts[4].set(right, bottom, z);
verts[5].set(right, top, z);
}
void CursorDecorator::render(IRenderer& renderer, Block& block)
{
m_part = m_part ? m_part : m_atlas->getPart("cursor");
if(m_part)
{
setVertices(m_vertices, block.getGlobalLeft(), block.getGlobalTop(), block.getGlobalLeft() + m_part->m_pixel_width, block.getGlobalBottom(), block.getZ());
m_part->getUvs(&m_uvs[0]);
if(m_atlas->getTexture())
{
renderer.renderImage(m_atlas->getTexture(), &m_vertices[0].x, m_uvs, 6);
}
}
}
bool CursorDecorator::create(Gui& gui, const char* atlas)
{
m_part = NULL;
m_atlas = gui.loadAtlas(atlas);
return m_atlas != NULL;
}
} // ~namespace UI
} // ~namespace Lux

View file

@ -0,0 +1,40 @@
#pragma once
#include "core/lux.h"
#include "core/vec3.h"
#include "gui/atlas.h"
#include "gui/decorator_base.h"
namespace Lux
{
namespace UI
{
class Dockable;
class Gui;
class IRenderer;
class TextureBase;
class LUX_GUI_API CursorDecorator : public DecoratorBase
{
public:
CursorDecorator(const char* name) : DecoratorBase(name) {}
bool create(Gui& gui, const char* atlas);
virtual void render(IRenderer& renderer, Block& block) LUX_OVERRIDE;
private:
void setVertices(Vec3* verts, float left, float top, float right, float bottom, float z) const;
private:
Atlas* m_atlas;
const Atlas::Part* m_part;
Vec3 m_vertices[24];
float m_uvs[48];
};
} // ~namespace UI
} // ~namespace Lux

View file

@ -2,6 +2,7 @@
#include "core/lux.h"
#include "gui/block.h"
namespace Lux
@ -26,6 +27,7 @@ namespace UI
virtual TextureBase* loadImage(const char* name, FS::FileSystem& file_system) = 0;
virtual void beginRender(float w, float h) = 0;
virtual void renderImage(TextureBase* image, float* vertices, float* tex_coords, int vertex_count) = 0;
virtual Block::Area getCharArea(const char* text, int pos, float max_width) = 0;
virtual void measureText(const char* text, float* w, float* h, float max_width) = 0;
virtual void renderText(const char* text, float x, float y, float z, float max_width) = 0;
virtual void pushScissorArea(float left, float top, float right, float bottom) = 0;

View file

@ -1,12 +1,13 @@
#include "gui/opengl_renderer.h"
#include <cstdio>
#include <Windows.h>
#include <gl/GL.h>
#include "gui/opengl_renderer.h"
#include "core/array.h"
#include "core/delegate_list.h"
#include "core/file_system.h"
#include "core/ifile.h"
#include "core/map.h"
#include "core/math_utils.h"
#include "core/pod_array.h"
#include "core/string.h"
#include "core/vec3.h"
@ -221,6 +222,73 @@ namespace UI
glLoadIdentity();
}
Block::Area OpenGLRenderer::getCharArea(const char* text, int pos, float max_width)
{
Block::Area area;
if(text)
{
float width = 0;
float height = 0;
float prev_h = 0;
const char* c = text;
bool is_multiline = false;
OpenGLRendererImpl::Character character;
bool found = false;
bool is_some_char = false;
while(*c)
{
if(m_impl->m_characters.find(*c, character))
{
is_some_char = true;
if(c - text == pos)
{
found = true;
area.left = width;
area.top = prev_h + character.y_offset;
area.right = width + character.x_advance;
area.bottom = prev_h + character.pixel_h + character.y_offset;
area.rel_bottom = area.rel_left = area.rel_right = area.rel_top = 0;
break;
}
width += character.x_advance;
height = Math::max(height, character.pixel_h);
if(width > max_width || *c == '\n')
{
is_multiline = true;
width = 0;
prev_h += height;
}
}
else if(*c == '\n')
{
is_multiline = true;
width = 0;
prev_h += height;
}
++c;
}
if(!found)
{
if(is_some_char)
{
area.left = width;
area.top = prev_h + character.y_offset;
area.right = width + character.x_advance;
area.bottom = prev_h + character.pixel_h + character.y_offset;
}
else
{
area.left = 0;
area.right = 3;
area.top = 0;
area.bottom = 20;
}
area.rel_bottom = area.rel_left = area.rel_right = area.rel_top = 0;
}
}
return area;
}
void OpenGLRenderer::measureText(const char* text, float* w, float* h, float max_width)
{
if(!text)

View file

@ -24,6 +24,7 @@ namespace UI
virtual void loadFont(const char* path, FS::FileSystem& file_system) LUX_OVERRIDE;
virtual void beginRender(float w, float h) LUX_OVERRIDE;
virtual void renderImage(TextureBase* image, float* vertices, float* tex_coords, int vertex_count) LUX_OVERRIDE;
virtual Block::Area getCharArea(const char* text, int pos, float max_width) LUX_OVERRIDE;
virtual void measureText(const char* text, float* w, float* h, float max_width) LUX_OVERRIDE;
virtual void renderText(const char* text, float x, float y, float z, float max_width) LUX_OVERRIDE;
virtual void pushScissorArea(float left, float top, float right, float bottom) LUX_OVERRIDE;