improve text input
This commit is contained in:
@@ -570,6 +570,14 @@
|
||||
<ItemGroup>
|
||||
<Image Include="icon.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Xsd Include="extra\layout.xsd">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</Xsd>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
|
||||
@@ -739,4 +739,9 @@
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Xsd Include="extra\layout.xsd">
|
||||
<Filter>extras</Filter>
|
||||
</Xsd>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<root>
|
||||
<root
|
||||
xmlns="http://panopainter.com/layout.xsd"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
>
|
||||
|
||||
<!--brush icon-->
|
||||
<layout id="tpl-brush-icon">
|
||||
@@ -60,15 +63,15 @@
|
||||
<node grow="1">
|
||||
<node dir="row" height="30" align="center" grow="1">
|
||||
<text text="Author" margin="0 5 0 0"/>
|
||||
<text-input align="center" pad="5" grow="1" height="30" color=".3"/>
|
||||
<text-input pad="5" grow="1" height="30" color=".3"/>
|
||||
</node>
|
||||
<node dir="row" height="30" align="center" grow="1">
|
||||
<text text="URL" margin="0 5 0 0"/>
|
||||
<text-input align="center" pad="5" grow="1" height="30" color=".3"/>
|
||||
<text-input pad="5" grow="1" height="30" color=".3"/>
|
||||
</node>
|
||||
<node dir="row" height="30" align="center" grow="1">
|
||||
<node dir="row" height="80" align="center" grow="1">
|
||||
<text text="Description" margin="0 5 0 0"/>
|
||||
<text-input align="center" pad="5" grow="1" height="30" color=".3"/>
|
||||
<text-input text-vertical-align="top" pad="5" grow="1" min-height="80" color=".3" multiline="1"/>
|
||||
</node>
|
||||
</node>
|
||||
</border>
|
||||
@@ -2009,5 +2012,6 @@ Here's a list of what's available in this release.
|
||||
</border>
|
||||
-->
|
||||
<!--<ref id="dialog-brush-upload"/>-->
|
||||
</layout>
|
||||
|
||||
</layout>
|
||||
</root>
|
||||
|
||||
352
extra/layout.xsd
Normal file
352
extra/layout.xsd
Normal file
@@ -0,0 +1,352 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema id="layout"
|
||||
targetNamespace="http://panopainter.com/layout.xsd"
|
||||
elementFormDefault="qualified"
|
||||
xmlns="http://panopainter.com/layout.xsd"
|
||||
xmlns:mstns="http://panopainter.com/layout.xsd"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
>
|
||||
<xs:element name="root">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" ref="layout"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="layout">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="button-custom"/>
|
||||
<xs:element ref="popup-menu"/>
|
||||
</xs:choice>
|
||||
<xs:element minOccurs="0" ref="canvas"/>
|
||||
<xs:element minOccurs="0" ref="node"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="popup-menu">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="button-custom"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="border-color" use="required" type="xs:decimal"/>
|
||||
<xs:attribute name="color" use="required"/>
|
||||
<xs:attribute name="dir" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="position" use="required"/>
|
||||
<xs:attribute name="positioning" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="thickness" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="width" use="required" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="canvas">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="grow" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="positioning" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="border">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0">
|
||||
<xs:element ref="checkbox"/>
|
||||
<xs:element ref="image"/>
|
||||
<xs:element ref="image-texture"/>
|
||||
<xs:element ref="color-quad"/>
|
||||
</xs:choice>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="button"/>
|
||||
<xs:element ref="button-custom"/>
|
||||
<xs:element ref="combobox"/>
|
||||
<xs:element ref="node"/>
|
||||
<xs:element ref="scroll"/>
|
||||
<xs:element ref="slider-h"/>
|
||||
<xs:element ref="text"/>
|
||||
<xs:element ref="text-input"/>
|
||||
<xs:element ref="colorwheel"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="border-color" type="xs:decimal"/>
|
||||
<xs:attribute name="color"/>
|
||||
<xs:attribute name="dir" type="xs:NCName"/>
|
||||
<xs:attribute name="flood-events" type="xs:integer"/>
|
||||
<xs:attribute name="grow" type="xs:decimal"/>
|
||||
<xs:attribute name="height"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="max-width" type="xs:integer"/>
|
||||
<xs:attribute name="min-height" type="xs:integer"/>
|
||||
<xs:attribute name="min-width" type="xs:integer"/>
|
||||
<xs:attribute name="mouse-capture" type="xs:boolean"/>
|
||||
<xs:attribute name="pad"/>
|
||||
<xs:attribute name="position"/>
|
||||
<xs:attribute name="positioning" type="xs:NCName"/>
|
||||
<xs:attribute name="rtl" type="xs:NCName"/>
|
||||
<xs:attribute name="shrink" type="xs:integer"/>
|
||||
<xs:attribute name="thickness" type="xs:integer"/>
|
||||
<xs:attribute name="width"/>
|
||||
<xs:attribute name="wrap" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="color-quad">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="color" use="required"/>
|
||||
<xs:attribute name="grow" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="colorwheel">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="aspect-ratio" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="button-custom">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0">
|
||||
<xs:element ref="checkbox"/>
|
||||
<xs:element ref="icon"/>
|
||||
</xs:choice>
|
||||
<xs:element minOccurs="0" ref="image"/>
|
||||
<xs:element minOccurs="0" ref="border"/>
|
||||
<xs:element minOccurs="0" ref="stroke-preview"/>
|
||||
<xs:element minOccurs="0" maxOccurs="unbounded" ref="text"/>
|
||||
<xs:element minOccurs="0" ref="combobox"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="border-color"/>
|
||||
<xs:attribute name="color"/>
|
||||
<xs:attribute name="dir" type="xs:NCName"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="os"/>
|
||||
<xs:attribute name="pad"/>
|
||||
<xs:attribute name="shrink" type="xs:integer"/>
|
||||
<xs:attribute name="thickness" type="xs:integer"/>
|
||||
<xs:attribute name="width"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="icon">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="icon" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="width" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="stroke-preview">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="node">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="button-custom"/>
|
||||
<xs:element ref="checkbox"/>
|
||||
<xs:element ref="image"/>
|
||||
<xs:element ref="image-texture"/>
|
||||
<xs:element ref="node"/>
|
||||
<xs:element ref="scroll"/>
|
||||
<xs:element ref="slider-h"/>
|
||||
<xs:element ref="text"/>
|
||||
<xs:element ref="panel-quick"/>
|
||||
</xs:choice>
|
||||
<xs:choice>
|
||||
<xs:element ref="combobox"/>
|
||||
<xs:element ref="text-input"/>
|
||||
<xs:element ref="slider-hue"/>
|
||||
<xs:element ref="slider-v"/>
|
||||
<xs:element minOccurs="0" maxOccurs="unbounded" ref="button"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="color"/>
|
||||
<xs:attribute name="dir" type="xs:NCName"/>
|
||||
<xs:attribute name="flood-events" type="xs:integer"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="max-height" type="xs:integer"/>
|
||||
<xs:attribute name="min-height" type="xs:integer"/>
|
||||
<xs:attribute name="min-width" type="xs:integer"/>
|
||||
<xs:attribute name="os"/>
|
||||
<xs:attribute name="pad"/>
|
||||
<xs:attribute name="rtl" type="xs:NCName"/>
|
||||
<xs:attribute name="shrink" type="xs:integer"/>
|
||||
<xs:attribute name="width"/>
|
||||
<xs:attribute name="wrap" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="panel-quick">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="slider-hue">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="slider-v">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="height" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="shrink" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="width" use="required" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="image">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="autosize" type="xs:integer"/>
|
||||
<xs:attribute name="height"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="mips" type="xs:boolean"/>
|
||||
<xs:attribute name="path"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="checkbox">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="height" type="xs:integer"/>
|
||||
<xs:attribute name="icon"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="width" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="image-texture">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="text"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="aspect-ratio" type="xs:integer"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="pad" type="xs:integer"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="text">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="color"/>
|
||||
<xs:attribute name="font-size" type="xs:integer"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height" type="xs:integer"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="os"/>
|
||||
<xs:attribute name="pad" type="xs:integer"/>
|
||||
<xs:attribute name="text"/>
|
||||
<xs:attribute name="text-wrap-width" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="button">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="color"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="os"/>
|
||||
<xs:attribute name="pad" type="xs:integer"/>
|
||||
<xs:attribute name="text" use="required"/>
|
||||
<xs:attribute name="width" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="scroll">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element minOccurs="0" ref="ref"/>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element ref="border"/>
|
||||
<xs:element ref="image"/>
|
||||
<xs:element ref="node"/>
|
||||
</xs:choice>
|
||||
<xs:choice>
|
||||
<xs:element ref="button"/>
|
||||
<xs:element minOccurs="0" maxOccurs="unbounded" ref="text"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="color" use="required"/>
|
||||
<xs:attribute name="dir" type="xs:NCName"/>
|
||||
<xs:attribute name="flood-events" type="xs:integer"/>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="justify" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="min-width" type="xs:integer"/>
|
||||
<xs:attribute name="mouse-capture" type="xs:boolean"/>
|
||||
<xs:attribute name="pad"/>
|
||||
<xs:attribute name="rtl" type="xs:NCName"/>
|
||||
<xs:attribute name="scroll-color" type="xs:decimal"/>
|
||||
<xs:attribute name="shrink" type="xs:integer"/>
|
||||
<xs:attribute name="wrap" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ref">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="text-input">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="align" type="xs:NCName"/>
|
||||
<xs:attribute name="color" use="required" type="xs:decimal"/>
|
||||
<xs:attribute name="grow" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="height" type="xs:integer"/>
|
||||
<xs:attribute name="id" type="xs:NCName"/>
|
||||
<xs:attribute name="min-height" type="xs:integer"/>
|
||||
<xs:attribute name="multiline" type="xs:integer"/>
|
||||
<xs:attribute name="pad" use="required" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="combobox">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="combo-list" use="required"/>
|
||||
<xs:attribute name="default" type="xs:integer"/>
|
||||
<xs:attribute name="height" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="text" type="xs:NMTOKEN"/>
|
||||
<xs:attribute name="width" use="required"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="slider-h">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="grow" type="xs:integer"/>
|
||||
<xs:attribute name="height" type="xs:integer"/>
|
||||
<xs:attribute name="id" use="required" type="xs:NCName"/>
|
||||
<xs:attribute name="margin"/>
|
||||
<xs:attribute name="value" type="xs:decimal"/>
|
||||
<xs:attribute name="width" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
@@ -113,7 +113,7 @@ void App::dialog_newdoc()
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
std::string name = dialog->input->m_string;
|
||||
std::string name = dialog->input->m_text;
|
||||
std::string path = work_path + "/" + name + ".ppi";
|
||||
|
||||
if (name.empty())
|
||||
@@ -381,7 +381,7 @@ void App::dialog_save()
|
||||
|
||||
dialog->btn_ok->on_click = [this, dialog](Node*)
|
||||
{
|
||||
std::string name = dialog->input->m_string;
|
||||
std::string name = dialog->input->m_text;
|
||||
std::string path = work_path + "/" + name + ".ppi";
|
||||
|
||||
if (name.empty())
|
||||
|
||||
36
src/font.cpp
36
src/font.cpp
@@ -83,7 +83,7 @@ std::vector<TextMesh::Token> TextMesh::tokenize(const std::string& s, const Font
|
||||
bool is_delim = std::find(delims.begin(), delims.end(), c) != delims.end();
|
||||
wrap |= is_delim; // set wrap to notify a delimiter has been reached
|
||||
// when a new non-delim char is detected, start a new token
|
||||
if (wrap && !is_delim)
|
||||
if (wrap && !is_delim && !tmp.empty())
|
||||
{
|
||||
parts.push_back(tmp);
|
||||
tmp.clear();
|
||||
@@ -130,40 +130,61 @@ bool TextMesh::create()
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextMesh::update(kFont id, const char* text)
|
||||
void TextMesh::update(kFont id, const std::string& text)
|
||||
{
|
||||
font_id = id;
|
||||
auto& f = FontManager::get(id);
|
||||
float spacing = f.bounds.w - f.bounds.y;
|
||||
float avg_width = f.bounds.z - f.bounds.x;
|
||||
cur_box = glm::vec4(0, 0, 5, spacing);
|
||||
|
||||
glm::vec2 bbmin(FLT_MAX);
|
||||
glm::vec2 bbmax(-FLT_MAX);
|
||||
if (text.empty())
|
||||
{
|
||||
bbmin = { 0, -spacing * 0.5f };
|
||||
bbmax = { 1, +spacing * 0.5f };
|
||||
}
|
||||
|
||||
if (f.chars.size())
|
||||
{
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
std::vector<glm::vec4> v;
|
||||
std::vector<GLushort> idx;
|
||||
glm::vec2 bbmin(FLT_MAX);
|
||||
glm::vec2 bbmax(-FLT_MAX);
|
||||
|
||||
std::vector<Token> parts = max_width > 0 ?
|
||||
tokenize(text, f) :
|
||||
std::vector<Token>{ Token(text, 0.f) };
|
||||
|
||||
if (parts.empty())
|
||||
{
|
||||
bbmin = { 0, -spacing * 0.5f };
|
||||
bbmax = { 1, +spacing * 0.5f };
|
||||
}
|
||||
|
||||
for (auto p : parts)
|
||||
{
|
||||
if (max_width > 0 && x + p.w > max_width * f.scale)
|
||||
if (max_width > 0 && (x + p.w) * f.scale > max_width)
|
||||
{
|
||||
x = 0;
|
||||
y += f.size * f.scale;
|
||||
y += spacing;
|
||||
}
|
||||
for (char c : p.s)
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
x = 0;
|
||||
y += f.size * f.scale;
|
||||
y += spacing;
|
||||
continue;
|
||||
}
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad((stbtt_bakedchar*)f.chars.data(), f.w, f.h, c - f.start_char, &x, &y, &q, true);
|
||||
if (max_width > 0 && x * f.scale > max_width /*font scale factor*/)
|
||||
{
|
||||
x = 0;
|
||||
y += spacing;
|
||||
}
|
||||
auto n = (int)v.size();
|
||||
v.emplace_back(q.x0 / f.scale, q.y1 / f.scale, q.s0, q.t1);
|
||||
v.emplace_back(q.x0 / f.scale, q.y0 / f.scale, q.s0, q.t0);
|
||||
@@ -177,6 +198,7 @@ void TextMesh::update(kFont id, const char* text)
|
||||
idx.push_back(n + 3);
|
||||
bbmin = glm::min(bbmin, { q.x0 / f.scale, q.y0 / f.scale });
|
||||
bbmax = glm::max(bbmax, { q.x1 / f.scale, q.y1 / f.scale });
|
||||
cur_box = glm::vec4(x, y, 5, spacing);
|
||||
}
|
||||
}
|
||||
for (auto& vi : v)
|
||||
|
||||
@@ -58,7 +58,8 @@ public:
|
||||
GLuint font_buffers[2] = {0, 0};
|
||||
kFont font_id;
|
||||
glm::vec2 bb = { 0, 0 };
|
||||
glm::vec4 cur_box;
|
||||
bool create();
|
||||
void update(kFont id, const char* text);
|
||||
void update(kFont id, const std::string& text);
|
||||
void draw();
|
||||
};
|
||||
|
||||
@@ -1036,7 +1036,7 @@ void Node::update_internal(const glm::vec2& origin, const glm::mat4& proj, float
|
||||
m_mvp = proj * pos * scale * pivot * prescale;
|
||||
m_proj = proj;
|
||||
|
||||
if (m_size != old_size || m_zoom != zoom)
|
||||
if (!glm::any(glm::isnan(m_size)) && m_size != old_size || m_zoom != zoom)
|
||||
{
|
||||
m_zoom = zoom;
|
||||
handle_resize(old_size, m_size, zoom);
|
||||
|
||||
@@ -48,6 +48,9 @@ enum class kAttribute : uint16_t
|
||||
AutoSize = const_hash("autosize"),
|
||||
MouseCapture = const_hash("mouse-capture"),
|
||||
ScrollColor = const_hash("scroll-color"),
|
||||
Multiline = const_hash("multiline"),
|
||||
TextAlign = const_hash("text-align"),
|
||||
TextVerticalAlign = const_hash("text-vertical-align"),
|
||||
};
|
||||
|
||||
enum class kWidget : uint16_t
|
||||
|
||||
@@ -43,5 +43,5 @@ void NodeDialogLayerRename::loaded()
|
||||
|
||||
std::string NodeDialogLayerRename::get_name()
|
||||
{
|
||||
return input ? input->m_string : "";
|
||||
return input ? input->m_text : "";
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ void NodeInputBox::init()
|
||||
btn_ok = m_template->find<NodeButton>("btn-ok");
|
||||
btn_ok->on_click = [&](Node*) {
|
||||
if (on_submit)
|
||||
on_submit(this, m_field_text->m_text->m_text);
|
||||
on_submit(this, m_field_text->m_text);
|
||||
};
|
||||
btn_cancel = m_template->find<NodeButton>("btn-cancel");
|
||||
btn_cancel->on_click = [&](Node*) { destroy(); };
|
||||
@@ -39,7 +39,7 @@ kEventResult NodeInputBox::handle_event(Event* e)
|
||||
break;
|
||||
case kEventType::KeyUp:
|
||||
if (ke->m_key == kKey::KeyEnter && on_submit)
|
||||
on_submit(this, m_field_text->m_text->m_text);
|
||||
on_submit(this, m_field_text->m_text);
|
||||
break;
|
||||
case kEventType::KeyChar:
|
||||
break;
|
||||
|
||||
@@ -14,7 +14,7 @@ void NodeText::clone_copy(Node* dest) const
|
||||
NodeText* n = static_cast<NodeText*>(dest);
|
||||
n->m_text_mesh.max_width = m_text_mesh.max_width;
|
||||
n->m_text_mesh.create();
|
||||
n->m_text_mesh.update(font_id, m_text.c_str());
|
||||
n->m_text_mesh.update(font_id, m_text);
|
||||
n->m_text = m_text;
|
||||
n->m_font = m_font;
|
||||
n->m_color = m_color;
|
||||
@@ -31,7 +31,7 @@ void NodeText::create()
|
||||
sprintf(font, "%s-%d", m_font.c_str(), m_font_size);
|
||||
font_id = (kFont)const_hash(font);
|
||||
m_text_mesh.create();
|
||||
m_text_mesh.update(font_id, m_text.c_str());
|
||||
m_text_mesh.update(font_id, m_text);
|
||||
SetSize(m_text_mesh.bb);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ void NodeText::set_font(kFont fontID)
|
||||
{
|
||||
font_id = fontID;
|
||||
m_text_mesh.create();
|
||||
m_text_mesh.update(font_id, m_text.c_str());
|
||||
m_text_mesh.update(font_id, m_text);
|
||||
SetSize(m_text_mesh.bb);
|
||||
}
|
||||
|
||||
@@ -116,5 +116,6 @@ void NodeText::draw()
|
||||
|
||||
void NodeText::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
|
||||
{
|
||||
m_text_mesh.update(font_id, m_text.c_str());
|
||||
if (old_size != new_size)
|
||||
m_text_mesh.update(font_id, m_text);
|
||||
}
|
||||
|
||||
@@ -14,53 +14,39 @@ void NodeTextInput::on_tick(float dt)
|
||||
timer += dt;
|
||||
|
||||
bool focus = root()->current_key_capture.get() == this;
|
||||
if (m_cursor && !focus)
|
||||
m_cursor->m_display = false;
|
||||
if (!focus)
|
||||
m_cursor_visible = false;
|
||||
m_thinkness = focus;
|
||||
|
||||
if (timer > 1.0)
|
||||
{
|
||||
timer = 0;
|
||||
if (focus && m_cursor)
|
||||
if (focus)
|
||||
{
|
||||
m_cursor->m_display = !m_cursor->m_display;
|
||||
m_cursor_visible = !m_cursor_visible;
|
||||
app_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTextInput::clone_finalize(Node* dest) const
|
||||
void NodeTextInput::clone_copy(Node* dest) const
|
||||
{
|
||||
NodeBorder::clone_copy(dest);
|
||||
NodeTextInput* n = static_cast<NodeTextInput*>(dest);
|
||||
if (n->m_children.size())
|
||||
{
|
||||
n->m_text = (NodeText*)n->m_children[0].get();
|
||||
n->m_cursor = (NodeBorder*)n->m_children[1].get();
|
||||
}
|
||||
n->m_string = m_string;
|
||||
|
||||
}
|
||||
n->m_multiline = m_multiline;
|
||||
|
||||
void NodeTextInput::init()
|
||||
{
|
||||
init_controls();
|
||||
}
|
||||
|
||||
void NodeTextInput::init_controls()
|
||||
{
|
||||
m_text = new NodeText;
|
||||
add_child(m_text);
|
||||
m_text->m_font = "arial";
|
||||
m_text->m_font_size = 11;
|
||||
m_text->m_text = "TextInput";
|
||||
m_text->create();
|
||||
m_string = "TextInput";
|
||||
YGNodeStyleSetFlexDirection(y_node, YGFlexDirectionRow);
|
||||
m_cursor = add_child<NodeBorder>();
|
||||
m_cursor->SetWidth(4);
|
||||
m_cursor->SetHeightP(100);
|
||||
m_cursor->SetMargin(0, 0, 0, 2);
|
||||
n->m_text_mesh.max_width = m_text_mesh.max_width;
|
||||
n->m_text_mesh.create();
|
||||
n->m_text_mesh.update(font_id, m_text);
|
||||
n->m_text = m_text;
|
||||
n->m_font = m_font;
|
||||
n->m_color = m_color;
|
||||
n->m_font_size = m_font_size;
|
||||
n->font_id = font_id;
|
||||
n->m_off = m_off;
|
||||
n->m_text_align_v = m_text_align_v;
|
||||
n->m_text_align_h = m_text_align_h;
|
||||
}
|
||||
|
||||
kEventResult NodeTextInput::handle_event(Event* e)
|
||||
@@ -74,15 +60,14 @@ kEventResult NodeTextInput::handle_event(Event* e)
|
||||
case kEventType::MouseUpL:
|
||||
key_capture();
|
||||
timer = 0;
|
||||
if (m_cursor)
|
||||
m_cursor->m_display = true;
|
||||
m_cursor_visible = true;
|
||||
break;
|
||||
case kEventType::KeyDown:
|
||||
//switch (ke->m_key)
|
||||
//{
|
||||
//case VK_BACK:
|
||||
// m_string.erase(m_string.end() - 1);
|
||||
// m_text->set_text(m_string.c_str());
|
||||
// m_text->set_text(m_string);
|
||||
// break;
|
||||
//default:
|
||||
// break;
|
||||
@@ -93,38 +78,42 @@ kEventResult NodeTextInput::handle_event(Event* e)
|
||||
on_return(this);
|
||||
if (ke->m_key == kKey::KeyBackspace)
|
||||
{
|
||||
if (!m_string.empty())
|
||||
if (!m_text.empty())
|
||||
{
|
||||
m_string.erase(m_string.end() - 1);
|
||||
m_text->set_text(m_string.c_str());
|
||||
m_text.erase(m_text.end() - 1);
|
||||
set_text(m_text);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kEventType::KeyChar:
|
||||
if (m_cursor)
|
||||
{
|
||||
timer = 0;
|
||||
m_cursor->m_display = true;
|
||||
}
|
||||
m_cursor_visible = true;
|
||||
if (ke->m_char == '\b' // backspace
|
||||
|| ke->m_char == 0x7f // DEL
|
||||
)
|
||||
{
|
||||
if (!m_string.empty())
|
||||
if (!m_text.empty())
|
||||
{
|
||||
m_string.erase(m_string.end() - 1);
|
||||
m_text->set_text(m_string.c_str());
|
||||
m_text.erase(m_text.end() - 1);
|
||||
set_text(m_text);
|
||||
}
|
||||
}
|
||||
else if (ke->m_char == '\n' || ke->m_char == '\r') // enter/return
|
||||
{
|
||||
if (on_return)
|
||||
if (m_multiline)
|
||||
{
|
||||
m_text += '\n';
|
||||
set_text(m_text);
|
||||
}
|
||||
else if (on_return)
|
||||
{
|
||||
on_return(this);
|
||||
}
|
||||
}
|
||||
else if (ke->m_char >= 32 && ke->m_char < (32 + 96))
|
||||
{
|
||||
m_string += (char)ke->m_char;
|
||||
m_text->set_text(m_string.c_str());
|
||||
m_text += (char)ke->m_char;
|
||||
set_text(m_text);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -136,9 +125,117 @@ kEventResult NodeTextInput::handle_event(Event* e)
|
||||
|
||||
void NodeTextInput::set_text(const std::string& s)
|
||||
{
|
||||
if (m_text)
|
||||
m_text->set_text(s.c_str());
|
||||
m_string = s;
|
||||
m_text = s;
|
||||
m_text_mesh.update(font_id, s);
|
||||
update_layout();
|
||||
}
|
||||
|
||||
void NodeTextInput::set_text_format(const char* fmt, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buffer, sizeof(buffer), fmt, args);
|
||||
va_end(args);
|
||||
m_text = buffer;
|
||||
m_text_mesh.update(font_id, buffer);
|
||||
update_layout();
|
||||
}
|
||||
|
||||
void NodeTextInput::update_layout()
|
||||
{
|
||||
auto pad = GetPadding();
|
||||
float h = GetHeight() - (pad[1] + pad[3]);
|
||||
float w = GetWidth() - (pad[0] + pad[2]);
|
||||
|
||||
if (m_text_align_v == TextAlign::Begin)
|
||||
m_off.y = pad[0];
|
||||
else if (m_text_align_v == TextAlign::Center)
|
||||
m_off.y = (h - m_text_mesh.bb.y) * 0.5f + pad[0];
|
||||
else
|
||||
m_off.y = (h - m_text_mesh.bb.y) + pad[0];
|
||||
|
||||
if (m_text_align_h == TextAlign::Begin)
|
||||
m_off.x = pad[3];
|
||||
else if (m_text_align_v == TextAlign::Center)
|
||||
m_off.x = (w - m_text_mesh.bb.x) * 0.5f + pad[3];
|
||||
else
|
||||
m_off.x = (w - m_text_mesh.bb.x) + pad[3];
|
||||
}
|
||||
|
||||
void NodeTextInput::handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom)
|
||||
{
|
||||
auto pad = GetPadding();
|
||||
m_text_mesh.max_width = m_multiline ? new_size.x - (pad[1] + pad[3]) : 0;
|
||||
update_layout();
|
||||
}
|
||||
|
||||
void NodeTextInput::create()
|
||||
{
|
||||
NodeBorder::create();
|
||||
if (!m_font.empty())
|
||||
{
|
||||
char font[64];
|
||||
sprintf(font, "%s-%d", m_font.c_str(), m_font_size);
|
||||
font_id = (kFont)const_hash(font);
|
||||
m_text_mesh.create();
|
||||
m_text_mesh.update(font_id, m_text);
|
||||
update_layout();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTextInput::draw()
|
||||
{
|
||||
NodeBorder::draw();
|
||||
|
||||
glm::mat4 pos = glm::translate(glm::vec3(glm::floor(m_pos + m_off), 0));
|
||||
ShaderManager::use(kShader::Font);
|
||||
ShaderManager::u_int(kShaderUniform::Tex, 0);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * pos);
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, m_color);
|
||||
glEnable(GL_BLEND);
|
||||
m_text_mesh.draw();
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
if (m_cursor_visible)
|
||||
{
|
||||
glDisable(GL_BLEND);
|
||||
glm::mat4 cur_pos = glm::translate(glm::vec3(m_pos + m_off + xy(m_text_mesh.cur_box), 0));
|
||||
glm::mat4 cur_scale = glm::scale(glm::vec3(zw(m_text_mesh.cur_box), 1));
|
||||
glm::mat4 cur_pivot = glm::translate(glm::vec3(0.5, 0.5, 0));
|
||||
ShaderManager::use(kShader::Color);
|
||||
ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * cur_pos * cur_scale * cur_pivot);
|
||||
ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 });
|
||||
m_plane.draw_fill();
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTextInput::parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr)
|
||||
{
|
||||
switch (ka)
|
||||
{
|
||||
case kAttribute::Multiline:
|
||||
m_multiline = attr->BoolValue();
|
||||
break;
|
||||
case kAttribute::TextAlign:
|
||||
if (strcmp(attr->Value(), "left") == 0)
|
||||
m_text_align_h = TextAlign::Begin;
|
||||
else if (strcmp(attr->Value(), "center") == 0)
|
||||
m_text_align_h = TextAlign::Center;
|
||||
else if (strcmp(attr->Value(), "right") == 0)
|
||||
m_text_align_h = TextAlign::End;
|
||||
break;
|
||||
case kAttribute::TextVerticalAlign:
|
||||
if (strcmp(attr->Value(), "top") == 0)
|
||||
m_text_align_v = TextAlign::Begin;
|
||||
else if (strcmp(attr->Value(), "center") == 0)
|
||||
m_text_align_v = TextAlign::Center;
|
||||
else if (strcmp(attr->Value(), "bottom") == 0)
|
||||
m_text_align_v = TextAlign::End;
|
||||
break;
|
||||
default:
|
||||
NodeBorder::parse_attributes(ka, attr);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeTextInput::destroy()
|
||||
|
||||
@@ -1,22 +1,44 @@
|
||||
#pragma once
|
||||
#include "node_border.h"
|
||||
#include "node_text.h"
|
||||
#include "shape.h"
|
||||
|
||||
class NodeTextInput : public NodeBorder
|
||||
{
|
||||
public:
|
||||
float timer = 0;
|
||||
NodeText* m_text;
|
||||
NodeBorder* m_cursor;
|
||||
NodeBorder* m_border;
|
||||
std::string m_string;
|
||||
public:
|
||||
enum class TextAlign
|
||||
{
|
||||
Begin,
|
||||
Center,
|
||||
End,
|
||||
};
|
||||
|
||||
TextMesh m_text_mesh;
|
||||
std::string m_text;
|
||||
std::string m_font = "arial";
|
||||
glm::vec4 m_color{ 1, 1, 1, 1 };
|
||||
int m_font_size = 11;
|
||||
kFont font_id;
|
||||
bool m_multiline = false;
|
||||
bool m_cursor_visible = false;
|
||||
glm::vec2 m_off;
|
||||
TextAlign m_text_align_v = TextAlign::Center;
|
||||
TextAlign m_text_align_h = TextAlign::Begin;
|
||||
|
||||
std::function<void(NodeTextInput*target)> on_return;
|
||||
|
||||
virtual Node* clone_instantiate() const override;
|
||||
virtual void clone_finalize(Node* dest) const override;
|
||||
virtual void init() override;
|
||||
virtual void clone_copy(Node* dest) const override;
|
||||
virtual void on_tick(float dt) override;
|
||||
virtual kEventResult handle_event(Event* e) override;
|
||||
virtual void destroy() override;
|
||||
void init_controls();
|
||||
virtual void parse_attributes(kAttribute ka, const tinyxml2::XMLAttribute* attr) override;
|
||||
virtual void draw() override;
|
||||
virtual void create() override;
|
||||
virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size, float zoom) override;
|
||||
|
||||
void set_text(const std::string& s);
|
||||
void set_text_format(const char* fmt, ...);
|
||||
void update_layout();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user