// Copyright (C) 2017 Jérôme Leclercq // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequisites.hpp #include #include #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; } /*! * \ingroup NDK * \class Ndk::Console * \brief NDK class that represents a console to help development with Lua scripting */ /*! * \brief Constructs a Console object with a world to interact with * * \param world World to interact with * \param size (Width, Height) of the console * \param instance Lua instance that will interact with the world */ Console::Console(BaseWidget* parent, Nz::LuaState& state) : BaseWidget(parent), m_historyPosition(0), m_defaultFont(Nz::Font::GetDefault()), m_state(state), m_characterSize(24) { // History m_history = Add(); m_history->EnableBackground(true); m_history->SetReadOnly(true); m_history->SetBackgroundColor(Nz::Color(80, 80, 160, 128)); m_history->SetPadding(0.f, 0.f, 0.f, 4.f); // Input m_input = Add(); m_input->EnableBackground(true); m_input->SetText(s_inputPrefix); m_input->SetTextColor(Nz::Color::Black); m_input->SetPadding(0.f, 2.f, 0.f, 2.f); m_input->OnTextAreaKeyReturn.Connect(this, &Console::ExecuteInput); // Protect input prefix from erasure/selection m_input->SetCursorPosition(s_inputPrefixSize); m_input->OnTextAreaCursorMove.Connect([](const TextAreaWidget* textArea, std::size_t* newCursorPos) { *newCursorPos = std::max(*newCursorPos, s_inputPrefixSize); }); m_input->OnTextAreaKeyBackspace.Connect([](const TextAreaWidget* textArea, bool* ignoreDefaultAction) { if (textArea->GetGlyphIndex() <= s_inputPrefixSize) *ignoreDefaultAction = true; }); // Handle history m_input->OnTextAreaKeyUp.Connect([&] (const TextAreaWidget* textArea, bool* ignoreDefaultAction) { *ignoreDefaultAction = true; if (m_historyPosition > 0) m_historyPosition--; m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]); }); m_input->OnTextAreaKeyDown.Connect([&] (const TextAreaWidget* textArea, bool* ignoreDefaultAction) { *ignoreDefaultAction = true; if (++m_historyPosition >= m_commandHistory.size()) m_historyPosition = 0; m_input->SetText(s_inputPrefix + m_commandHistory[m_historyPosition]); }); // General SetPadding(0.f, 0.f, 0.f, 0.f); } /*! * \brief Adds a line to the console * * \param text New line of text * \param color Color for the text */ void Console::AddLine(const Nz::String& text, const Nz::Color& color) { m_historyLines.emplace_back(Line{ color, text }); m_history->AppendText(text + '\n'); } /*! * \brief Clears the console */ void Console::Clear() { m_historyLines.clear(); m_history->Clear(); m_input->SetText(s_inputPrefix); } void Console::ResizeToContent() { } /*! * \brief Sets the character size * * \param size Size of the font */ void Console::SetCharacterSize(unsigned int size) { m_characterSize = size; m_history->SetCharacterSize(size); m_input->SetCharacterSize(size); Layout(); } /*! * \brief Sets the text font * * \param font Reference to a valid font * * \remark Produces a NazaraAssert if font is invalid or null */ 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(); } /*! * \brief Performs this action when an input is added to the console */ void Console::ExecuteInput(const TextAreaWidget* textArea, bool* ignoreDefaultAction) { NazaraAssert(textArea == m_input, "Unexpected signal from an other text area"); Nz::String input = m_input->GetText(); Nz::String inputCmd = input.SubString(s_inputPrefixSize); m_input->SetText(s_inputPrefix); if (m_commandHistory.empty() || m_commandHistory.back() != inputCmd) m_commandHistory.push_back(inputCmd); m_historyPosition = m_commandHistory.size(); AddLine(input); //< With the input prefix if (!m_state.Execute(inputCmd)) AddLine(m_state.GetLastError(), Nz::Color::Red); } /*! * \brief Places the console according to its layout */ void Console::Layout() { Nz::Vector2f origin = GetContentOrigin(); const Nz::Vector2f& size = GetContentSize(); unsigned int lineHeight = m_defaultFont->GetSizeInfo(m_characterSize).lineHeight; float historyHeight = size.y - lineHeight; m_maxHistoryLines = static_cast(std::ceil(historyHeight / lineHeight)); float diff = historyHeight - m_maxHistoryLines * lineHeight; m_history->SetPosition(origin.x, origin.y + diff); m_history->SetSize({size.x, historyHeight - diff - 4.f}); m_input->SetContentSize({size.x, size.y - historyHeight}); m_input->SetPosition(origin.x, origin.y + historyHeight); } }