Moved all the bobbycar gui code into this library

This commit is contained in:
2021-08-20 13:16:55 +02:00
parent f4b568f055
commit ad0d90339e
47 changed files with 1876 additions and 1 deletions

View File

@ -1,17 +1,59 @@
set(headers
src/accessorinterface.h
src/actioninterface.h
src/changevaluedisplay.h
src/changevaluedisplay_bool.h
src/changevaluedisplay_daylightsavingmode.h
src/changevaluedisplay_sntp_sync_mode_t.h
src/checkboxicon.h
src/colorinterface.h
src/display.h
src/fontinterface.h
src/icon.h
src/iconinterface.h
src/menudisplay.h
src/menuitem.h
src/tftinstance.h
src/textinterface.h
src/actions/backproxyaction.h
src/actions/dummyaction.h
src/actions/setvalueaction.h
src/actions/switchscreenaction.h
src/icons/back.h
src/icons/checked.h
src/icons/unchecked.h
src/widgets/graph.h
src/widgets/label.h
src/widgets/progressbar.h
src/widgets/reverseprogressbar.h
src/widgets/verticalmeter.h
src/widgets/vumeter.h
)
set(sources
src/changevaluedisplay.cpp
src/changevaluedisplay_bool.cpp
src/changevaluedisplay_daylightsavingmode.cpp
src/changevaluedisplay_sntp_sync_mode_t.cpp
src/menudisplay.cpp
src/tftinstance.cpp
src/icons/back.cpp
src/icons/checked.cpp
src/icons/unchecked.cpp
src/widgets/label.cpp
src/widgets/progressbar.cpp
src/widgets/reverseprogressbar.cpp
src/widgets/verticalmeter.cpp
src/widgets/vumeter.cpp
)
set(dependencies
cpputils
cxx-ring-buffer
espchrono
espcpputils
expected
fmt
TFT_eSPI
)

BIN
icons/back.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
icons/checked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
icons/unchecked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

21
src/accessorinterface.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
namespace espgui {
template<typename T>
struct AccessorInterface
{
virtual T getValue() const = 0;
virtual void setValue(T value) = 0;
};
//! A special type of AccessorInterface that allows for simple variable read/write operations
//! Can be used to read and write global settings for example.
template<typename T>
struct RefAccessor : public virtual AccessorInterface<T>
{
virtual T& getRef() const = 0;
T getValue() const override { return getRef(); };
void setValue(T value) override { getRef() = value; };
};
} // namespace espgui

9
src/actioninterface.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
namespace espgui {
class ActionInterface
{
public:
virtual void triggered() = 0;
};
} // namespace espgui

View File

@ -0,0 +1,23 @@
#pragma once
// local includes
#include "actioninterface.h"
namespace espgui {
class BackProxyAction : public virtual ActionInterface
{
public:
BackProxyAction(BackInterface &backInterface) :
m_backInterface{backInterface}
{
}
void triggered() override
{
m_backInterface.back();
}
private:
BackInterface &m_backInterface;
};
} // namespace espgui

12
src/actions/dummyaction.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
// local includes
#include "actioninterface.h"
namespace espgui {
class DummyAction : public virtual ActionInterface
{
public:
void triggered() override {}
};
} // namespace espgui

View File

@ -0,0 +1,31 @@
#pragma once
// local includes
#include "actioninterface.h"
#include "accessorinterface.h"
namespace espgui {
template<typename T>
class SetValueAction : public virtual ActionInterface
{
public:
SetValueAction(T value, AccessorInterface<T> &accessorInterface, BackInterface &backInterface) :
m_value{value},
m_accessorInterface{accessorInterface},
m_backInterface{backInterface}
{
}
void triggered() override
{
m_accessorInterface.setValue(m_value);
}
T value() const { return m_value; }
private:
const T m_value;
AccessorInterface<T> &m_accessorInterface;
BackInterface &m_backInterface;
};
} // namespace espgui

View File

@ -0,0 +1,13 @@
#pragma once
// local includes
#include "actioninterface.h"
namespace espgui {
template<typename Tscreen, typename ...Targs>
class SwitchScreenAction : public virtual ActionInterface
{
public:
void triggered() override { switchScreen<Tscreen>(std::make_unique<Targs>()...); }
};
} // namespace espgui

View File

@ -0,0 +1,34 @@
#include "changevaluedisplay.h"
namespace espgui {
void ChangeValueDisplayInterface::initScreen()
{
tft.fillScreen(TFT_BLACK);
m_titleLabel.start();
tft.fillRect(0, 34, tft.width(), 3, TFT_WHITE);
tft.drawRect(25, 75, 190, 65, TFT_WHITE);
m_valueLabel.start();
tft.setTextFont(4);
tft.setTextColor(TFT_WHITE);
tft.drawString("Change value and", 10, 160);
tft.drawString("press button to", 10, 185);
tft.drawString("confirm and go", 10, 210);
tft.drawString("back.", 10, 235);
}
template<>
void ChangeValueDisplay<float>::redraw()
{
tft.setTextFont(4);
tft.setTextColor(TFT_YELLOW);
m_titleLabel.redraw(text());
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextFont(7);
m_valueLabel.redraw(fmt::format("{:.02f}", m_value));
}
} // namespace espgui

129
src/changevaluedisplay.h Normal file
View File

@ -0,0 +1,129 @@
#pragma once
// 3rdparty lib includes
#include <fmt/core.h>
// local includes
#include "display.h"
#include "textinterface.h"
#include "actioninterface.h"
#include "accessorinterface.h"
#include "widgets/label.h"
#include "tftinstance.h"
namespace espgui {
class ChangeValueDisplayInterface :
public Display,
public virtual TextInterface,
public virtual ActionInterface
{
public:
void initScreen() override;
TextInterface *asTextInterface() override { return this; }
const TextInterface *asTextInterface() const override { return this; }
ChangeValueDisplayInterface *asChangeValueDisplayInterface() override { return this; }
const ChangeValueDisplayInterface *asChangeValueDisplayInterface() const override { return this; }
virtual int shownValue() const = 0;
virtual void setShownValue(int value) = 0;
protected:
Label m_titleLabel{5, 5}; // 230, 25
Label m_valueLabel{26, 81}; // 188, 53
};
template<typename Tvalue>
class ChangeValueDisplaySettingsInterface
{
public:
virtual Tvalue step() const { return 1; };
};
template<typename Tvalue, typename Tratio>
class RatioNumberStep : public virtual ChangeValueDisplaySettingsInterface<Tvalue>
{
public:
Tvalue step() const override { return Tvalue(Tratio::num) / Tratio::den; }
};
template<typename Tvalue>
class ChangeValueDisplay :
public ChangeValueDisplayInterface,
public virtual AccessorInterface<Tvalue>,
public virtual ChangeValueDisplaySettingsInterface<Tvalue>
{
using Base = ChangeValueDisplayInterface;
public:
void start() override;
void update() override;
void redraw() override;
void rotate(int offset) override;
void confirm() override;
int shownValue() const { return m_value; }
void setShownValue(int value) { m_value = value; }
private:
Tvalue m_value{};
int m_rotateOffset;
bool m_pressed{};
};
template<typename Tvalue>
void ChangeValueDisplay<Tvalue>::start()
{
m_value = static_cast<AccessorInterface<Tvalue>*>(this)->getValue();
m_rotateOffset = 0;
m_pressed = false;
}
template<typename Tvalue>
void ChangeValueDisplay<Tvalue>::update()
{
if (!m_pressed)
{
const auto rotateOffset = m_rotateOffset;
m_rotateOffset = 0;
m_value -= rotateOffset * static_cast<ChangeValueDisplaySettingsInterface<Tvalue>*>(this)->step();
}
else
{
static_cast<AccessorInterface<Tvalue>*>(this)->setValue(m_value);
triggered();
}
}
template<typename Tvalue>
void ChangeValueDisplay<Tvalue>::redraw()
{
tft.setTextFont(4);
tft.setTextColor(TFT_YELLOW);
m_titleLabel.redraw(text());
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextFont(7);
m_valueLabel.redraw(std::to_string(m_value));
}
template<>
void ChangeValueDisplay<float>::redraw();
template<typename Tvalue>
void ChangeValueDisplay<Tvalue>::rotate(int offset)
{
m_rotateOffset += offset;
}
template<typename Tvalue>
void ChangeValueDisplay<Tvalue>::confirm()
{
m_pressed = true;
}
} // namespace espgui

View File

@ -0,0 +1,32 @@
#include "changevaluedisplay_bool.h"
// local includes
#include "actions/setvalueaction.h"
#include "actions/backproxyaction.h"
#include "icons/back.h"
namespace espgui {
namespace {
constexpr char TEXT_TRUE[] = "true";
constexpr char TEXT_FALSE[] = "false";
constexpr char TEXT_BACK[] = "Back";
} // namespace
ChangeValueDisplay<bool>::ChangeValueDisplay()
{
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<bool>, StaticText<TEXT_TRUE>>>(true, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<bool>, StaticText<TEXT_FALSE>>>(false, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, BackProxyAction, StaticText<TEXT_BACK>, StaticMenuItemIcon<&icons::back>>>(*this);
}
void ChangeValueDisplay<bool>::start()
{
Base::start();
switch (getValue())
{
case true: setSelectedIndex(0); break;
case false: setSelectedIndex(1); break;
}
}
} // namespace espgui

View File

@ -0,0 +1,22 @@
#pragma once
// local includes
#include "changevaluedisplay.h"
#include "menudisplay.h"
#include "actioninterface.h"
namespace espgui {
template<>
class ChangeValueDisplay<bool> :
public MenuDisplay,
public virtual AccessorInterface<bool>,
public virtual ActionInterface
{
using Base = MenuDisplay;
public:
ChangeValueDisplay();
void start() override;
};
} // namespace espgui

View File

@ -0,0 +1,43 @@
#include "changevaluedisplay_daylightsavingmode.h"
// esp-idf includes
#include <esp_log.h>
// local includes
#include "actions/setvalueaction.h"
#include "actions/backproxyaction.h"
#include "icons/back.h"
namespace espgui {
namespace {
constexpr const char * const TAG = "ESPGUI";
constexpr char TEXT_NONE[] = "None";
constexpr char TEXT_EUROPEANSUMMERTIME[] = "EuropeanSummerTime";
constexpr char TEXT_USDAYLIGHTTIME[] = "UsDaylightTime";
constexpr char TEXT_BACK[] = "Back";
} // namespace
ChangeValueDisplay<espchrono::DayLightSavingMode>::ChangeValueDisplay()
{
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<espchrono::DayLightSavingMode>, StaticText<TEXT_NONE>>>(espchrono::DayLightSavingMode::None, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<espchrono::DayLightSavingMode>, StaticText<TEXT_EUROPEANSUMMERTIME>>>(espchrono::DayLightSavingMode::EuropeanSummerTime, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<espchrono::DayLightSavingMode>, StaticText<TEXT_USDAYLIGHTTIME>>>(espchrono::DayLightSavingMode::UsDaylightTime, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, BackProxyAction, StaticText<TEXT_BACK>, StaticMenuItemIcon<&icons::back>>>(*this);
}
void ChangeValueDisplay<espchrono::DayLightSavingMode>::start()
{
Base::start();
switch (const auto value = getValue())
{
case espchrono::DayLightSavingMode::None: setSelectedIndex(0); break;
case espchrono::DayLightSavingMode::EuropeanSummerTime: setSelectedIndex(1); break;
case espchrono::DayLightSavingMode::UsDaylightTime: setSelectedIndex(2); break;
default:
ESP_LOGW(TAG, "Unknown DayLightSavingMode: %i", int(value));
setSelectedIndex(3);
}
}
}

View File

@ -0,0 +1,25 @@
#pragma once
// 3rdparty lib includes
#include <espchrono.h>
// local includes
#include "changevaluedisplay.h"
#include "menudisplay.h"
#include "actioninterface.h"
namespace espgui {
template<>
class ChangeValueDisplay<espchrono::DayLightSavingMode> :
public MenuDisplay,
public virtual AccessorInterface<espchrono::DayLightSavingMode>,
public virtual ActionInterface
{
using Base = MenuDisplay;
public:
ChangeValueDisplay();
void start() override;
};
} // namespace espgui

View File

@ -0,0 +1,40 @@
#include "changevaluedisplay_sntp_sync_mode_t.h"
// esp-idf includes
#include <esp_log.h>
// local includes
#include "actions/setvalueaction.h"
#include "actions/backproxyaction.h"
#include "icons/back.h"
namespace espgui {
namespace {
constexpr const char * const TAG = "ESPGUI";
constexpr char TEXT_IMMED[] = "IMMED";
constexpr char TEXT_SMOOTH[] = "SMOOTH";
constexpr char TEXT_BACK[] = "Back";
} // namespace
ChangeValueDisplay<sntp_sync_mode_t>::ChangeValueDisplay()
{
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<sntp_sync_mode_t>, StaticText<TEXT_IMMED>>>(SNTP_SYNC_MODE_IMMED, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, SetValueAction<sntp_sync_mode_t>, StaticText<TEXT_SMOOTH>>>(SNTP_SYNC_MODE_SMOOTH, *this, *this);
constructMenuItem<makeComponentArgs<MenuItem, BackProxyAction, StaticText<TEXT_BACK>, StaticMenuItemIcon<&icons::back>>>(*this);
}
void ChangeValueDisplay<sntp_sync_mode_t>::start()
{
Base::start();
switch (const auto value = getValue())
{
case SNTP_SYNC_MODE_IMMED: setSelectedIndex(0); break;
case SNTP_SYNC_MODE_SMOOTH: setSelectedIndex(1); break;
default:
ESP_LOGW("BOBBY", "Unknown sntp_sync_mode_t: %i", int(value));
setSelectedIndex(2);
}
}
}

View File

@ -0,0 +1,24 @@
#pragma once
// esp-idf includes
#include <esp_sntp.h>
// local includes
#include "changevaluedisplay.h"
#include "menudisplay.h"
namespace espgui {
template<>
class ChangeValueDisplay<sntp_sync_mode_t> :
public MenuDisplay,
public virtual AccessorInterface<sntp_sync_mode_t>,
public virtual ActionInterface
{
using Base = MenuDisplay;
public:
ChangeValueDisplay();
void start() override;
};
} // namespace espgui

18
src/checkboxicon.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
// local includes
#include "menuitem.h"
#include "accessorinterface.h"
#include "icons/checked.h"
#include "icons/unchecked.h"
namespace espgui {
class CheckboxIcon : public virtual MenuItemIconInterface, public virtual AccessorInterface<bool>
{
public:
const MenuItemIcon *icon() const override
{
return getValue() ? &icons::checked : &icons::unchecked;
}
};
} // namespace espgui

33
src/colorinterface.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
// 3rdparty lib includes
#include <TFT_eSPI.h>
namespace espgui {
class ColorInterface {
public:
virtual int color() const { return TFT_WHITE; };
};
template<int TColor>
class StaticColor : public virtual ColorInterface
{
public:
static constexpr int STATIC_COLOR = TColor;
int color() const override { return TColor; }
};
using DefaultColor = StaticColor<TFT_WHITE>;
using DisabledColor = StaticColor<TFT_DARKGREY>;
class ChangeableColor : public virtual ColorInterface
{
public:
int color() const override { return m_color; }
void setColor(const int &color) { m_color = color; }
private:
int m_color;
};
} // namespace espgui

88
src/display.h Normal file
View File

@ -0,0 +1,88 @@
#pragma once
// system includes
#include <string>
// forward declares
namespace espgui {
class TextInterface;
class MenuDisplay;
class ChangeValueDisplayInterface;
}
namespace espgui {
template<typename ...T>
class makeComponent : public T...
{};
template <typename T1, typename T2, typename ...T3>
class makeComponentArgs : public T1, public T2, public T3...
{
public:
template<typename ...T>
makeComponentArgs(T&& ...args) :
T2{std::forward<T>(args)...}
{
}
};
class ConfirmInterface
{
public:
virtual void confirm() = 0;
};
class BackInterface
{
public:
virtual void back() = 0;
};
template<typename T>
class ConfirmActionInterface : public virtual ConfirmInterface
{
public:
void confirm() override { T{}.triggered(); }
};
class DummyConfirm : public virtual ConfirmInterface
{
public:
void confirm() override {}
};
template<typename T>
class BackActionInterface : public virtual BackInterface
{
public:
void back() override { T{}.triggered(); }
};
class DummyBack : public virtual BackInterface
{
public:
void back() override {}
};
class Display : public virtual ConfirmInterface, public virtual BackInterface {
public:
virtual ~Display() = default;
virtual void start() {};
virtual void initScreen() {};
virtual void update() {};
virtual void redraw() {};
virtual void stop() {}
virtual void rotate(int offset) {}
virtual TextInterface *asTextInterface() { return nullptr; }
virtual const TextInterface *asTextInterface() const { return nullptr; }
virtual MenuDisplay *asMenuDisplay() { return nullptr; }
virtual const MenuDisplay *asMenuDisplay() const { return nullptr; }
virtual ChangeValueDisplayInterface *asChangeValueDisplayInterface() { return nullptr; }
virtual const ChangeValueDisplayInterface *asChangeValueDisplayInterface() const { return nullptr; }
};
} // namespace espgui

29
src/fontinterface.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
namespace espgui {
class FontInterface {
public:
virtual int font() const { return 4; };
};
template<int TFont>
class StaticFont : public virtual FontInterface
{
public:
static constexpr int STATIC_FONT = TFont;
int font() const override { return TFont; }
};
using DefaultFont = StaticFont<4>;
class ChangeableFont : public virtual FontInterface
{
public:
int font() const override { return m_font; }
void setfont(const int &font) { m_font = font; }
private:
int m_font;
};
} // namespace espgui

View File

@ -3,6 +3,7 @@
// system includes
#include <cstdint>
namespace espgui {
template<uint16_t width, uint16_t height>
struct Icon
{
@ -11,3 +12,4 @@ struct Icon
const unsigned short buffer[width*height];
};
} // namespace espgui

20
src/iconinterface.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
// local includes
#include "icon.h"
namespace espgui {
template<uint16_t width, uint16_t height>
class IconInterface
{
public:
virtual const Icon<width, height> *icon() const { return nullptr; }
};
template<uint16_t width, uint16_t height, const Icon<width, height> *T>
class StaticIcon : public virtual IconInterface<width, height>
{
public:
virtual const Icon<width, height> *icon() const { return T; }
};
} // namespace espgui

44
src/icons/back.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "back.h"
namespace espgui {
namespace icons {
const Icon<24, 24> back{{
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x016C, 0x014B, 0x010B, 0x01AC, 0x018C, 0x018C, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0010 (16) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014B, 0x0009, 0x0000, // 0x0020 (32) pixels
0x2AD0, 0x3331, 0x00CB, 0x09CD, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x012B, 0x0007, 0x0000, 0x1A6F, 0x5CD7, 0x5C96, 0x0003, 0x09ED, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0040 (64) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010B, 0x0006, 0xFFFF, 0x022F, 0x6518, // 0x0050 (80) pixels
0x75FB, 0x5C96, 0x0000, 0x1AB0, 0x2312, 0x22F1, 0x1AB0, 0x1A6F, 0x0A0E, 0x016C, 0x0048, 0x00EA, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0060 (96) pixels
0x0000, 0x0000, 0x010A, 0x0029, 0x0000, 0x022F, 0x6538, 0x761B, 0x761B, 0x5CD7, 0x024F, 0x22D0, 0x22D0, 0x1AB0, 0x1A6F, 0x124F, // 0x0070 (112) pixels
0x01CD, 0x002A, 0x0000, 0x018C, 0x0007, 0x014B, 0x0000, 0x0000, 0x0002, 0x0003, 0x0A2E, 0x0000, 0x1A90, 0x6518, 0x7E3C, 0x761C, // 0x0080 (128) pixels
0x761C, 0x6E1C, 0x65BB, 0x5D9A, 0x5D7A, 0x5539, 0x54F8, 0x4CB7, 0x4C76, 0x3BD4, 0x2B11, 0x1A6F, 0x016C, 0x43F5, 0x012B, 0x00A6, // 0x0090 (144) pixels
0x0006, 0x024F, 0x0000, 0x22F1, 0x6518, 0x7E3C, 0x7E5C, 0x765C, 0x765C, 0x6E3C, 0x661C, 0x65FC, 0x5DBB, 0x559A, 0x4D5A, 0x44F9, // 0x00A0 (160) pixels
0x44D8, 0x3C97, 0x4456, 0x3BD4, 0x2AF1, 0x09CD, 0x5D7C, 0x00AA, 0x022F, 0x0000, 0x2AF1, 0x6518, 0x863C, 0x7E3C, 0x6E3C, 0x663C, // 0x00B0 (176) pixels
0x663D, 0x5E3D, 0x5E1C, 0x5DFC, 0x55DB, 0x559B, 0x4D5A, 0x4519, 0x3CB8, 0x3477, 0x3436, 0x33F5, 0x3BD4, 0x2AF1, 0x018C, 0x0000, // 0x00C0 (192) pixels
0x014B, 0x22B0, 0x54B7, 0x5D9A, 0x4D7A, 0x3D7B, 0x357B, 0x2D9C, 0x2DDC, 0x2DDC, 0x2DBC, 0x2D7B, 0x355A, 0x3D5A, 0x453A, 0x4519, // 0x00D0 (208) pixels
0x3CD8, 0x3477, 0x3436, 0x2BD5, 0x2BB5, 0x3394, 0x1A6F, 0x014C, 0x016B, 0x1A90, 0x3436, 0x24D9, 0x14D9, 0x1D1A, 0x1D5B, 0x1D9C, // 0x00E0 (224) pixels
0x25DD, 0x25DD, 0x1D9C, 0x1D5B, 0x1D1A, 0x1CD9, 0x1CB8, 0x2CB8, 0x3CB8, 0x3477, 0x2C36, 0x2BD5, 0x2394, 0x33D5, 0x22F1, 0x11CC, // 0x00F0 (240) pixels
0x022F, 0x0004, 0x1AD0, 0x2C57, 0x24F9, 0x1D1A, 0x1D5B, 0x1D7C, 0x1DBC, 0x25BC, 0x1D9C, 0x1D5B, 0x1D1A, 0x1CD9, 0x1C98, 0x1457, // 0x0100 (256) pixels
0x1C37, 0x2C36, 0x2C16, 0x23D5, 0x2394, 0x23B5, 0x2B32, 0x11CD, 0x0000, 0x0A0E, 0x0000, 0x22F1, 0x2C77, 0x251A, 0x1D3A, 0x1D5B, // 0x0110 (272) pixels
0x1D7B, 0x1D7B, 0x1D5B, 0x1D3A, 0x1CFA, 0x1CB9, 0x1C78, 0x1457, 0x1416, 0x13D5, 0x23D5, 0x23B5, 0x1B94, 0x1B94, 0x2B52, 0x11ED, // 0x0120 (288) pixels
0x0023, 0x0000, 0x0A4F, 0x0000, 0x22F1, 0x3497, 0x251A, 0x1D1A, 0x1D3A, 0x2D5B, 0x2D3A, 0x2D1A, 0x24F9, 0x24D9, 0x1478, 0x1437, // 0x0130 (304) pixels
0x13F6, 0x13B5, 0x1394, 0x1B74, 0x1B74, 0x1B94, 0x2B52, 0x11ED, 0x0000, 0x0087, 0x0000, 0x124F, 0x0000, 0x1AB0, 0x3497, 0x1D1A, // 0x0140 (320) pixels
0x1CFA, 0x3497, 0x1B32, 0x2373, 0x2393, 0x2BD4, 0x3415, 0x2C57, 0x13F6, 0x1395, 0x1374, 0x1353, 0x1354, 0x2394, 0x2B32, 0x11ED, // 0x0150 (336) pixels
0x0000, 0x0000, 0x0000, 0x00EA, 0x00EA, 0x0000, 0x126F, 0x3497, 0x24D9, 0x3435, 0x0000, 0x1270, 0x0000, 0x0029, 0x01AC, 0x22F1, // 0x0160 (352) pixels
0x33F5, 0x1395, 0x1354, 0x1333, 0x0B33, 0x2394, 0x22F1, 0x22B0, 0x0000, 0x0000, 0x0000, 0x0000, 0x010B, 0x0008, 0x0000, 0x1A6F, // 0x0170 (368) pixels
0x3477, 0x3435, 0x0006, 0x09ED, 0x0027, 0x00EA, 0x01AC, 0x0000, 0x22B0, 0x2BB4, 0x0B33, 0x1333, 0x0B34, 0x2B73, 0x22B0, 0x09AD, // 0x0180 (384) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x012B, 0x0009, 0x0000, 0x1A90, 0x3352, 0x004A, 0x09CD, 0x0000, 0x0000, 0x010A, 0x012B, // 0x0190 (400) pixels
0x09CD, 0x2B53, 0x1354, 0x0B33, 0x1B54, 0x2B32, 0x228F, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014B, 0x010B, // 0x01A0 (416) pixels
0x0007, 0x018D, 0x01AC, 0x01AC, 0x0000, 0x0000, 0x0000, 0x014B, 0x09CD, 0x2B32, 0x1374, 0x0B33, 0x2353, 0x2AF1, 0x08EA, 0x1A0D, // 0x01B0 (432) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x01AD, 0x09ED, 0x00CA, 0x018C, 0x018C, 0x0000, 0x0000, 0x0000, 0x018C, // 0x01C0 (448) pixels
0x09ED, 0x2B53, 0x1354, 0x1B53, 0x2B32, 0x224E, 0x2B11, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x01D0 (464) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00A8, 0x09ED, 0x122E, 0x2B73, 0x1B54, 0x2B53, 0x32D0, 0x43D4, 0x0004, 0x0000, // 0x01E0 (480) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0049, 0x451B, // 0x01F0 (496) pixels
0x1AB0, 0x2BB4, 0x2B53, 0x32F0, 0x5D3A, 0x0008, 0x00C9, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0200 (512) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x018C, 0x010B, 0x3332, 0x3393, 0x2AD0, 0x873F, 0x012B, 0x11ED, 0x0000, 0x0000, // 0x0210 (528) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0109, 0x01AD, 0x018C, // 0x0220 (544) pixels
0x22B0, 0x32F0, 0x761E, 0x016C, 0x1A2E, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0230 (560) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x014B, 0x018C, 0x018C, 0x09CD, 0x4C77, 0x012B, 0x1A0E, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0240 (576) pixels
}};
} // namespace icons
} // namespace espgui

10
src/icons/back.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
// local includes
#include "icon.h"
namespace espgui {
namespace icons {
extern const Icon<24, 24> back;
} // namespace icons
} // namespace espgui

44
src/icons/checked.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "checked.h"
namespace espgui {
namespace icons {
const Icon<24, 24> checked{{
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0010 (16) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0020 (32) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0040 (64) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2945, 0x6B4D, 0x5AEB, 0x5AEB, // 0x0050 (80) pixels
0x5AEB, 0x5AEB, 0x5AEB, 0x5AEB, 0x5AEB, 0x5AEB, 0x5AEB, 0x8BF1, 0x0240, 0x01E0, 0x02C0, 0x0280, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0060 (96) pixels
0x0000, 0x0000, 0xA514, 0xA534, 0xA534, 0xBDD7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0x8C91, // 0x0070 (112) pixels
0x3807, 0x0000, 0x0000, 0x0260, 0x0260, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC618, 0xBDD7, 0xCE59, 0xAD75, 0xB596, 0xB596, // 0x0080 (128) pixels
0xB596, 0xB596, 0xAD75, 0xAD75, 0xAD75, 0xB596, 0xAD75, 0x9CD3, 0x0300, 0x8E31, 0x00E0, 0x0240, 0x0260, 0x0000, 0x0000, 0x0000, // 0x0090 (144) pixels
0x0000, 0x8431, 0xA534, 0xA514, 0xA534, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD55, 0xAD75, 0xAD75, 0xA554, 0x5BEB, // 0x00A0 (160) pixels
0x4508, 0xBF77, 0x1C43, 0x0240, 0x01C0, 0x02C0, 0x0000, 0x0000, 0x0000, 0x8C51, 0xFFFF, 0x9492, 0xCE59, 0xF7BE, 0xFFBF, 0xF79E, // 0x00B0 (176) pixels
0xF79E, 0xF79E, 0xEF7D, 0xEF7D, 0xF7BE, 0xEF9D, 0x8D51, 0x34E6, 0xA6F4, 0xA734, 0xA6F4, 0x54EA, 0x02E0, 0x0240, 0x0000, 0x0000, // 0x00C0 (192) pixels
0x0000, 0x7BCF, 0x0000, 0x8C72, 0x7C8F, 0x6CCD, 0xC698, 0xF79E, 0xEF7D, 0xEF7D, 0xEF5D, 0xF79E, 0xF79E, 0x8510, 0x34C6, 0x9EB3, // 0x00D0 (208) pixels
0x9692, 0x9672, 0x9692, 0x44C8, 0x02E0, 0x0220, 0x0000, 0x0000, 0x0160, 0x632C, 0x02A0, 0x3B27, 0x3426, 0x8E30, 0x23C4, 0xCEB9, // 0x00E0 (224) pixels
0xF77E, 0xEF7D, 0xF79E, 0xF79E, 0x8D51, 0x2463, 0x8E51, 0x8630, 0x8610, 0x8610, 0x44A8, 0x02A0, 0x0160, 0x0000, 0x0000, 0x0000, // 0x00F0 (240) pixels
0x0240, 0x02A0, 0x0260, 0x1BA3, 0x75AD, 0x860F, 0x656B, 0x4C29, 0xCED9, 0xF7BE, 0xEF7D, 0x8D31, 0x2C24, 0x7E0E, 0x7DEE, 0x7DCE, // 0x0100 (256) pixels
0x7DCE, 0x4CA9, 0x02A0, 0x1843, 0x0220, 0x0280, 0x0000, 0x0000, 0x0240, 0x0000, 0x0321, 0x6D6B, 0x75AC, 0x75AC, 0x75EC, 0x654A, // 0x0110 (272) pixels
0x33A6, 0xCED9, 0x9D73, 0x3C26, 0x7E0D, 0x75CC, 0x75AC, 0x6D8C, 0x5CEA, 0x2AE5, 0x634D, 0x2288, 0x7BCE, 0x0000, 0x0000, 0x0000, // 0x0120 (288) pixels
0x0260, 0x0220, 0x0200, 0x1361, 0x6549, 0x658A, 0x6DCA, 0x760B, 0x5CE8, 0x02E0, 0x33C4, 0x760B, 0x6DCA, 0x6DCA, 0x6DAA, 0x7D6D, // 0x0130 (304) pixels
0x646C, 0x6B6D, 0x6B6E, 0x8C4E, 0x52AD, 0x0000, 0x0000, 0x0000, 0x0200, 0x5B0B, 0x0220, 0x2A85, 0x2B83, 0x6568, 0x5DC8, 0x65E8, // 0x0140 (320) pixels
0x7649, 0x6D49, 0x75EA, 0x6E09, 0x65C8, 0x65A9, 0x6CEB, 0x3BA8, 0xE75C, 0x7BD0, 0x634D, 0x73B1, 0x5ACA, 0x0000, 0x0000, 0x0000, // 0x0150 (336) pixels
0x0000, 0x5AAD, 0x000A, 0x630D, 0x5BCB, 0x2344, 0x65A6, 0x4D84, 0x55C4, 0x4DC4, 0x4DA3, 0x4D83, 0x4563, 0x4463, 0x1B83, 0xE73C, // 0x0160 (352) pixels
0xFFDF, 0x7BF0, 0x6B4D, 0x7BEE, 0x52AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x630E, 0x0010, 0x5AEC, 0xCE79, 0x9DB3, 0x1300, 0x5DC3, // 0x0170 (368) pixels
0x4E01, 0x4E22, 0x4E21, 0x4E02, 0x5D24, 0x4388, 0xE73C, 0xF79E, 0xFFDF, 0x8410, 0x6B6E, 0x840D, 0x630E, 0x0000, 0x0000, 0x0000, // 0x0180 (384) pixels
0x0000, 0x73AE, 0x8C6A, 0x630D, 0xC638, 0xFFFF, 0xADF5, 0x0B60, 0x6664, 0x56A1, 0x66C3, 0x5503, 0x3B48, 0xDF3B, 0xF7BE, 0xF79E, // 0x0190 (400) pixels
0xFFDF, 0x8431, 0x73AF, 0x6B4F, 0x73AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x738D, 0x5280, 0x6B4D, 0xC638, 0xFFFF, 0xFFFF, 0xA5D4, // 0x01A0 (416) pixels
0x1280, 0x7F45, 0x5D83, 0x0300, 0xEF9D, 0xFFFF, 0xFFDF, 0xF7BE, 0xFFFF, 0x8C51, 0x7BCF, 0x7BF2, 0x738D, 0x0000, 0x0000, 0x0000, // 0x01B0 (432) pixels
0x0000, 0x738D, 0x0000, 0x6B6E, 0xC638, 0xFFFF, 0xFFFF, 0xFFFF, 0x9572, 0x32A5, 0x53C9, 0xEF9D, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFDF, // 0x01C0 (448) pixels
0xFFFF, 0x8C72, 0x7BF0, 0x8C53, 0x738D, 0x0000, 0x0000, 0x0000, 0x0000, 0x7BD0, 0xA510, 0x7BCF, 0xA535, 0xCE59, 0xCE59, 0xCE59, // 0x01D0 (464) pixels
0xCE79, 0x84B0, 0xADB5, 0xCE59, 0xCE59, 0xCE59, 0xCE59, 0xCE59, 0xC638, 0x8C51, 0x8410, 0x8430, 0x7BD0, 0x0000, 0x0000, 0x0000, // 0x01E0 (480) pixels
0x0000, 0x8C71, 0x8431, 0x8431, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x8410, 0x83F0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, // 0x01F0 (496) pixels
0x7BF0, 0x8410, 0x8C51, 0x8431, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9CD3, 0x9492, 0xB593, 0x9493, 0x9491, 0x94B1, // 0x0200 (512) pixels
0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x9491, 0x9CF3, 0x4200, 0x8430, 0x9492, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0210 (528) pixels
0x0000, 0x0020, 0x0000, 0x630D, 0x8C71, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, // 0x0220 (544) pixels
0x8C71, 0x8410, 0x8C51, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0230 (560) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0240 (576) pixels
}};
} // namespace icons
} // namespace espgui

10
src/icons/checked.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
// local includes
#include "icon.h"
namespace espgui {
namespace icons {
extern const Icon<24, 24> checked;
} // namespace icons
} // namespace espgui

44
src/icons/unchecked.cpp Normal file
View File

@ -0,0 +1,44 @@
#include "unchecked.h"
namespace espgui {
namespace icons {
const Icon<24, 24> unchecked{{
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0010 (16) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0020 (32) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0030 (48) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0040 (64) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x2945, 0x6B4D, 0x5AEB, 0x5AEB, // 0x0050 (80) pixels
0x5AEB, 0x5AEB, 0x5AEB, 0x5AEB, 0x2124, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0060 (96) pixels
0x0000, 0x0000, 0xA514, 0xA534, 0xA534, 0xBDD7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDF7, 0xBDD7, 0xB5B6, 0xB5B6, 0x31A6, 0x31A6, 0x39E7, // 0x0070 (112) pixels
0x0180, 0x0000, 0x11E3, 0x4269, 0x4229, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xC618, 0xBDD7, 0xCE59, 0xAD75, 0xB596, 0xB596, // 0x0080 (128) pixels
0xB596, 0xB596, 0xAD75, 0xAD55, 0xA514, 0xA514, 0xA514, 0xA514, 0xA514, 0x8C71, 0x8C51, 0x5B0C, 0x4AC9, 0x0000, 0x0000, 0x0000, // 0x0090 (144) pixels
0x0000, 0x8431, 0xA534, 0xA514, 0xA534, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, 0xAD75, // 0x00A0 (160) pixels
0xB596, 0x8C71, 0x8431, 0x5BCC, 0x2A85, 0x52AC, 0x0000, 0x0000, 0x0000, 0x8C51, 0xFFFF, 0x9492, 0xC638, 0xF7BE, 0xFFDF, 0xFFDF, // 0x00B0 (176) pixels
0xF7BE, 0xF7BE, 0xFFDF, 0xF7BE, 0xF7DE, 0xF7BE, 0xFFDF, 0xFFDF, 0xFFFF, 0x8C51, 0x6B6E, 0x538A, 0x2A85, 0x528C, 0x0000, 0x0000, // 0x00C0 (192) pixels
0x5ACB, 0x52AA, 0xFFFF, 0x8451, 0xC638, 0xF7BE, 0xF7BE, 0xF7BE, 0xF79E, 0xF79E, 0xF79E, 0xF7BE, 0xF7BE, 0xF79E, 0xF7BE, 0xF7BE, // 0x00D0 (208) pixels
0xFFDF, 0x8C51, 0x6B6E, 0x53AA, 0x2284, 0x52AC, 0x0000, 0x0000, 0xEF7D, 0xFFFF, 0xEF5D, 0x632D, 0xBDF7, 0xF7BE, 0xF7BE, 0xF79E, // 0x00E0 (224) pixels
0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF79E, 0xF7BE, 0xF7BE, 0xFFDF, 0x8431, 0x6B6E, 0x4B0A, 0x52CB, 0x528C, 0x0000, 0x0000, // 0x00F0 (240) pixels
0xF7BE, 0xE73C, 0x8CD1, 0x738E, 0xBDF7, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, // 0x0100 (256) pixels
0xFFDF, 0x8431, 0x6B6E, 0x5B0C, 0x5ACC, 0x39C8, 0x0000, 0x0000, 0xF7BE, 0xF79E, 0xB616, 0x73CF, 0xC638, 0xFFDF, 0xF79E, 0xF7BE, // 0x0110 (272) pixels
0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF79E, 0xF7BE, 0xFFDF, 0xF7BE, 0xFFDF, 0x8431, 0x6B6E, 0x630D, 0x5AED, 0x0000, 0x0000, 0x0000, // 0x0120 (288) pixels
0xF79E, 0xFFFF, 0x8CD1, 0x52CB, 0xC618, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF79E, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, // 0x0130 (304) pixels
0xFFDF, 0x8431, 0x6B6E, 0x5AEC, 0x52AC, 0x0000, 0x0000, 0x0000, 0x10A2, 0xBDF7, 0x634B, 0x4A4A, 0xC618, 0xF7DE, 0xF7BE, 0xF7BE, // 0x0140 (320) pixels
0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xFFFF, 0x8C51, 0x6B6E, 0x7BCF, 0x5AED, 0x0000, 0x0000, 0x0000, // 0x0150 (336) pixels
0x0000, 0x0000, 0x528C, 0x4A6A, 0xC618, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7DF, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF79E, 0xF7BE, // 0x0160 (352) pixels
0xFFDF, 0x8431, 0x6B6E, 0x7BCF, 0x5AED, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x5ACD, 0x4A4A, 0xBDF8, 0xF7BE, 0xF7BE, 0xF7BE, // 0x0170 (368) pixels
0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xFFFF, 0x8C51, 0x738E, 0x73AE, 0x632E, 0x0000, 0x0000, 0x0000, // 0x0180 (384) pixels
0x0000, 0x0000, 0x6B6D, 0x4209, 0xC618, 0xFFDF, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xF7BE, 0xFFDF, 0xF7BE, // 0x0190 (400) pixels
0xFFDF, 0x8C51, 0x73AF, 0x738F, 0x73AE, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x6B4C, 0x4A6A, 0xC638, 0xFFFF, 0xFFFF, 0xF7BE, // 0x01A0 (416) pixels
0xFFDF, 0xFFDF, 0xF7BE, 0xFFDF, 0xF7BE, 0xF7BE, 0xFFDF, 0xF7BE, 0xFFFF, 0x8C51, 0x7BCF, 0x7BF1, 0x738D, 0x0000, 0x0000, 0x0000, // 0x01B0 (432) pixels
0x0000, 0x0000, 0x6B4C, 0x5ACB, 0xC638, 0xFFFF, 0xFFFF, 0xFFDF, 0xF7BE, 0xFFDF, 0xFFDF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFDF, // 0x01C0 (448) pixels
0xFFFF, 0x8C72, 0x7BF0, 0x8C53, 0x738D, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9492, 0x738E, 0xA534, 0xCE59, 0xCE59, 0xCE59, // 0x01D0 (464) pixels
0xCE59, 0xC638, 0xC618, 0xCE59, 0xCE59, 0xCE59, 0xCE59, 0xCE59, 0xC638, 0x8C51, 0x8410, 0x8430, 0x7BD0, 0x0000, 0x0000, 0x0000, // 0x01E0 (480) pixels
0x0000, 0x0000, 0x8C51, 0x8431, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, 0x7BF0, // 0x01F0 (496) pixels
0x7BF0, 0x8410, 0x8C51, 0x8431, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x9CD3, 0x9492, 0xB593, 0x9493, 0x9491, 0x94B1, // 0x0200 (512) pixels
0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x94B1, 0x9491, 0x9CF3, 0x4200, 0x8430, 0x9492, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0210 (528) pixels
0x0000, 0x0020, 0x0000, 0x630D, 0x8C71, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, 0x8C51, // 0x0220 (544) pixels
0x8C71, 0x8410, 0x8C51, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0230 (560) pixels
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 0x0240 (576) pixels
}};
} // namespace icons
} // namespace espgui

10
src/icons/unchecked.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
// local includes
#include "icon.h"
namespace espgui {
namespace icons {
extern const Icon<24, 24> unchecked;
} // namespace icons
} // namespace espgui

180
src/menudisplay.cpp Normal file
View File

@ -0,0 +1,180 @@
#include "menudisplay.h"
// local includes
#include "tftinstance.h"
namespace espgui {
void MenuDisplay::start()
{
m_selectedIndex = 0;
m_scrollOffset = 0;
m_rotateOffset = 0;
m_pressed = false;
}
void MenuDisplay::initScreen()
{
tft.fillScreen(TFT_BLACK);
m_titleLabel.start();
tft.fillRect(0, 34, tft.width(), 3, TFT_WHITE);
for (auto &label : m_labels)
label.start();
runForEveryMenuItem([](MenuItem &item){
item.start();
});
m_icons.fill(nullptr);
m_highlightedIndex = -1;
}
void MenuDisplay::update()
{
if (!m_pressed)
{
const auto offset = m_rotateOffset;
m_rotateOffset = 0;
const auto itemCount = menuItemCount();
if (itemCount)
{
if (m_selectedIndex == -1)
m_selectedIndex = 0;
m_selectedIndex = m_selectedIndex + offset;
if (m_selectedIndex < 0)
m_selectedIndex += itemCount;
if (m_selectedIndex >= itemCount)
m_selectedIndex -= itemCount;
if (m_selectedIndex < m_scrollOffset)
m_scrollOffset = m_selectedIndex;
if (m_selectedIndex >= m_scrollOffset + m_labels.size())
m_scrollOffset = m_selectedIndex - m_labels.size() + 1;
}
else
{
m_selectedIndex = -1;
m_scrollOffset = 0;
}
runForEveryMenuItem([&](MenuItem &item){
item.update();
});
}
else
{
m_pressed = false;
if (m_selectedIndex >= 0)
getMenuItem(m_selectedIndex).triggered();
}
}
void MenuDisplay::redraw()
{
tft.setTextFont(4);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
m_titleLabel.redraw(text());
int i{0};
auto labelsIter = std::begin(m_labels);
auto iconsIter = std::begin(m_icons);
int newHighlightedIndex{-1};
const auto drawItemRect = [](const auto &label, const auto color){
tft.drawRect(5,
label.y()-1,
240 - 10,
lineHeight+2,
color);
};
runForEveryMenuItem([&](MenuItem &item){
const auto index = i++;
if (index < m_scrollOffset)
return;
if (labelsIter == std::end(m_labels))
return;
const auto relativeIndex = index - m_scrollOffset;
const auto selected = index == m_selectedIndex;
if (selected)
newHighlightedIndex = relativeIndex;
else if (relativeIndex == m_highlightedIndex)
drawItemRect(*labelsIter, TFT_BLACK);
tft.setTextFont(item.font());
tft.setTextColor(item.color(), TFT_BLACK);
labelsIter->redraw(item.text());
if (item.icon() != *iconsIter)
{
tft.fillRect(5, labelsIter->y()+1, 24, 24, TFT_BLACK);
auto icon = item.icon();
if (icon)
{
tft.setSwapBytes(true);
tft.pushImage(6, labelsIter->y()+1, icon->WIDTH, icon->HEIGHT, icon->buffer);
tft.setSwapBytes(false);
}
*iconsIter = icon;
}
if (selected && (relativeIndex != m_highlightedIndex))
{
drawItemRect(*labelsIter, TFT_WHITE);
}
labelsIter++;
iconsIter++;
});
for (; labelsIter != std::end(m_labels); labelsIter++, iconsIter++)
{
const auto relativeIndex = std::distance(std::begin(m_labels), labelsIter);
if (relativeIndex == m_highlightedIndex)
drawItemRect(*labelsIter, TFT_BLACK);
labelsIter->clear();
if (*iconsIter)
{
tft.fillRect(5, labelsIter->y()+1, 24, 24, TFT_BLACK);
*iconsIter = nullptr;
}
}
m_highlightedIndex = newHighlightedIndex;
}
void MenuDisplay::stop()
{
runForEveryMenuItem([](MenuItem &item){
item.stop();
});
}
void MenuDisplay::rotate(int offset)
{
m_rotateOffset += offset;
}
void MenuDisplay::confirm()
{
m_pressed = true;
}
} // namespace espgui

127
src/menudisplay.h Normal file
View File

@ -0,0 +1,127 @@
#pragma once
// system includes
#include <array>
#include <algorithm>
#include <functional>
#include <cassert>
#include <memory>
// local includes
#include "display.h"
#include "textinterface.h"
#include "widgets/label.h"
#include "menuitem.h"
namespace espgui {
class MenuDisplay : public Display, public virtual TextInterface
{
public:
void start() override;
void initScreen() override;
void update() override;
void redraw() override;
void stop() override;
void rotate(int offset) override;
void confirm() override;
TextInterface *asTextInterface() override { return this; }
const TextInterface *asTextInterface() const override { return this; }
MenuDisplay *asMenuDisplay() override { return this; }
const MenuDisplay *asMenuDisplay() const override { return this; }
int selectedIndex() const { return m_selectedIndex; }
std::size_t menuItemCount() const { return m_menuItems.size(); }
MenuItem& getMenuItem(std::size_t index)
{
assert(index < m_menuItems.size());
return *m_menuItems[index].get();
}
const MenuItem& getMenuItem(std::size_t index) const
{
assert(index < m_menuItems.size());
return *m_menuItems[index].get();
}
void runForEveryMenuItem(std::function<void(MenuItem&)> &&callback)
{
for (const auto &ptr : m_menuItems)
callback(*ptr);
}
void runForEveryMenuItem(std::function<void(const MenuItem&)> &&callback) const
{
for (const auto &ptr : m_menuItems)
callback(*ptr);
}
template<typename T, typename... Args>
T &constructMenuItem(Args&&... args)
{
auto ptr = std::make_unique<T>(std::forward<Args>(args)...);
T &ref = *ptr;
emplaceMenuItem(std::move(ptr));
return ref;
}
void emplaceMenuItem(std::unique_ptr<MenuItem> &&ptr)
{
m_menuItems.emplace_back(std::move(ptr));
}
void clearMenuItems()
{
m_menuItems.clear();
}
std::unique_ptr<MenuItem> takeLastMenuItem()
{
assert(!m_menuItems.empty());
std::unique_ptr<MenuItem> ptr = std::move(m_menuItems.back());
m_menuItems.pop_back();
return ptr;
}
protected:
void setSelectedIndex(int selectedIndex) { m_selectedIndex = selectedIndex; }
private:
Label m_titleLabel{5, 5}; // 230, 25
static constexpr auto iconWidth = 25;
static constexpr auto horizontalSpacing = 10;
static constexpr auto topMargin = 40;
static constexpr auto lineHeight = 25;
static constexpr auto verticalSpacing = 3;
std::array<Label, 10> m_labels {{
Label{horizontalSpacing + iconWidth, topMargin+(0*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(1*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(2*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(3*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(4*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(5*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(6*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(7*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(8*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
Label{horizontalSpacing + iconWidth, topMargin+(9*(lineHeight+verticalSpacing))}, // 240-(horizontalSpacing*2)-iconWidth, lineHeight
}};
std::array<const Icon<24, 24> *, 10> m_icons;
int m_selectedIndex;
int m_scrollOffset;
int m_highlightedIndex;
int m_rotateOffset;
bool m_pressed;
std::vector<std::unique_ptr<MenuItem>> m_menuItems;
};
} // namespace espgui

30
src/menuitem.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
// local includes
#include "textinterface.h"
#include "fontinterface.h"
#include "colorinterface.h"
#include "iconinterface.h"
#include "actioninterface.h"
namespace espgui {
using MenuItemIconInterface = IconInterface<24, 24>;
using MenuItemIcon = Icon<24, 24>;
template<const MenuItemIcon *T>
using StaticMenuItemIcon = StaticIcon<24, 24, T>;
class MenuItem :
public virtual ActionInterface,
public virtual TextInterface,
public virtual FontInterface,
public virtual ColorInterface,
public virtual MenuItemIconInterface
{
public:
virtual void start() {}
virtual void update() {}
virtual void stop() {}
};
} // namespace espgui

77
src/textinterface.h Normal file
View File

@ -0,0 +1,77 @@
#pragma once
// system includes
#include <string>
// 3rdparty lib includes
#include <fmt/core.h>
namespace espgui {
class TextInterface {
public:
virtual std::string text() const = 0;
};
class EmptyText : public virtual TextInterface
{
public:
std::string text() const override { return {}; }
};
template<const char *Ttext>
class StaticText : public virtual TextInterface
{
public:
static constexpr const char *STATIC_TEXT = Ttext;
std::string text() const override { return Ttext; }
};
class ChangeableText : public virtual TextInterface
{
public:
std::string text() const override { return m_title; }
void setTitle(std::string &&title) { m_title = std::move(title); }
void setTitle(const std::string &title) { m_title = title; }
private:
std::string m_title;
};
template<typename T>
class CachedText : public virtual T
{
public:
std::string text() const override
{
if (!m_loaded)
{
m_text = T::text();
m_loaded = true;
}
return m_text;
}
private:
mutable bool m_loaded{};
mutable std::string m_text;
};
template<typename T>
class StaticallyCachedText : public virtual T
{
public:
std::string text() const override
{
static const auto text = T::text();
return text;
}
};
template<const char *Tprefix, typename Taccessor>
struct TextWithValueHelper : public virtual TextInterface
{
std::string text() const override { return fmt::format("{} {}", Tprefix, Taccessor{}.getValue()); }
};
} // namespace espgui

5
src/tftinstance.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "tftinstance.h"
namespace espgui {
TFT_eSPI tft;
}

8
src/tftinstance.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
// 3rdparty lib includes
#include <TFT_eSPI.h>
namespace espgui {
extern TFT_eSPI tft;
}

148
src/widgets/graph.h Normal file
View File

@ -0,0 +1,148 @@
#pragma once
// system includes
#include <functional>
// 3rdparty lib includes
#include <ring-buffer.h>
#include <cpputils.h>
// local includes
#include "label.h"
namespace espgui {
template<size_t LENGTH, size_t COUNT>
class Graph
{
static constexpr int labelOffset = -5;
static constexpr int leftMargin = 40;
public:
using Container = std::array<std::reference_wrapper<const ring_buffer<float, LENGTH>>, COUNT>;
static constexpr int WIDTH = LENGTH+40;
Graph(int x, int y, int height);
void start(const Container &buffers);
void redraw(const Container &buffers);
private:
void render(const Container &buffers, bool delta);
const int m_x, m_y, m_height;
std::array<Label, 5> m_labels;
std::array<std::array<int, COUNT>, LENGTH> m_lastPixels;
float m_min;
float m_max;
};
template<size_t LENGTH, size_t COUNT>
Graph<LENGTH, COUNT>::Graph(int x, int y, int height) :
m_x{x}, m_y{y}, m_height{height},
m_labels{
Label{m_x, int(m_y+(m_height/4.f*0)+labelOffset)},
Label{m_x, int(m_y+(m_height/4.f*1)+labelOffset)},
Label{m_x, int(m_y+(m_height/4.f*2)+labelOffset)},
Label{m_x, int(m_y+(m_height/4.f*3)+labelOffset)},
Label{m_x, int(m_y+(m_height/4.f*4)+labelOffset)},
}
{
}
template<size_t LENGTH, size_t COUNT>
void Graph<LENGTH, COUNT>::start(const Container &buffers)
{
m_min = 0.f;
m_max = 10.f;
tft.drawFastVLine(m_x+leftMargin-1, m_y, m_height, TFT_WHITE);
for (auto iter = std::begin(m_labels); iter != std::end(m_labels); iter++)
{
tft.drawFastHLine(m_x+leftMargin-5, float(m_y)+(float(m_height)/(m_labels.size()-1)*std::distance(std::begin(m_labels), iter)), 4, TFT_WHITE);
iter->start();
}
render(buffers, false);
}
template<size_t LENGTH, size_t COUNT>
void Graph<LENGTH, COUNT>::redraw(const Container &buffers)
{
render(buffers, true);
}
template<size_t LENGTH, size_t COUNT>
void Graph<LENGTH, COUNT>::render(const Container &buffers, bool delta)
{
float min{std::numeric_limits<float>::quiet_NaN()}, max{std::numeric_limits<float>::quiet_NaN()};
bool first{true};
for (const ring_buffer<float, LENGTH> &buffer : buffers)
{
auto minmax = std::minmax_element(std::cbegin(buffer), std::cend(buffer));
if (first || *minmax.first < min)
min = *minmax.first;
if (first || *minmax.second > max)
max = *minmax.second;
first = false;
}
if (min < m_min)
m_min = min*0.9f;
else if (min > m_min*1.1f)
m_min = min*1.1f;
if (max > m_max)
m_max = max*1.1f;
else if (max < m_max*0.9f)
m_max = max*1.1f;
if (m_max-m_min < 2.f)
{
m_min-=1.f;
m_max+=1.f;
}
if (m_min > 0 && m_max > 0)
m_min = 0;
if (m_min < 0 && m_max < 0)
m_max = 0;
tft.setTextFont(2);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
for (auto iter = std::begin(m_labels); iter != std::end(m_labels); iter++)
iter->redraw(std::to_string(int(m_max+((m_min-m_max)/(m_labels.size()-1)*std::distance(std::begin(m_labels), iter)))));
int x{leftMargin};
for (auto pixelsIter = std::begin(m_lastPixels); pixelsIter!=std::end(m_lastPixels); pixelsIter++)
{
const auto index0 = std::distance(std::begin(m_lastPixels), pixelsIter);
auto &pixels = *pixelsIter;
for (auto iter = std::begin(pixels); iter != std::end(pixels); iter++)
{
const auto index1 = std::distance(std::begin(pixels), iter);
const std::reference_wrapper<const ring_buffer<float, LENGTH>> &ref = *(std::begin(buffers)+index1);
const ring_buffer<float, LENGTH> &buffer = ref.get();
const float &val = *(std::begin(buffer)+index0);
int y = cpputils::mapValueClamped<float>(val, m_min, m_max, m_y+m_height-1, m_y+1);
if (!delta || *iter != y)
{
if (delta)
tft.drawFastVLine(x, *iter-1, 3, TFT_BLACK);
tft.drawFastVLine(x, y-1, 3, TFT_WHITE);
*iter = y;
}
}
x++;
}
}
} // namespace espgui

59
src/widgets/label.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "label.h"
// local includes
#include "tftinstance.h"
namespace espgui {
Label::Label(int x, int y) :
m_x{x},
m_y{y}
{
}
void Label::start()
{
m_lastStr.clear();
m_lastFont = -1;
m_lastColor = -1;
m_lastWidth = 0;
m_lastHeight = 0;
}
void Label::redraw(std::string_view str, bool forceRedraw)
{
if (m_lastStr == str &&
m_lastFont == tft.textfont &&
m_lastColor == tft.textcolor &&
!forceRedraw)
return;
const auto renderedWidth = tft.drawString(str.data(), m_x, m_y);
const auto renderedHeight = tft.fontHeight();
if (renderedWidth < m_lastWidth)
tft.fillRect(m_x + renderedWidth, m_y,
m_lastWidth - renderedWidth, m_lastHeight,
tft.textbgcolor);
if (renderedHeight < m_lastHeight)
tft.fillRect(m_x, m_y + renderedHeight,
renderedWidth, m_lastHeight - renderedHeight,
tft.textbgcolor);
m_lastStr = str;
m_lastFont = tft.textfont;
m_lastColor = tft.textcolor;
m_lastWidth = renderedWidth;
m_lastHeight = renderedHeight;
}
void Label::clear()
{
if (m_lastWidth || m_lastHeight)
tft.fillRect(m_x, m_y, m_lastWidth, m_lastHeight, tft.textbgcolor);
start();
}
}

30
src/widgets/label.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
// system includes
#include <string>
namespace espgui {
class Label
{
public:
Label(int x, int y);
int x() const { return m_x; };
int y() const { return m_y; };
void start();
void redraw(std::string_view str, bool forceRedraw = false);
void clear();
private:
const int m_x;
const int m_y;
std::string m_lastStr;
int m_lastFont;
int m_lastColor;
int m_lastWidth;
int m_lastHeight;
};
} // namespace espgui

View File

@ -0,0 +1,32 @@
#include "progressbar.h"
// 3rdparty lib includes
#include <cpputils.h>
// local includes
#include "tftinstance.h"
namespace espgui {
ProgressBar::ProgressBar(int x, int y, int width, int height, int min, int max, uint32_t color) :
m_x{x}, m_y{y}, m_width{width}, m_height{height}, m_min{min}, m_max{max}, m_color{color}
{
}
void ProgressBar::start()
{
m_lastValue = m_x+1;
tft.drawRect(m_x, m_y, m_width, m_height, TFT_WHITE);
}
void ProgressBar::redraw(int value)
{
value = cpputils::mapValueClamped(value, m_min, m_max, m_x+1, m_x+m_width-1);
if (value < m_lastValue)
tft.fillRect(value, m_y+1, m_lastValue-value, m_height-2, TFT_BLACK);
else if (value > m_lastValue)
tft.fillRect(m_lastValue, m_y+1, value-m_lastValue, m_height-2, m_color);
m_lastValue = value;
}
} // namespace espgui

29
src/widgets/progressbar.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
// system includes
#include <cstdint>
// 3rdparty lib includes
#include <TFT_eSPI.h>
namespace espgui {
class ProgressBar
{
public:
ProgressBar(int x, int y, int width, int height, int min, int max, uint32_t color=TFT_YELLOW);
void start();
void redraw(int value);
private:
const int m_x;
const int m_y;
const int m_width;
const int m_height;
const int m_min;
const int m_max;
const uint32_t m_color;
int m_lastValue{};
};
} // namespace espgui

View File

@ -0,0 +1,32 @@
#include "reverseprogressbar.h"
// 3rdparty lib includes
#include <cpputils.h>
// local includes
#include "tftinstance.h"
namespace espgui {
ReverseProgressBar::ReverseProgressBar(int x, int y, int width, int height, int min, int max, uint32_t color) :
m_x{x}, m_y{y}, m_width{width}, m_height{height}, m_min{min}, m_max{max}, m_color{color}
{
}
void ReverseProgressBar::start()
{
m_lastValue = m_x+m_width-1;
tft.drawRect(m_x, m_y, m_width, m_height, TFT_WHITE);
}
void ReverseProgressBar::redraw(int value)
{
value = cpputils::mapValueClamped(value, m_min, m_max, m_x+m_width-1, m_x+1);
if (value < m_lastValue)
tft.fillRect(value, m_y+1, m_lastValue-value, m_height-2, m_color);
else if (value > m_lastValue)
tft.fillRect(m_lastValue, m_y+1, value-m_lastValue, m_height-2, TFT_BLACK);
m_lastValue = value;
}
} // namespace espgui

View File

@ -0,0 +1,29 @@
#pragma once
// system includes
#include <cstdint>
// 3rdparty lib includes
#include <TFT_eSPI.h>
namespace espgui {
class ReverseProgressBar
{
public:
ReverseProgressBar(int x, int y, int width, int height, int min, int max, uint32_t color=TFT_YELLOW);
void start();
void redraw(int value);
private:
const int m_x;
const int m_y;
const int m_width;
const int m_height;
const int m_min;
const int m_max;
const uint32_t m_color;
int m_lastValue{};
};
} // namespace espgui

View File

@ -0,0 +1,62 @@
#include "verticalmeter.h"
// 3rdparty lib includes
#include <cpputils.h>
// local includes
#include "tftinstance.h"
namespace espgui {
VerticalMeter::VerticalMeter(const char *text, const char *format, int x, int y) :
m_text{text}, m_format{format}, m_x{x}, m_y{y}
{
}
void VerticalMeter::start()
{
int w = 36;
tft.drawRect(m_x, m_y, w, 155, TFT_GREY);
tft.fillRect(m_x + 2, m_y + 19, w - 3, 155 - 38, TFT_WHITE);
tft.setTextColor(TFT_CYAN, TFT_BLACK);
tft.drawCentreString(m_text, m_x + w / 2, m_y + 2, 2);
for (int i = 0; i < 110; i += 10)
tft.drawFastHLine(m_x + 20, m_y + 27 + i, 6, TFT_BLACK);
for (int i = 0; i < 110; i += 50)
tft.drawFastHLine(m_x + 20, m_y + 27 + i, 9, TFT_BLACK);
tft.fillTriangle(m_x + 3, m_y + 127, m_x + 3 + 16, m_y + 127, m_x + 3, m_y + 127 - 5, TFT_RED);
tft.fillTriangle(m_x + 3, m_y + 127, m_x + 3 + 16, m_y + 127, m_x + 3, m_y + 127 + 5, TFT_RED);
tft.drawCentreString("---", m_x + w / 2, m_y + 155 - 18, 2);
}
void VerticalMeter::redraw(float value, float min, float max)
{
tft.setTextColor(TFT_GREEN, TFT_BLACK);
char buf[16];
snprintf(&buf[0], 16, m_format, value);
tft.drawRightString(buf, m_x + 36 - 5, 187 - 27 + 155 - 18, 2);
const int dx = 3 + m_x;
value = cpputils::mapValueClamped<float>(value, min, max, 0.f, 100.f);
while (m_oldValue > value)
{
const int dy = 187 + 100 - m_oldValue;
tft.drawLine(dx, dy - 5, dx + 16, dy, TFT_WHITE);
m_oldValue--;
tft.drawLine(dx, dy + 6, dx + 16, dy + 1, TFT_RED);
}
while (m_oldValue < value)
{
const int dy = 187 + 100 - m_oldValue;
tft.drawLine(dx, dy + 5, dx + 16, dy, TFT_WHITE);
m_oldValue++;
tft.drawLine(dx, dy - 6, dx + 16, dy - 1, TFT_RED);
}
}
}

View File

@ -0,0 +1,23 @@
#pragma once
// system includes
#include <cstdint>
namespace espgui {
class VerticalMeter
{
public:
VerticalMeter(const char *text, const char *format, int x, int y);
void start();
void redraw(float value, float min, float max);
private:
const char * const m_text;
const char * const m_format;
const int m_x;
const int m_y;
float m_oldValue{};
};
} // namespace espgui

135
src/widgets/vumeter.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "vumeter.h"
// local includes
#include "tftinstance.h"
namespace espgui {
void VuMeter::start()
{
ltx = 0;
osx = 120;
osy = 120;
// Meter outline
tft.fillRect(0, 0, 239, 126, TFT_GREY);
tft.fillRect(5, 3, 230, 119, TFT_WHITE);
tft.setTextColor(TFT_BLACK); // Text colour
// Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
for (int i = -50; i < 51; i += 5) {
// Long scale tick length
int tl = 15;
// Coodinates of tick to draw
float sx = cos((i - 90) * 0.0174532925);
float sy = sin((i - 90) * 0.0174532925);
uint16_t x0 = sx * (100 + tl) + 120;
uint16_t y0 = sy * (100 + tl) + 140;
uint16_t x1 = sx * 100 + 120;
uint16_t y1 = sy * 100 + 140;
// Coordinates of next tick for zone fill
float sx2 = cos((i + 5 - 90) * 0.0174532925);
float sy2 = sin((i + 5 - 90) * 0.0174532925);
int x2 = sx2 * (100 + tl) + 120;
int y2 = sy2 * (100 + tl) + 140;
int x3 = sx2 * 100 + 120;
int y3 = sy2 * 100 + 140;
// Yellow zone limits
//if (i >= -50 && i < 0) {
// tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW);
// tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW);
//}
// Green zone limits
if (i >= 0 && i < 25) {
tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN);
}
// Orange zone limits
if (i >= 25 && i < 50) {
tft.fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE);
tft.fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE);
}
// Short scale tick length
if (i % 25 != 0) tl = 8;
// Recalculate coords incase tick lenght changed
x0 = sx * (100 + tl) + 120;
y0 = sy * (100 + tl) + 140;
x1 = sx * 100 + 120;
y1 = sy * 100 + 140;
// Draw tick
tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
// Check if labels should be drawn, with position tweaks
if (i % 25 == 0) {
// Calculate label positions
x0 = sx * (100 + tl + 10) + 120;
y0 = sy * (100 + tl + 10) + 140;
switch (i / 25) {
case -2: tft.drawCentreString("0", x0, y0 - 12, 2); break;
case -1: tft.drawCentreString("7.5", x0, y0 - 9, 2); break;
case 0: tft.drawCentreString("15", x0, y0 - 6, 2); break;
case 1: tft.drawCentreString("22.5", x0, y0 - 9, 2); break;
case 2: tft.drawCentreString("30", x0, y0 - 12, 2); break;
}
}
// Now draw the arc of the scale
sx = cos((i + 5 - 90) * 0.0174532925);
sy = sin((i + 5 - 90) * 0.0174532925);
x0 = sx * 100 + 120;
y0 = sy * 100 + 140;
// Draw scale arc, don't draw the last part
if (i < 50) tft.drawLine(x0, y0, x1, y1, TFT_BLACK);
}
tft.drawString("KM/h", 5 + 230 - 40, 119 - 20, 2); // Units at bottom right
tft.drawCentreString("KM/h", 120, 70, 4); // Comment out to avoid font 4
tft.drawRect(5, 3, 230, 119, TFT_BLACK); // Draw bezel line
}
void VuMeter::redraw(float value)
{
tft.setTextColor(TFT_BLACK, TFT_WHITE);
char buf[8]; dtostrf(value, 4, 0, buf);
tft.drawRightString(buf, 50, 119 - 25, 4);
if (value < -3) value = -3; // Limit value to emulate needle end stops
if (value > 33) value = 33;
float sdeg = map(value, -3, 33, -150, -30); // Map value to angle
// Calcualte tip of needle coords
float sx = cos(sdeg * 0.0174532925);
float sy = sin(sdeg * 0.0174532925);
// Calculate x delta of needle start (does not start at pivot point)
float tx = tan((sdeg + 90) * 0.0174532925);
// Erase old needle image
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_WHITE);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_WHITE);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_WHITE);
// Re-plot text under needle
tft.setTextColor(TFT_BLACK);
tft.drawCentreString("KM/h", 120, 70, 4); // // Comment out to avoid font 4
// Store new needle end coords for next erase
ltx = tx;
osx = sx * 98 + 120;
osy = sy * 98 + 140;
// Draw the needle in the new postion, magenta makes needle a bit bolder
// draws 3 lines to thicken needle
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, TFT_RED);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, TFT_MAGENTA);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, TFT_RED);
}
} // namespace espgui

17
src/widgets/vumeter.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
// system includes
#include <cstdint>
namespace espgui {
class VuMeter
{
public:
void start();
void redraw(float value);
private:
float ltx; // Saved x coord of bottom of needle
uint16_t osx, osy; // Saved x & y coords
};
} // namespace espgui