add: Simple widget system
This commit is contained in:
64
frame/src/ScreenManager.cpp
Normal file
64
frame/src/ScreenManager.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "ScreenManager.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
ScreenManager::ScreenManager(std::unique_ptr<display::IDisplay> pDisplay)
|
||||
: mDisplay(std::move(pDisplay))
|
||||
, mRenderTarget(mDisplay->getSize())
|
||||
{
|
||||
}
|
||||
|
||||
void ScreenManager::Update()
|
||||
{
|
||||
if(mRoot)
|
||||
{
|
||||
mRoot->Update();
|
||||
}
|
||||
if(mDisplay)
|
||||
{
|
||||
mDisplay->Update();
|
||||
if(!mDisplay->isOpen())
|
||||
{
|
||||
mIsRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::Render()
|
||||
{
|
||||
if(mRoot)
|
||||
{
|
||||
mRenderTarget.Clear();
|
||||
mRoot->Render(mRenderTarget);
|
||||
if(mDisplay)
|
||||
{
|
||||
mDisplay->Display(mRenderTarget.getImage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::MainLoop()
|
||||
{
|
||||
while(mIsRunning)
|
||||
{
|
||||
Update();
|
||||
|
||||
if(mRoot && mRoot->isDirty())
|
||||
{
|
||||
Render();
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(mUpdateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenManager::setRoot(widgets::Widget::shared_ptr widget)
|
||||
{
|
||||
mRoot = widget;
|
||||
mRoot->ComputeChildSize(mDisplay->getSize());
|
||||
}
|
||||
|
||||
} // namespace frame
|
||||
36
frame/src/ScreenManager.hpp
Normal file
36
frame/src/ScreenManager.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "display/IDisplay.hpp"
|
||||
#include "render/RenderTarget.hpp"
|
||||
#include "widgets/Widget.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace frame
|
||||
{
|
||||
|
||||
class ScreenManager
|
||||
{
|
||||
std::unique_ptr<display::IDisplay> mDisplay;
|
||||
|
||||
render::RenderTarget mRenderTarget;
|
||||
widgets::Widget::shared_ptr mRoot;
|
||||
|
||||
std::chrono::duration<double> mUpdateInterval = 0.5s;
|
||||
|
||||
bool mIsRunning = true;
|
||||
|
||||
public:
|
||||
ScreenManager(std::unique_ptr<display::IDisplay> pDisplay);
|
||||
|
||||
void Update();
|
||||
void Render();
|
||||
|
||||
void MainLoop();
|
||||
|
||||
void setRoot(widgets::Widget::shared_ptr widget);
|
||||
};
|
||||
|
||||
} // namespace frame
|
||||
@@ -12,6 +12,8 @@ namespace frame::display
|
||||
std::string mName;
|
||||
Size mSize;
|
||||
|
||||
bool mIsOpen = true;
|
||||
|
||||
public:
|
||||
IDisplay(std::string_view pName, Size pSize)
|
||||
: mName(pName)
|
||||
@@ -33,5 +35,15 @@ namespace frame::display
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
bool isOpen() const
|
||||
{
|
||||
return mIsOpen;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
mIsOpen = false;
|
||||
}
|
||||
};
|
||||
} // namespace frame::display
|
||||
|
||||
@@ -55,6 +55,10 @@ namespace frame::display
|
||||
sf::Event ev;
|
||||
while(window.pollEvent(ev))
|
||||
{
|
||||
if(ev.type == sf::Event::Closed)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,31 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#include "ScreenManager.hpp"
|
||||
#include "display/Display.hpp"
|
||||
#include "font/Font.hpp"
|
||||
#include "render/RenderTarget.hpp"
|
||||
#include "widgets/clock/Analog.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
auto display = frame::display::Create();
|
||||
|
||||
if(!display)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
display->Init();
|
||||
|
||||
frame::ScreenManager screen(std::move(display));
|
||||
|
||||
screen.setRoot(frame::widgets::AnalogClock::Create());
|
||||
|
||||
screen.MainLoop();
|
||||
}
|
||||
|
||||
/*
|
||||
int main()
|
||||
{
|
||||
constexpr double pi = 3.14159;
|
||||
@@ -167,7 +188,7 @@ int main()
|
||||
|
||||
display->Clear(frame::display::Color::WHITE);
|
||||
}
|
||||
|
||||
*/
|
||||
/*
|
||||
#include <EPD_7in5_V2.h>
|
||||
|
||||
|
||||
@@ -7,15 +7,20 @@ namespace frame::render
|
||||
|
||||
RenderTarget::RenderTarget(Size size)
|
||||
: image(size)
|
||||
, root_size{0, 0, (int)size.width, (int)size.height}
|
||||
{
|
||||
}
|
||||
|
||||
void RenderTarget::DrawPixel(Vector const& position, Color color)
|
||||
{
|
||||
if(position.x < 0 || position.y < 0 || position.x >= image.getWidth()
|
||||
|| position.y >= image.getHeight())
|
||||
auto current = getCurrentSize();
|
||||
|
||||
if(position.x < 0 || position.y < 0 || position.x >= current.width
|
||||
|| position.y >= current.height
|
||||
|| (position.x + current.left) >= root_size.width
|
||||
|| (position.y + current.top) >= root_size.height)
|
||||
return;
|
||||
image.at(position.x, position.y) = color;
|
||||
image.at(position.x + current.left, position.y + current.top) = color;
|
||||
}
|
||||
|
||||
void RenderTarget::DrawLine(Vector const& start,
|
||||
@@ -77,12 +82,16 @@ namespace frame::render
|
||||
|
||||
void RenderTarget::DrawCircle(Vector const& center,
|
||||
int32_t radius,
|
||||
int32_t line,
|
||||
Color color)
|
||||
{
|
||||
int32_t const r_2 = radius * radius;
|
||||
Vector pos{0, -radius};
|
||||
|
||||
DrawPointsMirrorCircle(center, pos, color);
|
||||
for(auto i = 0; i < line; ++i)
|
||||
{
|
||||
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
||||
}
|
||||
|
||||
while(pos.x <= radius / std::sqrt(2.f))
|
||||
{
|
||||
@@ -102,7 +111,10 @@ namespace frame::render
|
||||
E += 2 * pos.x - 2 * pos.y + 5;
|
||||
}
|
||||
|
||||
DrawPointsMirrorCircle(center, pos, color);
|
||||
for(auto i = 0; i < line; ++i)
|
||||
{
|
||||
DrawPointsMirrorCircle(center, {pos.x, pos.y + i}, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +211,7 @@ namespace frame::render
|
||||
|
||||
switch(alignVertical)
|
||||
{
|
||||
case AlignVertical::TOP: start.y += lineSpacing_3 * 2; break;
|
||||
case AlignVertical::TOP: start.y -= y_offset; break;
|
||||
case AlignVertical::CENTER:
|
||||
start.y += rect.height / 2;
|
||||
start.y += text_height / 2;
|
||||
@@ -207,7 +219,7 @@ namespace frame::render
|
||||
break;
|
||||
case AlignVertical::BOTTOM:
|
||||
start.y += rect.height;
|
||||
start.y += text_height + y_offset;
|
||||
start.y -= text_height + y_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -249,6 +261,30 @@ namespace frame::render
|
||||
image.Clear(color);
|
||||
}
|
||||
|
||||
Rect RenderTarget::getCurrentSize() const
|
||||
{
|
||||
if(Scissor.size() == 0)
|
||||
{
|
||||
return root_size;
|
||||
}
|
||||
return Scissor.top();
|
||||
}
|
||||
|
||||
void RenderTarget::pushViewport(Rect rect)
|
||||
{
|
||||
auto current = getCurrentSize();
|
||||
|
||||
Scissor.push({current.top + rect.top,
|
||||
current.left + rect.left,
|
||||
rect.width,
|
||||
rect.height});
|
||||
}
|
||||
|
||||
void RenderTarget::popViewport()
|
||||
{
|
||||
Scissor.pop();
|
||||
}
|
||||
|
||||
void RenderTarget::DrawPointsMirrorCircle(Vector const& center,
|
||||
Vector const& pos,
|
||||
Color color)
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "../Vector.hpp"
|
||||
#include "../font/Font.hpp"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace frame
|
||||
{
|
||||
enum class AlignVertical { TOP, CENTER, BOTTOM };
|
||||
@@ -19,6 +21,9 @@ namespace frame::render
|
||||
{
|
||||
Image image;
|
||||
|
||||
Rect root_size;
|
||||
std::stack<Rect> Scissor;
|
||||
|
||||
public:
|
||||
RenderTarget(Size size);
|
||||
|
||||
@@ -30,6 +35,7 @@ namespace frame::render
|
||||
|
||||
void DrawCircle(Vector const& center,
|
||||
int32_t radius,
|
||||
int32_t line,
|
||||
Color color = BLACK);
|
||||
|
||||
void DrawRectFilled(Vector const& TopLeft,
|
||||
@@ -71,6 +77,10 @@ namespace frame::render
|
||||
return image;
|
||||
}
|
||||
|
||||
Rect getCurrentSize() const;
|
||||
void pushViewport(Rect rect);
|
||||
void popViewport();
|
||||
|
||||
private:
|
||||
void DrawPointsMirrorCircle(Vector const& center,
|
||||
Vector const& pos,
|
||||
|
||||
56
frame/src/widgets/Widget.cpp
Normal file
56
frame/src/widgets/Widget.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "Widget.hpp"
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::shared_ptr Widget::getParent() const
|
||||
{
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void Widget::setParent(week_ptr ptr)
|
||||
{
|
||||
mParent = ptr;
|
||||
}
|
||||
|
||||
bool Widget::isDirty() const
|
||||
{
|
||||
return mDirty;
|
||||
}
|
||||
|
||||
void Widget::setSize(Vector size)
|
||||
{
|
||||
mSize = size;
|
||||
ComputeChildSize(size);
|
||||
}
|
||||
|
||||
Vector const& Widget::getSize() const
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
void Widget::setPosition(Vector size)
|
||||
{
|
||||
mRelativePosition = size;
|
||||
}
|
||||
|
||||
Vector Widget::getPosition() const
|
||||
{
|
||||
return mRelativePosition;
|
||||
}
|
||||
|
||||
void Widget::setDirty()
|
||||
{
|
||||
mDirty = true;
|
||||
if(mParent)
|
||||
{
|
||||
mParent->setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setClear()
|
||||
{
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
44
frame/src/widgets/Widget.hpp
Normal file
44
frame/src/widgets/Widget.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "../Rect.hpp"
|
||||
#include "../render/RenderTarget.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class Widget
|
||||
{
|
||||
public:
|
||||
using shared_ptr = std::shared_ptr<Widget>;
|
||||
using week_ptr = std::shared_ptr<Widget>;
|
||||
|
||||
public:
|
||||
virtual ~Widget() = default;
|
||||
|
||||
virtual void Update() = 0;
|
||||
virtual void Render(render::RenderTarget& rt) = 0;
|
||||
virtual void ComputeChildSize(Vector size){};
|
||||
|
||||
shared_ptr getParent() const;
|
||||
void setParent(week_ptr ptr);
|
||||
|
||||
void setSize(Vector size);
|
||||
Vector const& getSize() const;
|
||||
|
||||
void setPosition(Vector size);
|
||||
Vector getPosition() const;
|
||||
|
||||
bool isDirty() const;
|
||||
void setDirty();
|
||||
void setClear();
|
||||
|
||||
private:
|
||||
week_ptr mParent;
|
||||
bool mDirty = true;
|
||||
|
||||
Vector mRelativePosition;
|
||||
Vector mSize;
|
||||
};
|
||||
|
||||
} // namespace frame::widgets
|
||||
94
frame/src/widgets/clock/Analog.cpp
Normal file
94
frame/src/widgets/clock/Analog.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "Analog.hpp"
|
||||
|
||||
#include <ctime>
|
||||
#include <fmt/format.h>
|
||||
|
||||
constexpr double pi = 3.14159;
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
Widget::shared_ptr AnalogClock::Create()
|
||||
{
|
||||
return std::make_shared<AnalogClock>();
|
||||
}
|
||||
|
||||
void AnalogClock::Update()
|
||||
{
|
||||
auto const now = std::chrono::system_clock::now();
|
||||
auto const min = std::chrono::floor<std::chrono::minutes>(now);
|
||||
|
||||
if(last_time != min)
|
||||
{
|
||||
setDirty();
|
||||
last_time = min;
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogClock::Render(render::RenderTarget& rt)
|
||||
{
|
||||
auto const size = rt.getCurrentSize();
|
||||
auto const min = std::min(size.height, size.width);
|
||||
|
||||
auto const radius = (min / 2) - 10;
|
||||
|
||||
auto const center = Vector{size.width / 2, size.height / 2};
|
||||
|
||||
rt.DrawCircle(center, radius, 4);
|
||||
rt.DrawCircle(center, 6, 4);
|
||||
|
||||
auto step = 360 / 60;
|
||||
|
||||
for(int i = 0; i < 60; ++i)
|
||||
{
|
||||
double const x = std::cos(step * i * pi / 180.f);
|
||||
double const y = std::sin(step * i * pi / 180.f);
|
||||
|
||||
auto lineLength = 10;
|
||||
if(i % 5 == 0)
|
||||
{
|
||||
lineLength = 20;
|
||||
if(i % 3 == 0)
|
||||
{
|
||||
lineLength = 50;
|
||||
}
|
||||
}
|
||||
|
||||
auto const fromCenter = radius - lineLength;
|
||||
|
||||
rt.DrawLine({int32_t(x * (radius - 1) + center.x),
|
||||
int32_t(y * (radius - 1) + center.y)},
|
||||
{int32_t(x * fromCenter + center.x),
|
||||
int32_t(y * fromCenter + center.y)});
|
||||
}
|
||||
|
||||
// pls c++ 20
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(last_time);
|
||||
auto c_time = std::localtime(&t);
|
||||
|
||||
// Render Pointer Minute
|
||||
|
||||
auto const min_steps = 360 / 60;
|
||||
auto const min_rad = (min_steps * c_time->tm_min - 90) * pi / 180.f;
|
||||
|
||||
rt.DrawLine(center,
|
||||
{center.x + int32_t(std::cos(min_rad) * radius),
|
||||
center.y + int32_t(std::sin(min_rad) * radius)});
|
||||
|
||||
// Render Pointer Hour
|
||||
|
||||
auto const hour_steps = 360 / 12;
|
||||
auto hour_deg = (hour_steps * c_time->tm_hour - 90);
|
||||
auto const normalied_min = (c_time->tm_min / 59.0);
|
||||
hour_deg += int32_t(normalied_min * hour_steps);
|
||||
|
||||
auto const hour_rad = hour_deg * pi / 180.f;
|
||||
|
||||
rt.DrawLine(center,
|
||||
{center.x + int32_t(std::cos(hour_rad) * radius * 0.6),
|
||||
center.y + int32_t(std::sin(hour_rad) * radius * 0.6)});
|
||||
|
||||
setClear();
|
||||
}
|
||||
|
||||
} // namespace frame::widgets
|
||||
22
frame/src/widgets/clock/Analog.hpp
Normal file
22
frame/src/widgets/clock/Analog.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "../Widget.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
namespace frame::widgets
|
||||
{
|
||||
|
||||
class AnalogClock : public Widget
|
||||
{
|
||||
using Time = std::chrono::time_point<std::chrono::system_clock>;
|
||||
|
||||
Time last_time;
|
||||
|
||||
public:
|
||||
static Widget::shared_ptr Create();
|
||||
|
||||
void Update() override;
|
||||
void Render(render::RenderTarget& rt) override;
|
||||
};
|
||||
} // namespace frame::widgets
|
||||
0
frame/src/widgets/clock/Digital.cpp
Normal file
0
frame/src/widgets/clock/Digital.cpp
Normal file
0
frame/src/widgets/clock/Digital.hpp
Normal file
0
frame/src/widgets/clock/Digital.hpp
Normal file
Reference in New Issue
Block a user