From 3d79db4c5282491d14db71d9e839ebe419be0303 Mon Sep 17 00:00:00 2001 From: Lynix Date: Fri, 28 Oct 2016 17:46:23 +0200 Subject: [PATCH] Sdk/Widgets: Add TextAreaWidget (experimental) --- SDK/include/NDK/Widgets.hpp | 3 +- SDK/include/NDK/Widgets/TextAreaWidget.hpp | 62 ++++++++ SDK/include/NDK/Widgets/TextAreaWidget.inl | 9 ++ SDK/src/NDK/Widgets/TextAreaWidget.cpp | 176 +++++++++++++++++++++ 4 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 SDK/include/NDK/Widgets/TextAreaWidget.hpp create mode 100644 SDK/include/NDK/Widgets/TextAreaWidget.inl create mode 100644 SDK/src/NDK/Widgets/TextAreaWidget.cpp diff --git a/SDK/include/NDK/Widgets.hpp b/SDK/include/NDK/Widgets.hpp index 4ab22004e..26e7f39ad 100644 --- a/SDK/include/NDK/Widgets.hpp +++ b/SDK/include/NDK/Widgets.hpp @@ -1,4 +1,4 @@ -// This file was automatically generated on 27 Oct 2016 at 18:53:37 +// This file was automatically generated on 27 Oct 2016 at 21:39:00 #pragma once @@ -7,5 +7,6 @@ #include #include +#include #endif // NDK_WIDGETS_GLOBAL_HPP diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.hpp b/SDK/include/NDK/Widgets/TextAreaWidget.hpp new file mode 100644 index 000000000..94d984bd8 --- /dev/null +++ b/SDK/include/NDK/Widgets/TextAreaWidget.hpp @@ -0,0 +1,62 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#pragma once + +#ifndef NDK_WIDGETS_TEXTAREAWIDGET_HPP +#define NDK_WIDGETS_TEXTAREAWIDGET_HPP + +#include +#include +#include +#include + +namespace Ndk +{ + class World; + + class NDK_API TextAreaWidget : public BaseWidget + { + public: + TextAreaWidget(BaseWidget* parent = nullptr); + TextAreaWidget(const TextAreaWidget&) = delete; + TextAreaWidget(TextAreaWidget&&) = default; + ~TextAreaWidget() = default; + + void AppendText(const Nz::String& text); + + //virtual TextAreaWidget* Clone() const = 0; + + std::size_t GetHoveredGlyph(float x, float y) const; + + void ResizeToContent() override; + + void SetText(const Nz::String& text); + + TextAreaWidget& operator=(const TextAreaWidget&) = delete; + TextAreaWidget& operator=(TextAreaWidget&&) = default; + + private: + void RefreshCursor(); + + void OnMouseEnter() override; + void OnMouseButtonPress(int /*x*/, int /*y*/, Nz::Mouse::Button button) override; + void OnMouseMoved(int x, int y, int deltaX, int deltaY) override; + void OnMouseExit() override; + void OnTextEntered(char32_t character, bool repeated) override; + + void Write(const Nz::String& text); + + EntityHandle m_cursorEntity; + EntityHandle m_textEntity; + Nz::SimpleTextDrawer m_drawer; + Nz::SpriteRef m_cursorSprite; + Nz::TextSpriteRef m_textSprite; + std::size_t m_cursorPosition; + }; +} + +#include + +#endif // NDK_WIDGETS_TEXTAREAWIDGET_HPP diff --git a/SDK/include/NDK/Widgets/TextAreaWidget.inl b/SDK/include/NDK/Widgets/TextAreaWidget.inl new file mode 100644 index 000000000..96b57500e --- /dev/null +++ b/SDK/include/NDK/Widgets/TextAreaWidget.inl @@ -0,0 +1,9 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include + +namespace Ndk +{ +} diff --git a/SDK/src/NDK/Widgets/TextAreaWidget.cpp b/SDK/src/NDK/Widgets/TextAreaWidget.cpp new file mode 100644 index 000000000..c85e878c9 --- /dev/null +++ b/SDK/src/NDK/Widgets/TextAreaWidget.cpp @@ -0,0 +1,176 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Development Kit" +// For conditions of distribution and use, see copyright notice in Prerequesites.hpp + +#include +#include +#include +#include +#include +#include + +namespace Ndk +{ + TextAreaWidget::TextAreaWidget(BaseWidget* parent) : + BaseWidget(parent), + m_cursorPosition(0U) + { + m_cursorSprite = Nz::Sprite::New(); + m_cursorSprite->SetColor(Nz::Color(192, 192, 192)); + m_cursorSprite->SetSize(1.f, m_drawer.GetFont()->GetSizeInfo(m_drawer.GetCharacterSize()).lineHeight); + + m_cursorEntity = CreateEntity(); + m_cursorEntity->AddComponent().Attach(m_cursorSprite, 10); + m_cursorEntity->AddComponent().SetParent(this); + m_cursorEntity->Enable(false); + + m_textSprite = Nz::TextSprite::New(); + + m_textEntity = CreateEntity(); + m_textEntity->AddComponent().Attach(m_textSprite); + m_textEntity->AddComponent().SetParent(this); + + Layout(); + } + + void TextAreaWidget::AppendText(const Nz::String& text) + { + m_drawer.AppendText(text); + + m_textSprite->Update(m_drawer); + } + + std::size_t TextAreaWidget::GetHoveredGlyph(float x, float y) const + { + std::size_t glyphCount = m_drawer.GetGlyphCount(); + if (glyphCount > 0) + { + std::size_t lineCount = m_drawer.GetLineCount(); + std::size_t line = 0U; + for (; line < lineCount - 1; ++line) + { + Nz::Rectf lineBounds = m_drawer.GetLine(line).bounds; + if (lineBounds.GetMaximum().y > y) + break; + } + + std::size_t upperLimit = (line != lineCount - 1) ? m_drawer.GetLine(line + 1).glyphIndex : glyphCount; + upperLimit = std::min(upperLimit, glyphCount); + + std::size_t i = m_drawer.GetLine(line).glyphIndex; + for (; i < upperLimit - 1; ++i) + { + Nz::Rectf bounds = m_drawer.GetGlyph(i).bounds; + if (x < bounds.x + bounds.width) + break; + } + + return i; + } + + return 0; + } + + void TextAreaWidget::ResizeToContent() + { + SetContentSize(Nz::Vector2f(m_textSprite->GetBoundingVolume().obb.localBox.GetLengths())); + } + + void TextAreaWidget::SetText(const Nz::String& text) + { + m_drawer.SetText(text); + + m_textSprite->Update(m_drawer); + } + + void TextAreaWidget::RefreshCursor() + { + std::size_t lineCount = m_drawer.GetLineCount(); + std::size_t line = 0U; + for (std::size_t i = line + 1; i < lineCount; ++i) + { + if (m_drawer.GetLine(i).glyphIndex > m_cursorPosition) + break; + + line = i; + } + + Nz::Rectf bounds = m_drawer.GetGlyph(m_cursorPosition).bounds; + m_cursorEntity->GetComponent().SetPosition(bounds.x, m_drawer.GetLine(line).bounds.y); + } + + void TextAreaWidget::OnMouseEnter() + { + m_cursorEntity->Enable(true); + } + + void TextAreaWidget::OnMouseButtonPress(int x, int y, Nz::Mouse::Button button) + { + if (button == Nz::Mouse::Left) + { + GrabKeyboard(); + + m_cursorPosition = GetHoveredGlyph(x, y); + RefreshCursor(); + } + } + + void TextAreaWidget::OnMouseMoved(int x, int y, int deltaX, int deltaY) + { + } + + void TextAreaWidget::OnMouseExit() + { + m_cursorEntity->Enable(false); + } + + void TextAreaWidget::OnTextEntered(char32_t character, bool /*repeated*/) + { + switch (character) + { + case '\b': + { + Nz::String text = m_drawer.GetText(); + + text.Resize(-1, Nz::String::HandleUtf8); + m_drawer.SetText(text); + + m_textSprite->Update(m_drawer); + break; + } + + case '\r': + case '\n': + Write(Nz::String('\n')); + break; + + default: + { + if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control) + break; + + Write(Nz::String::Unicode(character)); + break; + } + } + } + + void TextAreaWidget::Write(const Nz::String& text) + { + if (m_cursorPosition >= m_drawer.GetGlyphCount()) + { + AppendText(text); + m_cursorPosition = m_drawer.GetGlyphCount(); + } + else + { + Nz::String currentText = m_drawer.GetText(); + currentText.Insert(m_cursorPosition, text); + SetText(currentText); + + m_cursorPosition += text.GetSize(); + } + + RefreshCursor(); + } +}