Work on TextField

This commit is contained in:
Jack Andersen 2015-12-19 18:39:09 -10:00
parent 5d56ef2cf6
commit 95fd2f90a9
12 changed files with 366 additions and 31 deletions

View File

@ -60,7 +60,13 @@ public:
void mouseMove(const boo::SWindowCoord&); void mouseMove(const boo::SWindowCoord&);
void mouseEnter(const boo::SWindowCoord&); void mouseEnter(const boo::SWindowCoord&);
void mouseLeave(const boo::SWindowCoord&); void mouseLeave(const boo::SWindowCoord&);
void scroll(const boo::SWindowCoord&, const boo::SScrollDelta&);
void touchDown(const boo::STouchCoord&, uintptr_t);
void touchUp(const boo::STouchCoord&, uintptr_t);
void touchMove(const boo::STouchCoord&, uintptr_t);
void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
void think();
void draw(boo::IGraphicsCommandQueue* gfxQ); void draw(boo::IGraphicsCommandQueue* gfxQ);
}; };

View File

@ -23,8 +23,7 @@ class ModalWindow : public View
int m_width = 0; int m_width = 0;
int m_height = 0; int m_height = 0;
int m_widthConstrain; RectangleConstraint m_constraint;
int m_heightConstrain;
Zeus::CColor m_windowBg; Zeus::CColor m_windowBg;
Zeus::CColor m_windowBgClear; Zeus::CColor m_windowBgClear;
@ -57,7 +56,7 @@ protected:
virtual void updateContentOpacity(float opacity) {} virtual void updateContentOpacity(float opacity) {}
public: public:
ModalWindow(ViewResources& res, View& parentView, int widthConstrain=-1, int heightConstrain=-1); ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint);
void think(); void think();
bool skipBuildInAnimation(); bool skipBuildInAnimation();

View File

@ -4,6 +4,7 @@
#include "View.hpp" #include "View.hpp"
#include "ViewResources.hpp" #include "ViewResources.hpp"
#include "MultiLineTextView.hpp" #include "MultiLineTextView.hpp"
#include "TextField.hpp"
#include "SplitView.hpp" #include "SplitView.hpp"
#include "Tooltip.hpp" #include "Tooltip.hpp"
#include "FontCache.hpp" #include "FontCache.hpp"
@ -26,6 +27,7 @@ class RootView : public View
bool m_destroyed = false; bool m_destroyed = false;
IViewManager& m_viewMan; IViewManager& m_viewMan;
ViewResources* m_viewRes; ViewResources* m_viewRes;
View* m_activeTextView = nullptr;
DeferredWindowEvents<RootView> m_events; DeferredWindowEvents<RootView> m_events;
@ -64,6 +66,13 @@ public:
const ThemeData& themeData() const {return m_viewRes->m_theme;} const ThemeData& themeData() const {return m_viewRes->m_theme;}
View* setContentView(View* view); View* setContentView(View* view);
void setActiveTextView(View* textView)
{
if (m_activeTextView)
m_activeTextView->setActive(false);
m_activeTextView = textView;
textView->setActive(true);
}
void resetTooltip(ViewResources& res); void resetTooltip(ViewResources& res);
void displayTooltip(const std::string& name, const std::string& help); void displayTooltip(const std::string& name, const std::string& help);

View File

@ -12,7 +12,7 @@ class TextField : public Control
std::string m_textStr; std::string m_textStr;
std::unique_ptr<TextView> m_text; std::unique_ptr<TextView> m_text;
SolidShaderVert m_verts[28]; SolidShaderVert m_verts[32];
boo::IGraphicsBufferD* m_bVertsBuf = nullptr; boo::IGraphicsBufferD* m_bVertsBuf = nullptr;
boo::IVertexFormat* m_bVtxFmt = nullptr; /* OpenGL only */ boo::IVertexFormat* m_bVtxFmt = nullptr; /* OpenGL only */
boo::IShaderDataBinding* m_bShaderBinding = nullptr; boo::IShaderDataBinding* m_bShaderBinding = nullptr;
@ -20,6 +20,13 @@ class TextField : public Control
int m_nomWidth = 0; int m_nomWidth = 0;
int m_nomHeight = 0; int m_nomHeight = 0;
size_t m_selectionStart = 0;
size_t m_selectionCount = 0;
size_t m_cursorPos = 0;
size_t m_cursorFrames = 0;
bool m_active = false;
void setInactive(); void setInactive();
void setHover(); void setHover();
void setDisabled(); void setDisabled();
@ -27,19 +34,29 @@ class TextField : public Control
public: public:
TextField(ViewResources& res, View& parentView, IStringBinding* strBind); TextField(ViewResources& res, View& parentView, IStringBinding* strBind);
const std::string& getText() const {return m_textStr;}
void setText(const std::string& str); void setText(const std::string& str);
void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey); void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey);
void mouseMove(const boo::SWindowCoord&); void mouseMove(const boo::SWindowCoord&);
void mouseEnter(const boo::SWindowCoord&); void mouseEnter(const boo::SWindowCoord&);
void mouseLeave(const boo::SWindowCoord&coord); void mouseLeave(const boo::SWindowCoord&);
void charKeyDown(unsigned long, boo::EModifierKey, bool);
void specialKeyDown(boo::ESpecialKey, boo::EModifierKey, bool);
void think();
void resized(const boo::SWindowRect& rootView, const boo::SWindowRect& sub); void resized(const boo::SWindowRect& rootView, const boo::SWindowRect& sub);
void draw(boo::IGraphicsCommandQueue* gfxQ); void draw(boo::IGraphicsCommandQueue* gfxQ);
int nominalWidth() const {return m_nomWidth;} int nominalWidth() const {return m_nomWidth;}
int nominalHeight() const {return m_nomHeight;} int nominalHeight() const {return m_nomHeight;}
void setActive(bool active);
void setCursorPos(size_t pos);
void setSelectionRange(size_t start, size_t count);
void clearSelectionRange();
void setMultiplyColor(const Zeus::CColor& color) void setMultiplyColor(const Zeus::CColor& color)
{ {
View::setMultiplyColor(color); View::setMultiplyColor(color);

View File

@ -67,6 +67,7 @@ public:
RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, const Zeus::CColor& defaultColor); RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, const Zeus::CColor& defaultColor);
}; };
std::vector<RenderGlyph>& accessGlyphs() {return m_glyphs;} std::vector<RenderGlyph>& accessGlyphs() {return m_glyphs;}
const std::vector<RenderGlyph>& accessGlyphs() const {return m_glyphs;}
void updateGlyphs() {m_valid = false;} void updateGlyphs() {m_valid = false;}
void typesetGlyphs(const std::string& str, void typesetGlyphs(const std::string& str,
@ -85,10 +86,13 @@ public:
int nominalHeight() const {return m_fontAtlas.FT_LineHeight() >> 6;} int nominalHeight() const {return m_fontAtlas.FT_LineHeight() >> 6;}
std::pair<int,int> queryGlyphDimensions(size_t pos) const; std::pair<int,int> queryGlyphDimensions(size_t pos) const;
size_t reverseSelectGlyph(int x) const;
int queryReverseAdvance(size_t idx) const;
private: private:
std::vector<RenderGlyph> m_glyphs; std::vector<RenderGlyph> m_glyphs;
std::vector<std::pair<int,int>> m_glyphDims; std::vector<std::pair<int,int>> m_glyphDims;
std::vector<int> m_glyphAdvs;
}; };
} }

View File

@ -18,6 +18,65 @@ class ThemeData;
class ViewResources; class ViewResources;
class RootView; class RootView;
class RectangleConstraint
{
public:
enum class Test
{
Fixed,
Minimum,
Maximum
};
private:
int m_x, m_y;
Test m_xtest, m_ytest;
public:
RectangleConstraint(int x=-1, int y=-1, Test xtest=Test::Fixed, Test ytest=Test::Fixed)
: m_x(x), m_y(y), m_xtest(xtest), m_ytest(ytest) {}
std::pair<int,int> solve(int x, int y) const
{
std::pair<int,int> ret;
if (m_x < 0)
ret.first = x;
else
{
switch (m_xtest)
{
case Test::Fixed:
ret.first = m_x;
break;
case Test::Minimum:
ret.first = std::max(m_x, x);
break;
case Test::Maximum:
ret.first = std::min(m_x, x);
break;
}
}
if (m_y < 0)
ret.second = y;
else
{
switch (m_ytest)
{
case Test::Fixed:
ret.second = m_y;
break;
case Test::Minimum:
ret.second = std::max(m_y, y);
break;
case Test::Maximum:
ret.second = std::min(m_y, y);
break;
}
}
return ret;
}
};
class View class View
{ {
public: public:
@ -127,13 +186,26 @@ public:
virtual int nominalWidth() const {return 0;} virtual int nominalWidth() const {return 0;}
virtual int nominalHeight() const {return 0;} virtual int nominalHeight() const {return 0;}
virtual void updateCVar(HECL::CVar* cvar) {} virtual void setActive(bool) {}
virtual void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) {} virtual void mouseDown(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) {}
virtual void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) {} virtual void mouseUp(const boo::SWindowCoord&, boo::EMouseButton, boo::EModifierKey) {}
virtual void mouseMove(const boo::SWindowCoord&) {} virtual void mouseMove(const boo::SWindowCoord&) {}
virtual void mouseEnter(const boo::SWindowCoord&) {} virtual void mouseEnter(const boo::SWindowCoord&) {}
virtual void mouseLeave(const boo::SWindowCoord&) {} virtual void mouseLeave(const boo::SWindowCoord&) {}
virtual void scroll(const boo::SWindowCoord&, const boo::SScrollDelta&) {}
virtual void touchDown(const boo::STouchCoord&, uintptr_t) {}
virtual void touchUp(const boo::STouchCoord&, uintptr_t) {}
virtual void touchMove(const boo::STouchCoord&, uintptr_t) {}
virtual void charKeyDown(unsigned long, boo::EModifierKey, bool) {}
virtual void charKeyUp(unsigned long, boo::EModifierKey) {}
virtual void specialKeyDown(boo::ESpecialKey, boo::EModifierKey, bool) {}
virtual void specialKeyUp(boo::ESpecialKey, boo::EModifierKey) {}
virtual void modKeyDown(boo::EModifierKey, bool) {}
virtual void modKeyUp(boo::EModifierKey) {}
virtual void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub); virtual void resized(const boo::SWindowRect& root, const boo::SWindowRect& sub);
virtual void think() {}
virtual void draw(boo::IGraphicsCommandQueue* gfxQ); virtual void draw(boo::IGraphicsCommandQueue* gfxQ);
}; };

View File

@ -14,6 +14,7 @@ class ThemeData
{ {
Zeus::CColor m_uiText = Zeus::CColor::skWhite; Zeus::CColor m_uiText = Zeus::CColor::skWhite;
Zeus::CColor m_fieldText = Zeus::CColor::skBlack; Zeus::CColor m_fieldText = Zeus::CColor::skBlack;
Zeus::CColor m_selectedFieldText = Zeus::CColor::skWhite;
Zeus::CColor m_vpBg = {0.2, 0.2, 0.2, 1.0}; Zeus::CColor m_vpBg = {0.2, 0.2, 0.2, 1.0};
Zeus::CColor m_tbBg = {0.4, 0.4, 0.4, 1.0}; Zeus::CColor m_tbBg = {0.4, 0.4, 0.4, 1.0};
@ -38,10 +39,12 @@ class ThemeData
Zeus::CColor m_textfield1Hover = {0.6425, 0.6425, 0.6425, 1.0}; Zeus::CColor m_textfield1Hover = {0.6425, 0.6425, 0.6425, 1.0};
Zeus::CColor m_textfield2Disabled = {0.7823, 0.7823, 0.7823, 0.5}; Zeus::CColor m_textfield2Disabled = {0.7823, 0.7823, 0.7823, 0.5};
Zeus::CColor m_textfield1Disabled = {0.5725, 0.5725, 0.5725, 0.5}; Zeus::CColor m_textfield1Disabled = {0.5725, 0.5725, 0.5725, 0.5};
Zeus::CColor m_textfieldSelection = {0.2725, 0.2725, 0.2725, 1.0};
public: public:
virtual const Zeus::CColor& uiText() const {return m_uiText;} virtual const Zeus::CColor& uiText() const {return m_uiText;}
virtual const Zeus::CColor& fieldText() const {return m_fieldText;} virtual const Zeus::CColor& fieldText() const {return m_fieldText;}
virtual const Zeus::CColor& selectedFieldText() const {return m_selectedFieldText;}
virtual const Zeus::CColor& viewportBackground() const {return m_vpBg;} virtual const Zeus::CColor& viewportBackground() const {return m_vpBg;}
virtual const Zeus::CColor& toolbarBackground() const {return m_tbBg;} virtual const Zeus::CColor& toolbarBackground() const {return m_tbBg;}
@ -66,6 +69,7 @@ public:
virtual const Zeus::CColor& textfield2Hover() const {return m_textfield2Hover;} virtual const Zeus::CColor& textfield2Hover() const {return m_textfield2Hover;}
virtual const Zeus::CColor& textfield1Disabled() const {return m_textfield1Disabled;} virtual const Zeus::CColor& textfield1Disabled() const {return m_textfield1Disabled;}
virtual const Zeus::CColor& textfield2Disabled() const {return m_textfield2Disabled;} virtual const Zeus::CColor& textfield2Disabled() const {return m_textfield2Disabled;}
virtual const Zeus::CColor& textfieldSelection() const {return m_textfieldSelection;}
}; };
class ViewResources class ViewResources

View File

@ -5,8 +5,8 @@ namespace Specter
{ {
#define BROWSER_MARGIN 30 #define BROWSER_MARGIN 30
#define BROWSER_MIN_WIDTH 100 #define BROWSER_MIN_WIDTH 600
#define BROWSER_MIN_HEIGHT 100 #define BROWSER_MIN_HEIGHT 300
static std::vector<HECL::SystemString> PathComponents(const HECL::SystemString& path) static std::vector<HECL::SystemString> PathComponents(const HECL::SystemString& path)
{ {
@ -40,7 +40,10 @@ static std::vector<HECL::SystemString> PathComponents(const HECL::SystemString&
} }
FileBrowser::FileBrowser(ViewResources& res, View& parentView, const HECL::SystemString& initialPath) FileBrowser::FileBrowser(ViewResources& res, View& parentView, const HECL::SystemString& initialPath)
: ModalWindow(res, parentView), : ModalWindow(res, parentView, RectangleConstraint(BROWSER_MIN_WIDTH * res.pixelFactor(),
BROWSER_MIN_HEIGHT * res.pixelFactor(),
RectangleConstraint::Test::Minimum,
RectangleConstraint::Test::Minimum)),
m_comps(PathComponents(initialPath)), m_comps(PathComponents(initialPath)),
m_fileFieldBind(*this) m_fileFieldBind(*this)
{ {
@ -102,20 +105,34 @@ void FileBrowser::mouseLeave(const boo::SWindowCoord& coord)
m_fileField.mouseLeave(coord); m_fileField.mouseLeave(coord);
} }
void FileBrowser::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
{
}
void FileBrowser::touchDown(const boo::STouchCoord& coord, uintptr_t tid)
{
}
void FileBrowser::touchUp(const boo::STouchCoord& coord, uintptr_t tid)
{
}
void FileBrowser::touchMove(const boo::STouchCoord& coord, uintptr_t tid)
{
}
void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
{ {
ModalWindow::resized(root, root); ModalWindow::resized(root, root);
float pf = rootView().viewRes().pixelFactor(); float pf = rootView().viewRes().pixelFactor();
boo::SWindowRect centerRect = sub; boo::SWindowRect centerRect = subRect();
centerRect.size[0] = std::max(root.size[0] - BROWSER_MARGIN * 2 * pf, BROWSER_MIN_WIDTH * pf);
centerRect.size[1] = std::max(root.size[1] - BROWSER_MARGIN * 2 * pf, BROWSER_MIN_HEIGHT * pf);
centerRect.location[0] = root.size[0] / 2 - (centerRect.size[0] / 2.0); centerRect.location[0] = root.size[0] / 2 - (centerRect.size[0] / 2.0);
centerRect.location[1] = root.size[1] / 2 - (centerRect.size[1] / 2.0); centerRect.location[1] = root.size[1] / 2 - (centerRect.size[1] / 2.0);
boo::SWindowRect pathRect = centerRect; boo::SWindowRect pathRect = centerRect;
pathRect.location[0] += 10 * pf; pathRect.location[0] += 25 * pf;
pathRect.location[1] += pathRect.size[1] - 20 * pf; pathRect.location[1] += pathRect.size[1] - 50 * pf;
for (PathButton& b : m_pathButtons) for (PathButton& b : m_pathButtons)
{ {
pathRect.size[0] = b.m_button.m_view->nominalWidth(); pathRect.size[0] = b.m_button.m_view->nominalWidth();
@ -124,13 +141,19 @@ void FileBrowser::resized(const boo::SWindowRect& root, const boo::SWindowRect&
pathRect.location[0] += pathRect.size[0] + 2; pathRect.location[0] += pathRect.size[0] + 2;
} }
pathRect.location[0] = centerRect.location[0] + 10 * pf; pathRect.location[0] = centerRect.location[0] + 25 * pf;
pathRect.location[1] -= 25 * pf; pathRect.location[1] -= 25 * pf;
pathRect.size[0] = centerRect.size[0] - 20 * pf; pathRect.size[0] = centerRect.size[0] - 50 * pf;
pathRect.size[1] = m_fileField.m_view->nominalHeight(); pathRect.size[1] = m_fileField.m_view->nominalHeight();
m_fileField.m_view->resized(root, pathRect); m_fileField.m_view->resized(root, pathRect);
} }
void FileBrowser::think()
{
ModalWindow::think();
m_fileField.m_view->think();
}
void FileBrowser::draw(boo::IGraphicsCommandQueue* gfxQ) void FileBrowser::draw(boo::IGraphicsCommandQueue* gfxQ)
{ {
ModalWindow::draw(gfxQ); ModalWindow::draw(gfxQ);

View File

@ -168,10 +168,9 @@ void ModalWindow::setFillColors(float t)
m_cornersFilled[i]->colorGlyphs(color); m_cornersFilled[i]->colorGlyphs(color);
} }
ModalWindow::ModalWindow(ViewResources& res, View& parentView, int widthConstrain, int heightConstrain) ModalWindow::ModalWindow(ViewResources& res, View& parentView, const RectangleConstraint& constraint)
: View(res, parentView), : View(res, parentView),
m_widthConstrain(widthConstrain), m_constraint(constraint),
m_heightConstrain(heightConstrain),
m_windowBg(res.themeData().splashBackground()), m_windowBg(res.themeData().splashBackground()),
m_windowBgClear(m_windowBg), m_windowBgClear(m_windowBg),
m_line1(res.themeData().splash1()), m_line1(res.themeData().splash1()),
@ -302,10 +301,10 @@ void ModalWindow::resized(const boo::SWindowRect& root, const boo::SWindowRect&
float pf = rootView().viewRes().pixelFactor(); float pf = rootView().viewRes().pixelFactor();
boo::SWindowRect centerRect = sub; boo::SWindowRect centerRect = sub;
m_width = m_widthConstrain < 0 ? root.size[0] - CONTENT_MARGIN * pf * 2 : m_widthConstrain; std::pair<int,int> constrained = m_constraint.solve(root.size[0] - CONTENT_MARGIN * pf * 2,
m_height = m_heightConstrain < 0 ? root.size[1] - CONTENT_MARGIN * pf * 2 : m_heightConstrain; root.size[1] - CONTENT_MARGIN * pf * 2);
m_width = std::max(m_width, int(WINDOW_MIN_DIM * pf)); m_width = std::max(constrained.first, int(WINDOW_MIN_DIM * pf));
m_height = std::max(m_height, int(WINDOW_MIN_DIM * pf)); m_height = std::max(constrained.second, int(WINDOW_MIN_DIM * pf));
centerRect.size[0] = m_width; centerRect.size[0] = m_width;
centerRect.size[1] = m_height; centerRect.size[1] = m_height;
centerRect.location[0] = root.size[0] / 2 - m_width / 2.0; centerRect.location[0] = root.size[0] / 2 - m_width / 2.0;

View File

@ -80,44 +80,79 @@ void RootView::mouseLeave(const boo::SWindowCoord& coord)
void RootView::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll) void RootView::scroll(const boo::SWindowCoord& coord, const boo::SScrollDelta& scroll)
{ {
if (m_view)
m_view->scroll(coord, scroll);
} }
void RootView::touchDown(const boo::STouchCoord& coord, uintptr_t tid) void RootView::touchDown(const boo::STouchCoord& coord, uintptr_t tid)
{ {
if (m_view)
m_view->touchDown(coord, tid);
} }
void RootView::touchUp(const boo::STouchCoord& coord, uintptr_t tid) void RootView::touchUp(const boo::STouchCoord& coord, uintptr_t tid)
{ {
if (m_view)
m_view->touchUp(coord, tid);
} }
void RootView::touchMove(const boo::STouchCoord& coord, uintptr_t tid) void RootView::touchMove(const boo::STouchCoord& coord, uintptr_t tid)
{ {
if (m_view)
m_view->touchMove(coord, tid);
} }
void RootView::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat) void RootView::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
{ {
if (m_view)
m_view->charKeyDown(charCode, mods, isRepeat);
if (m_activeTextView)
m_activeTextView->charKeyDown(charCode, mods, isRepeat);
} }
void RootView::charKeyUp(unsigned long charCode, boo::EModifierKey mods) void RootView::charKeyUp(unsigned long charCode, boo::EModifierKey mods)
{ {
if (m_view)
m_view->charKeyUp(charCode, mods);
if (m_activeTextView)
m_activeTextView->charKeyUp(charCode, mods);
} }
void RootView::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat) void RootView::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat)
{ {
if (key == boo::ESpecialKey::Enter && (mods & boo::EModifierKey::Alt) != boo::EModifierKey::None) if (key == boo::ESpecialKey::Enter && (mods & boo::EModifierKey::Alt) != boo::EModifierKey::None)
{
m_window->setFullscreen(!m_window->isFullscreen()); m_window->setFullscreen(!m_window->isFullscreen());
return;
}
if (m_view)
m_view->specialKeyDown(key, mods, isRepeat);
if (m_activeTextView)
m_activeTextView->specialKeyDown(key, mods, isRepeat);
} }
void RootView::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods) void RootView::specialKeyUp(boo::ESpecialKey key, boo::EModifierKey mods)
{ {
if (m_view)
m_view->specialKeyUp(key, mods);
if (m_activeTextView)
m_activeTextView->specialKeyUp(key, mods);
} }
void RootView::modKeyDown(boo::EModifierKey mod, bool isRepeat) void RootView::modKeyDown(boo::EModifierKey mod, bool isRepeat)
{ {
if (m_view)
m_view->modKeyDown(mod, isRepeat);
if (m_activeTextView)
m_activeTextView->modKeyDown(mod, isRepeat);
} }
void RootView::modKeyUp(boo::EModifierKey mod) void RootView::modKeyUp(boo::EModifierKey mod)
{ {
if (m_view)
m_view->modKeyUp(mod);
if (m_activeTextView)
m_activeTextView->modKeyUp(mod);
} }
View* RootView::setContentView(View* view) View* RootView::setContentView(View* view)

View File

@ -8,7 +8,7 @@ namespace Specter
TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBind) TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBind)
: Control(res, parentView, strBind) : Control(res, parentView, strBind)
{ {
m_bVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), 28); m_bVertsBuf = res.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(SolidShaderVert), 32);
if (!res.m_viewRes.m_texVtxFmt) if (!res.m_viewRes.m_texVtxFmt)
{ {
@ -40,7 +40,9 @@ TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBi
m_verts[4].m_color = rootView().themeData().textfield2Inactive(); m_verts[4].m_color = rootView().themeData().textfield2Inactive();
for (int i=5 ; i<28 ; ++i) for (int i=5 ; i<28 ; ++i)
m_verts[i].m_color = res.themeData().textfield2Inactive(); m_verts[i].m_color = res.themeData().textfield2Inactive();
m_bVertsBuf->load(m_verts, sizeof(SolidShaderVert) * 28); for (int i=28 ; i<32 ; ++i)
m_verts[i].m_color = res.themeData().textfieldSelection();
m_bVertsBuf->load(m_verts, sizeof(m_verts));
m_text.reset(new TextView(res, *this, res.m_mainFont, TextView::Alignment::Left, 1024)); m_text.reset(new TextView(res, *this, res.m_mainFont, TextView::Alignment::Left, 1024));
setText("Test"); setText("Test");
@ -48,8 +50,13 @@ TextField::TextField(ViewResources& res, View& parentView, IStringBinding* strBi
void TextField::setText(const std::string& str) void TextField::setText(const std::string& str)
{ {
m_textStr = str; clearSelectionRange();
m_text->typesetGlyphs(str, rootView().themeData().fieldText()); auto it = str.cbegin();
for (; it != str.cend() ; ++it)
if (*it == '\n')
break;
m_textStr.assign(str.cbegin(), it);
m_text->typesetGlyphs(m_textStr, rootView().themeData().fieldText());
} }
void TextField::setInactive() void TextField::setInactive()
@ -59,7 +66,7 @@ void TextField::setInactive()
m_verts[2].m_color = rootView().themeData().textfield1Inactive(); m_verts[2].m_color = rootView().themeData().textfield1Inactive();
m_verts[3].m_color = rootView().themeData().textfield2Inactive(); m_verts[3].m_color = rootView().themeData().textfield2Inactive();
m_verts[4].m_color = rootView().themeData().textfield2Inactive(); m_verts[4].m_color = rootView().themeData().textfield2Inactive();
m_bVertsBuf->load(m_verts, sizeof(SolidShaderVert) * 28); m_bVertsBuf->load(m_verts, sizeof(m_verts));
} }
void TextField::setHover() void TextField::setHover()
@ -69,7 +76,7 @@ void TextField::setHover()
m_verts[2].m_color = rootView().themeData().textfield1Hover(); m_verts[2].m_color = rootView().themeData().textfield1Hover();
m_verts[3].m_color = rootView().themeData().textfield2Hover(); m_verts[3].m_color = rootView().themeData().textfield2Hover();
m_verts[4].m_color = rootView().themeData().textfield2Hover(); m_verts[4].m_color = rootView().themeData().textfield2Hover();
m_bVertsBuf->load(m_verts, sizeof(SolidShaderVert) * 28); m_bVertsBuf->load(m_verts, sizeof(m_verts));
} }
void TextField::setDisabled() void TextField::setDisabled()
@ -79,11 +86,19 @@ void TextField::setDisabled()
m_verts[2].m_color = rootView().themeData().textfield1Disabled(); m_verts[2].m_color = rootView().themeData().textfield1Disabled();
m_verts[3].m_color = rootView().themeData().textfield2Disabled(); m_verts[3].m_color = rootView().themeData().textfield2Disabled();
m_verts[4].m_color = rootView().themeData().textfield2Disabled(); m_verts[4].m_color = rootView().themeData().textfield2Disabled();
m_bVertsBuf->load(m_verts, sizeof(SolidShaderVert) * 28); m_bVertsBuf->load(m_verts, sizeof(m_verts));
} }
void TextField::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) void TextField::mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
{ {
if (!m_active)
{
rootView().setActiveTextView(this);
if (!m_selectionCount)
setSelectionRange(0, m_textStr.size());
}
else
setCursorPos(m_text->reverseSelectGlyph(coord.pixel[0] - m_text->subRect().location[0]));
} }
void TextField::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod) void TextField::mouseUp(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mod)
@ -97,11 +112,120 @@ void TextField::mouseMove(const boo::SWindowCoord& coord)
void TextField::mouseEnter(const boo::SWindowCoord& coord) void TextField::mouseEnter(const boo::SWindowCoord& coord)
{ {
setHover(); setHover();
rootView().window()->setCursor(boo::EMouseCursor::IBeam);
} }
void TextField::mouseLeave(const boo::SWindowCoord& coord) void TextField::mouseLeave(const boo::SWindowCoord& coord)
{ {
setInactive(); setInactive();
rootView().window()->setCursor(boo::EMouseCursor::Pointer);
}
void TextField::charKeyDown(unsigned long charCode, boo::EModifierKey mods, bool isRepeat)
{
if (m_selectionCount)
{
std::string newStr(m_textStr.cbegin(), m_textStr.cbegin() + m_selectionStart);
utf8proc_uint8_t theChar[5] = {};
utf8proc_ssize_t sz = utf8proc_encode_char(charCode, theChar);
if (sz > 0)
newStr += (char*)theChar;
newStr.append(m_textStr.cbegin() + m_selectionStart + m_selectionCount, m_textStr.cend());
setText(newStr);
}
else
{
std::string newStr(m_textStr.cbegin(), m_textStr.cbegin() + m_cursorPos);
utf8proc_uint8_t theChar[5] = {};
utf8proc_ssize_t sz = utf8proc_encode_char(charCode, theChar);
if (sz > 0)
newStr += (char*)theChar;
newStr.append(m_textStr.cbegin() + m_cursorPos, m_textStr.cend());
setText(newStr);
}
}
void TextField::specialKeyDown(boo::ESpecialKey key, boo::EModifierKey mods, bool isRepeat)
{
if (key == boo::ESpecialKey::Left)
{
if (m_selectionCount)
m_cursorPos = m_selectionStart;
setCursorPos(m_cursorPos==0 ? 0 : (m_cursorPos-1));
}
else if (key == boo::ESpecialKey::Right)
{
if (m_selectionCount)
m_cursorPos = m_selectionStart + m_selectionCount - 1;
setCursorPos(m_cursorPos+1);
}
}
void TextField::think()
{
++m_cursorFrames;
}
void TextField::setActive(bool active)
{
m_active = active;
if (!active)
clearSelectionRange();
}
void TextField::setCursorPos(size_t pos)
{
clearSelectionRange();
m_cursorPos = std::min(pos, m_textStr.size());
m_cursorFrames = 0;
float pf = rootView().viewRes().pixelFactor();
int offset1 = 4 * pf + m_text->queryReverseAdvance(m_cursorPos);
int offset2 = offset1 + 2 * pf;
m_verts[28].m_pos.assign(offset1, 18 * pf, 0);
m_verts[29].m_pos.assign(offset1, 4 * pf, 0);
m_verts[30].m_pos.assign(offset2, 18 * pf, 0);
m_verts[31].m_pos.assign(offset2, 4 * pf, 0);
m_bVertsBuf->load(m_verts, sizeof(m_verts));
}
void TextField::setSelectionRange(size_t start, size_t count)
{
m_selectionStart = std::min(start, m_textStr.size()-1);
m_selectionCount = std::min(count, m_textStr.size()-m_selectionStart);
ViewResources& res = rootView().viewRes();
float pf = res.pixelFactor();
int offset1 = 5 * pf;
int offset2 = offset1;
std::vector<TextView::RenderGlyph>& glyphs = m_text->accessGlyphs();
offset1 += glyphs[m_selectionStart].m_pos[0][0];
offset2 += glyphs[m_selectionStart+m_selectionCount-1].m_pos[2][0];
for (size_t i=0 ; i<glyphs.size() ; ++i)
{
if (i >= m_selectionStart && i< m_selectionStart + m_selectionCount)
glyphs[i].m_color = rootView().themeData().selectedFieldText();
else
glyphs[i].m_color = rootView().themeData().fieldText();
}
m_text->updateGlyphs();
m_verts[28].m_pos.assign(offset1, 18 * pf, 0);
m_verts[29].m_pos.assign(offset1, 4 * pf, 0);
m_verts[30].m_pos.assign(offset2, 18 * pf, 0);
m_verts[31].m_pos.assign(offset2, 4 * pf, 0);
m_bVertsBuf->load(m_verts, sizeof(m_verts));
}
void TextField::clearSelectionRange()
{
m_selectionStart = 0;
m_selectionCount = 0;
std::vector<TextView::RenderGlyph>& glyphs = m_text->accessGlyphs();
for (size_t i=0 ; i<glyphs.size() ; ++i)
glyphs[i].m_color = rootView().themeData().fieldText();
m_text->updateGlyphs();
} }
void TextField::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub) void TextField::resized(const boo::SWindowRect& root, const boo::SWindowRect& sub)
@ -146,7 +270,7 @@ void TextField::resized(const boo::SWindowRect& root, const boo::SWindowRect& su
m_verts[26].m_pos.assign(width+1, 1, 0); m_verts[26].m_pos.assign(width+1, 1, 0);
m_verts[27].m_pos.assign(width+1, 0, 0); m_verts[27].m_pos.assign(width+1, 0, 0);
m_bVertsBuf->load(m_verts, sizeof(SolidShaderVert) * 28); m_bVertsBuf->load(m_verts, sizeof(m_verts));
m_nomWidth = width; m_nomWidth = width;
m_nomHeight = height; m_nomHeight = height;
@ -163,6 +287,16 @@ void TextField::draw(boo::IGraphicsCommandQueue* gfxQ)
gfxQ->setShaderDataBinding(m_bShaderBinding); gfxQ->setShaderDataBinding(m_bShaderBinding);
gfxQ->setDrawPrimitive(boo::Primitive::TriStrips); gfxQ->setDrawPrimitive(boo::Primitive::TriStrips);
gfxQ->draw(0, 28); gfxQ->draw(0, 28);
if (m_active)
{
if (!m_selectionCount)
{
if (m_cursorFrames % 60 < 30)
gfxQ->draw(28, 4);
}
else
gfxQ->draw(28, 4);
}
m_text->draw(gfxQ); m_text->draw(gfxQ);
} }

View File

@ -355,6 +355,8 @@ void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& default
m_glyphs.reserve(str.size()); m_glyphs.reserve(str.size());
m_glyphDims.clear(); m_glyphDims.clear();
m_glyphDims.reserve(str.size()); m_glyphDims.reserve(str.size());
m_glyphAdvs.clear();
m_glyphAdvs.reserve(str.size());
int adv = 0; int adv = 0;
while (rem) while (rem)
@ -378,6 +380,7 @@ void TextView::typesetGlyphs(const std::string& str, const Zeus::CColor& default
adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas); adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas);
m_glyphs.emplace_back(adv, *glyph, defaultColor); m_glyphs.emplace_back(adv, *glyph, defaultColor);
m_glyphDims.emplace_back(glyph->m_width, glyph->m_height); m_glyphDims.emplace_back(glyph->m_width, glyph->m_height);
m_glyphAdvs.push_back(adv);
lCh = glyph->m_glyphIdx; lCh = glyph->m_glyphIdx;
rem -= sz; rem -= sz;
@ -422,6 +425,8 @@ void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaul
m_glyphs.reserve(str.size()); m_glyphs.reserve(str.size());
m_glyphDims.clear(); m_glyphDims.clear();
m_glyphDims.reserve(str.size()); m_glyphDims.reserve(str.size());
m_glyphAdvs.clear();
m_glyphAdvs.reserve(str.size());
int adv = 0; int adv = 0;
for (wchar_t ch : str) for (wchar_t ch : str)
@ -437,6 +442,7 @@ void TextView::typesetGlyphs(const std::wstring& str, const Zeus::CColor& defaul
adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas); adv += DoKern(m_fontAtlas.lookupKern(lCh, glyph->m_glyphIdx), m_fontAtlas);
m_glyphs.emplace_back(adv, *glyph, defaultColor); m_glyphs.emplace_back(adv, *glyph, defaultColor);
m_glyphDims.emplace_back(glyph->m_width, glyph->m_height); m_glyphDims.emplace_back(glyph->m_width, glyph->m_height);
m_glyphAdvs.push_back(adv);
lCh = glyph->m_glyphIdx; lCh = glyph->m_glyphIdx;
@ -516,5 +522,32 @@ std::pair<int,int> TextView::queryGlyphDimensions(size_t pos) const
return m_glyphDims[pos]; return m_glyphDims[pos];
} }
size_t TextView::reverseSelectGlyph(int x) const
{
size_t ret = 0;
size_t idx = 1;
int minDelta = abs(x);
for (int adv : m_glyphAdvs)
{
int thisDelta = abs(adv-x);
if (thisDelta < minDelta)
{
minDelta = thisDelta;
ret = idx;
}
++idx;
}
return ret;
}
int TextView::queryReverseAdvance(size_t idx) const
{
if (idx > m_glyphAdvs.size())
Log.report(LogVisor::FatalError,
"TextView::queryReverseGlyph(%" PRISize ") out of inclusive bounds: %" PRISize,
idx, m_glyphAdvs.size());
if (!idx) return 0;
return m_glyphAdvs[idx-1];
}
} }