// 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 ///TODO: For now is unable to display different color in the history, it needs a RichTextDrawer to do so namespace Ndk { namespace { const char s_inputPrefix[] = "> "; constexpr std::size_t s_inputPrefixSize = Nz::CountOf(s_inputPrefix) - 1; } Console::Console(World& world, const Nz::Vector2f& size, Nz::LuaInstance& instance) : m_historyPosition(0), m_defaultFont(Nz::Font::GetDefault()), m_instance(instance), m_size(size), m_opened(false), m_characterSize(24) { Nz::MaterialRef backgroundMaterial = Nz::Material::New(); backgroundMaterial->Enable(Nz::RendererParameter_Blend, true); backgroundMaterial->Enable(Nz::RendererParameter_DepthBuffer, false); backgroundMaterial->SetDstBlend(Nz::BlendFunc_InvSrcAlpha); backgroundMaterial->SetSrcBlend(Nz::BlendFunc_SrcAlpha); // History bakckground m_historyBackgroundSprite = Nz::Sprite::New(); m_historyBackgroundSprite->SetColor(Nz::Color(80, 80, 160, 128)); m_historyBackgroundSprite->SetMaterial(backgroundMaterial); m_historyBackground = world.CreateEntity(); m_historyBackground->Enable(m_opened); m_historyBackground->AddComponent().Attach(m_historyBackgroundSprite, -1); m_historyBackground->AddComponent().SetParent(this); // History m_historyDrawer.SetCharacterSize(m_characterSize); m_historyDrawer.SetColor(Nz::Color(200, 200, 200)); m_historyDrawer.SetFont(m_defaultFont); m_historyTextSprite = Nz::TextSprite::New(); m_history = world.CreateEntity(); m_history->Enable(m_opened); m_history->AddComponent().Attach(m_historyTextSprite); Ndk::NodeComponent& historyNode = m_history->AddComponent(); historyNode.SetParent(this); // Input background m_inputBackgroundSprite = Nz::Sprite::New(); m_inputBackgroundSprite->SetColor(Nz::Color(255, 255, 255, 200)); m_inputBackgroundSprite->SetMaterial(backgroundMaterial); m_inputBackground = world.CreateEntity(); m_inputBackground->Enable(m_opened); m_inputBackground->AddComponent().Attach(m_inputBackgroundSprite, -1); m_inputBackground->AddComponent().SetParent(this); // Input m_inputDrawer.SetColor(Nz::Color::Black); m_inputDrawer.SetCharacterSize(m_characterSize); m_inputDrawer.SetFont(m_defaultFont); m_inputDrawer.SetText(s_inputPrefix); m_inputTextSprite = Nz::TextSprite::New(); m_inputTextSprite->Update(m_inputDrawer); m_input = world.CreateEntity(); m_input->Enable(m_opened); m_input->AddComponent().Attach(m_inputTextSprite); Ndk::NodeComponent& inputNode = m_input->AddComponent(); inputNode.SetParent(this); Layout(); } void Console::AddLine(const Nz::String& text, const Nz::Color& color) { AddLineInternal(text, color); RefreshHistory(); } void Console::Clear() { m_historyLines.clear(); RefreshHistory(); } void Console::SendCharacter(char32_t character) { switch (character) { case '\b': { Nz::String input = m_inputDrawer.GetText(); if (input.GetLength() <= s_inputPrefixSize) // Prevent removal of the input prefix return; // Ignore if no user character is there input.Resize(-1, Nz::String::HandleUtf8); m_inputDrawer.SetText(input); break; } case '\r': case '\n': ExecuteInput(); break; default: { if (Nz::Unicode::GetCategory(character) == Nz::Unicode::Category_Other_Control) return; m_inputDrawer.AppendText(Nz::String::Unicode(character)); break; } } m_inputTextSprite->Update(m_inputDrawer); } void Console::SendEvent(Nz::WindowEvent event) { switch (event.type) { case Nz::WindowEventType_TextEntered: SendCharacter(event.text.character); break; case Nz::WindowEventType_KeyPressed: { switch (event.key.code) { case Nz::Keyboard::Down: case Nz::Keyboard::Up: if (event.key.code == Nz::Keyboard::Up) m_historyPosition = std::min(m_commandHistory.size(), m_historyPosition + 1); else { if (m_historyPosition > 1) m_historyPosition--; else if (m_historyPosition == 0) m_historyPosition = 1; } Nz::String text = m_commandHistory[m_commandHistory.size() - m_historyPosition]; m_inputDrawer.SetText(s_inputPrefix + text); m_inputTextSprite->Update(m_inputDrawer); break; } break; } } } void Console::SetCharacterSize(unsigned int size) { m_characterSize = size; m_historyDrawer.SetCharacterSize(m_characterSize); m_historyTextSprite->Update(m_historyDrawer); m_inputDrawer.SetCharacterSize(m_characterSize); m_inputTextSprite->Update(m_inputDrawer); Layout(); } void Console::SetSize(const Nz::Vector2f& size) { m_size = size; m_historyBackgroundSprite->SetSize(m_size); Layout(); } void Console::SetTextFont(Nz::FontRef font) { NazaraAssert(font && font->IsValid(), "Invalid font"); m_defaultFont = std::move(font); m_historyDrawer.SetFont(m_defaultFont); m_inputDrawer.SetFont(m_defaultFont); Layout(); } void Console::Show(bool show) { if (m_opened != show) { m_historyBackground->Enable(show); m_history->Enable(show); m_input->Enable(show); m_inputBackground->Enable(show); m_opened = show; } } void Console::AddLineInternal(const Nz::String& text, const Nz::Color& color) { m_historyLines.emplace_back(Line{color, text}); } void Console::ExecuteInput() { Nz::String input = m_inputDrawer.GetText(); Nz::String inputCmd = input.SubString(s_inputPrefixSize);; m_inputDrawer.SetText(s_inputPrefix); if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd) m_commandHistory.push_back(inputCmd); m_historyPosition = 0; AddLineInternal(input); //< With the input prefix if (!m_instance.Execute(inputCmd)) AddLineInternal(m_instance.GetLastError(), Nz::Color::Red); RefreshHistory(); } void Console::Layout() { unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight; Ndk::NodeComponent& inputNode = m_input->GetComponent(); inputNode.SetPosition(0.f, m_size.y - lineHeight - 5.f); float historyHeight = m_size.y - lineHeight - 5.f - 2.f; m_historyBackgroundSprite->SetSize(m_size.x, historyHeight); m_maxHistoryLines = static_cast(std::ceil(historyHeight / lineHeight)); Ndk::NodeComponent& historyNode = m_history->GetComponent(); historyNode.SetPosition(0.f, historyHeight - m_maxHistoryLines * lineHeight); Ndk::NodeComponent& inputBackgroundNode = m_inputBackground->GetComponent(); inputBackgroundNode.SetPosition(0.f, historyHeight + 2.f); m_inputBackgroundSprite->SetSize(m_size.x, m_size.y - historyHeight); } void Console::RefreshHistory() { m_historyDrawer.Clear(); auto it = m_historyLines.end(); if (m_historyLines.size() > m_maxHistoryLines) it -= m_maxHistoryLines; else it = m_historyLines.begin(); for (unsigned int i = 0; i < m_maxHistoryLines; ++i) { if (m_maxHistoryLines - i <= m_historyLines.size() && it != m_historyLines.end()) { m_historyDrawer.AppendText(it->text); ++it; } m_historyDrawer.AppendText(Nz::String('\n')); } m_historyTextSprite->Update(m_historyDrawer); } }