add: Simple widget system

This commit is contained in:
Simon Hardt
2022-03-06 03:04:32 +01:00
parent e1e410561c
commit 243bf41e90
14 changed files with 430 additions and 8 deletions

View File

@@ -8,17 +8,40 @@ configure_file (
set(src
src/main.cpp
src/Size.hpp
src/Vector.hpp
src/Rect.hpp
src/Image.hpp
src/Image.cpp
src/ScreenManager.hpp
src/ScreenManager.cpp
src/display/IDisplay.hpp
src/display/Display.hpp
src/display/Display.cpp
src/render/RenderTarget.hpp
src/render/RenderTarget.cpp
src/font/Font.hpp
src/font/Font.cpp
src/font/Glyph.hpp
src/font/Glyph.cpp
src/widgets/Widget.hpp
src/widgets/Widget.cpp
src/widgets/clock/Digital.hpp
src/widgets/clock/Digital.cpp
src/widgets/clock/Analog.hpp
src/widgets/clock/Analog.cpp
)
if (BUILD_EPD)

View 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

View 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

View File

@@ -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

View File

@@ -55,6 +55,10 @@ namespace frame::display
sf::Event ev;
while(window.pollEvent(ev))
{
if(ev.type == sf::Event::Closed)
{
Close();
}
}
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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,

View 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

View 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

View 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

View 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

View File

View File