131 lines
3.4 KiB
C++
131 lines
3.4 KiB
C++
#include <algorithm>
|
|
#include <functional>
|
|
#include <unistd.h>
|
|
|
|
#include "neovision/ansi.h"
|
|
|
|
#include "neovision/application.h"
|
|
|
|
namespace neovision {
|
|
|
|
std::weak_ptr<Application> TheApp;
|
|
|
|
/* There's a bit of evil voodoo going on where with the TheApp pointer.
|
|
Technically we don't "own" the TheApp pointer, the caller who instantiates
|
|
us does - yet we set the TheApp pointer (a weak (observing) pointer) -
|
|
in order to not force the user to do it. In order to do this, we create a
|
|
shared 'this' ptr with a custom no-op Deleter (to avoid a double-free, since
|
|
we get deleted already by the caller). */
|
|
Application::Application(): View(), m_selfPtr(this, [](Application*){})
|
|
{
|
|
TheApp = m_selfPtr;
|
|
}
|
|
|
|
Application::~Application()
|
|
{
|
|
if (m_running == true) Stop();
|
|
}
|
|
|
|
void Application::Initialize()
|
|
{
|
|
View::Initialize();
|
|
m_terminal.Initialize();
|
|
IO::Get().SetInputFunction([this](size_t b){
|
|
return m_terminal.Read(b);
|
|
});
|
|
IO::Get().SetOutputFunction([this](const std::wstring& data){
|
|
m_terminal.Write(data);
|
|
});
|
|
m_terminal.Unbuffer();
|
|
Size(m_terminal.Size()- Vector2D(1, 1), m_bgFillCharacter);
|
|
m_terminal.SetOnResize([&](){
|
|
const Vector2D newSize = m_terminal.Size() - Vector2D(1, 1);
|
|
Size(newSize, m_bgFillCharacter);
|
|
OnResize(newSize);
|
|
});
|
|
m_terminal.SetOnTerminate([&](){
|
|
Stop();
|
|
});
|
|
SetMouseReporting(MouseEventMode::ButtonOnly);
|
|
m_inputParser.SetOnKeyEvent([this](const std::wstring& k){
|
|
OnKey(k);
|
|
});
|
|
m_inputParser.SetOnMouseEvent([this](const MouseEventData& m){
|
|
OnMouse(m);
|
|
});
|
|
InitializeViews();
|
|
}
|
|
|
|
void Application::InitializeViews()
|
|
{
|
|
for (auto& view: m_views)
|
|
{
|
|
if (!view) return;
|
|
if (view->Initialized() != true) view->Initialize();
|
|
}
|
|
}
|
|
|
|
void Application::Run()
|
|
{
|
|
if (!Initialized())
|
|
{
|
|
throw std::runtime_error(
|
|
"Application::Run() called before "
|
|
"Application::Initialized() was called."
|
|
);
|
|
}
|
|
ClearScreen();
|
|
m_running = true;
|
|
InitializeViews();
|
|
while (m_running)
|
|
{
|
|
m_terminal.ProcessEvents();
|
|
OnDraw();
|
|
m_inputParser.Read();
|
|
}
|
|
}
|
|
|
|
void Application::Stop()
|
|
{
|
|
SetMouseReporting(MouseEventMode::None);
|
|
IO::Get().Write(ansi::MakeEscapeSequence(ansi::EscapeSequence::RIS));
|
|
m_terminal.Cleanup();
|
|
m_running = false;
|
|
}
|
|
|
|
Terminal& Application::Term()
|
|
{
|
|
return m_terminal;
|
|
}
|
|
|
|
void Application::OnMouse(const MouseEventData& mouseData)
|
|
{
|
|
/* Figure out what view is clicked. Reverse iterate so the top-most window
|
|
always receives the click (the array should be sorted in z-order) */
|
|
for (auto viewIter = m_views.rbegin(); viewIter != m_views.rend();
|
|
++viewIter)
|
|
{
|
|
auto& view = (*viewIter);
|
|
const std::uint32_t x1 = view->Position().X();
|
|
const std::uint32_t x2 = x1 + view->Size().X();
|
|
const std::uint32_t y1 = view->Position().Y();
|
|
const std::uint32_t y2 = y1 + view->Size().Y();
|
|
if (
|
|
(mouseData.position.X() >= x1)
|
|
&& (mouseData.position.X() <= x2)
|
|
&& (mouseData.position.Y() >= y1)
|
|
&& (mouseData.position.Y() <= y2) )
|
|
{
|
|
view->OnMouse(mouseData);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Application::OnKey(const std::wstring& keyData)
|
|
{
|
|
if (keyData.empty()) return;
|
|
}
|
|
|
|
} // namespace neovision
|