Functional menu
This commit is contained in:
parent
5b7ef1c24f
commit
06839fa806
6 changed files with 160 additions and 30 deletions
92
src/Menu.cpp
92
src/Menu.cpp
|
@ -5,7 +5,10 @@
|
||||||
|
|
||||||
RTC_DATA_ATTR WatchyDisplay * Menu::m_display = nullptr;
|
RTC_DATA_ATTR WatchyDisplay * Menu::m_display = nullptr;
|
||||||
RTC_DATA_ATTR std::function<void()> Menu::m_exitCallback = nullptr;
|
RTC_DATA_ATTR std::function<void()> Menu::m_exitCallback = nullptr;
|
||||||
RTC_DATA_ATTR uint8_t Menu::m_numPages = 0, Menu::m_currentPage = 0, Menu::m_numMenuItemsinCurrentPage = 0, Menu::m_currentMenuItem = 0;
|
RTC_DATA_ATTR uint8_t Menu::m_numPages = 0;
|
||||||
|
RTC_DATA_ATTR uint8_t Menu::m_currentPage = 0;
|
||||||
|
RTC_DATA_ATTR uint8_t Menu::m_numMenuItemsinCurrentPage = 0;
|
||||||
|
RTC_DATA_ATTR uint8_t Menu::m_currentMenuItem = 0;
|
||||||
|
|
||||||
Menu::Menu()
|
Menu::Menu()
|
||||||
{
|
{
|
||||||
|
@ -29,6 +32,7 @@ void Menu::HandleButtonPress(uint64_t buttonMask)
|
||||||
if (buttonMask & BACK_BTN_MASK) {
|
if (buttonMask & BACK_BTN_MASK) {
|
||||||
if (m_exitCallback) {
|
if (m_exitCallback) {
|
||||||
m_exitCallback();
|
m_exitCallback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,23 +49,45 @@ void Menu::HandleButtonPress(uint64_t buttonMask)
|
||||||
if (m_currentMenuItem > 0) {
|
if (m_currentMenuItem > 0) {
|
||||||
m_currentMenuItem--;
|
m_currentMenuItem--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Redraw(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonMask & DOWN_BTN_MASK) {
|
if (buttonMask & DOWN_BTN_MASK) {
|
||||||
if (m_currentMenuItem < m_pages[m_currentPage].menuItems.size() - 1) {
|
if (m_currentMenuItem < m_numMenuItemsinCurrentPage - 1) {
|
||||||
m_currentMenuItem++;
|
m_currentMenuItem++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Redraw(true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonMask & MENU_BTN_MASK) {
|
if (buttonMask & MENU_BTN_MASK) {
|
||||||
if (m_pages[m_currentPage].menuItems[m_currentMenuItem].callback) {
|
if (m_pages[m_currentPage].menuItems[m_currentMenuItem].callback != nullptr) {
|
||||||
m_pages[m_currentPage].menuItems[m_currentMenuItem].callback();
|
m_pages[m_currentPage].menuItems[m_currentMenuItem].callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pages[m_currentPage].menuItems[m_currentMenuItem].pageNum == 0 && m_currentPage == 0 && m_exitCallback) {
|
||||||
|
m_exitCallback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pages[m_currentPage].menuItems[m_currentMenuItem].pageNum > 0) {
|
|
||||||
m_currentPage = m_pages[m_currentPage].menuItems[m_currentMenuItem].pageNum;
|
m_currentPage = m_pages[m_currentPage].menuItems[m_currentMenuItem].pageNum;
|
||||||
m_currentMenuItem = 0;
|
m_currentMenuItem = 0;
|
||||||
|
|
||||||
|
Redraw(false);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (buttonMask & BACK_BTN_MASK) {
|
||||||
|
m_currentPage = m_pages[m_currentPage].backPageNum;
|
||||||
|
m_currentMenuItem = 0;
|
||||||
|
|
||||||
|
Redraw(false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,16 +95,20 @@ void Menu::SetPages(const std::vector<MenuPage> & pages)
|
||||||
{
|
{
|
||||||
// If the number of pages has changed, reset the current page and menu item.
|
// If the number of pages has changed, reset the current page and menu item.
|
||||||
if (pages.size() != m_numPages) {
|
if (pages.size() != m_numPages) {
|
||||||
|
Serial.println("Resetting current page");
|
||||||
m_currentPage = 0;
|
m_currentPage = 0;
|
||||||
m_currentMenuItem = 0;
|
m_currentMenuItem = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the number of menu items has changed, reset the current menu item.
|
// If the number of menu items has changed, reset the current menu item.
|
||||||
if (pages.size() > 0 && pages[m_currentMenuItem].menuItems.size() != m_numMenuItemsinCurrentPage) {
|
if (pages.size() > 0 && pages[m_currentMenuItem].menuItems.size() != m_numMenuItemsinCurrentPage) {
|
||||||
|
Serial.println("Resetting current menu item");
|
||||||
m_currentMenuItem = 0;
|
m_currentMenuItem = 0;
|
||||||
|
m_numMenuItemsinCurrentPage = pages[m_currentPage].menuItems.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pages = pages;
|
m_pages = pages;
|
||||||
|
m_numPages = pages.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::SetExitCallback(std::function<void()> callback)
|
void Menu::SetExitCallback(std::function<void()> callback)
|
||||||
|
@ -101,6 +131,10 @@ void Menu::Redraw(bool partialRefresh)
|
||||||
|
|
||||||
if (m_pages.size() > 0) {
|
if (m_pages.size() > 0) {
|
||||||
bool hasTitle = false;
|
bool hasTitle = false;
|
||||||
|
bool hasBody = false;
|
||||||
|
|
||||||
|
uint16_t top = 5;
|
||||||
|
|
||||||
if (m_pages[m_currentPage].title.length() > 0) {
|
if (m_pages[m_currentPage].title.length() > 0) {
|
||||||
hasTitle = true;
|
hasTitle = true;
|
||||||
|
|
||||||
|
@ -109,6 +143,7 @@ void Menu::Redraw(bool partialRefresh)
|
||||||
int16_t x, y;
|
int16_t x, y;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
|
|
||||||
|
m_display->setTextWrap(false);
|
||||||
m_display->getTextBounds(m_pages[m_currentPage].title.c_str(), 0, 0, &x, &y, &w, &h);
|
m_display->getTextBounds(m_pages[m_currentPage].title.c_str(), 0, 0, &x, &y, &w, &h);
|
||||||
|
|
||||||
if (m_pages[m_currentPage].titleAlignment == ALIGNMENT_LEFT) { // Left
|
if (m_pages[m_currentPage].titleAlignment == ALIGNMENT_LEFT) { // Left
|
||||||
|
@ -121,9 +156,56 @@ void Menu::Redraw(bool partialRefresh)
|
||||||
|
|
||||||
m_display->setCursor(x, 15);
|
m_display->setCursor(x, 15);
|
||||||
m_display->print(m_pages[m_currentPage].title.c_str());
|
m_display->print(m_pages[m_currentPage].title.c_str());
|
||||||
m_display->drawLine(5, 25, m_display->width() - 10, 25, GxEPD_BLACK);
|
m_display->drawLine(5, 20, m_display->width() - 10, 20, GxEPD_BLACK);
|
||||||
|
|
||||||
|
top += 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pages[m_currentPage].body.length() > 0) {
|
||||||
|
m_display->setFont(&FreeSans9pt7b);
|
||||||
|
m_display->setTextColor(GxEPD_BLACK);
|
||||||
|
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t w, h;
|
||||||
|
m_display->setTextWrap(false);
|
||||||
|
m_display->getTextBounds(m_pages[m_currentPage].body.c_str(), 0, 0, &x, &y, &w, &h);
|
||||||
|
m_display->setCursor(5, top);
|
||||||
|
m_display->print(m_pages[m_currentPage].body.c_str());
|
||||||
|
top += h - 5;
|
||||||
|
m_display->drawLine(5, top, m_display->width() - 10, top, GxEPD_BLACK);
|
||||||
|
top += 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < m_numMenuItemsinCurrentPage; i++) {
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t w, h;
|
||||||
|
m_display->getTextBounds(m_pages[m_currentPage].menuItems[m_currentMenuItem].title.c_str(), 5, top, &x, &y, &w, &h);
|
||||||
|
|
||||||
|
if (i == m_currentMenuItem) {
|
||||||
|
m_display->fillRect(5, top, m_display->width() - 15, h + 6, GxEPD_BLACK);
|
||||||
|
m_display->setTextColor(GxEPD_WHITE);
|
||||||
|
} else {
|
||||||
|
m_display->setTextColor(GxEPD_BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_display->setFont(&FreeSans9pt7b);
|
||||||
|
m_display->setCursor(x, top + 15);
|
||||||
|
m_display->print(m_pages[m_currentPage].menuItems[i].title.c_str());
|
||||||
|
|
||||||
|
top += h + 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_display->display(partialRefresh);
|
m_display->display(partialRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::Reset()
|
||||||
|
{
|
||||||
|
m_currentPage = 0;
|
||||||
|
m_currentMenuItem = 0;
|
||||||
|
m_numPages = m_pages.size();
|
||||||
|
|
||||||
|
if (m_numPages) {
|
||||||
|
m_numMenuItemsinCurrentPage = m_pages[m_currentPage].menuItems.size();
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,10 +22,10 @@ enum MenuTextAlignment {
|
||||||
|
|
||||||
struct MenuPage
|
struct MenuPage
|
||||||
{
|
{
|
||||||
|
uint8_t backPageNum;
|
||||||
std::string title;
|
std::string title;
|
||||||
MenuTextAlignment titleAlignment;
|
MenuTextAlignment titleAlignment;
|
||||||
std::string body;
|
std::string body;
|
||||||
MenuTextAlignment bodyAlignment;
|
|
||||||
std::vector<MenuItem> menuItems;
|
std::vector<MenuItem> menuItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ public:
|
||||||
void SetPages(const std::vector<MenuPage> & pages);
|
void SetPages(const std::vector<MenuPage> & pages);
|
||||||
void SetExitCallback(std::function<void()> callback);
|
void SetExitCallback(std::function<void()> callback);
|
||||||
void Redraw(bool partialRefresh = false);
|
void Redraw(bool partialRefresh = false);
|
||||||
|
void Reset();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,6 @@ RTC_DATA_ATTR bool WatchFace::m_menuSetup = false;
|
||||||
RTC_DATA_ATTR bool WatchFace::m_inMenu = false;
|
RTC_DATA_ATTR bool WatchFace::m_inMenu = false;
|
||||||
Menu WatchFace::m_menu;
|
Menu WatchFace::m_menu;
|
||||||
|
|
||||||
static const std::vector<MenuPage> menuPages = {
|
|
||||||
{
|
|
||||||
"Watchy",
|
|
||||||
ALIGNMENT_CENTER,
|
|
||||||
"",
|
|
||||||
ALIGNMENT_CENTER,
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"Sync NTP",
|
|
||||||
nullptr,
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void WatchFace::Setup() // Called after hardware is set up
|
void WatchFace::Setup() // Called after hardware is set up
|
||||||
{
|
{
|
||||||
if (!m_menuSetup) {
|
if (!m_menuSetup) {
|
||||||
|
@ -29,8 +13,7 @@ void WatchFace::Setup() // Called after hardware is set up
|
||||||
m_menu.Init(m_display);
|
m_menu.Init(m_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_menu.SetPages(menuPages);
|
SetupVolatileMenuStuff();
|
||||||
m_menu.SetExitCallback(std::bind(&WatchFace::MenuExited, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WatchFace::HandleButtonPress(uint64_t buttonMask)
|
void WatchFace::HandleButtonPress(uint64_t buttonMask)
|
||||||
|
@ -102,6 +85,51 @@ void WatchFace::DrawBatteryIcon()
|
||||||
m_display.fillRect(200 - 44, 9, (int)std::round(35.0f * level), 15, GxEPD_BLACK);
|
m_display.fillRect(200 - 44, 9, (int)std::round(35.0f * level), 15, GxEPD_BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WatchFace::SetupVolatileMenuStuff()
|
||||||
|
{
|
||||||
|
static const std::vector<MenuPage> menuPages = {
|
||||||
|
{
|
||||||
|
0, // backPageNum
|
||||||
|
"WATCHY", // title
|
||||||
|
ALIGNMENT_CENTER, // titleAlignment
|
||||||
|
"", // body
|
||||||
|
{ // Menu items
|
||||||
|
{
|
||||||
|
"Sync NTP", // title
|
||||||
|
nullptr, // callback
|
||||||
|
1 // pageNum
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Back", // title
|
||||||
|
nullptr, // callback
|
||||||
|
0 // pageNum
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
0, // backPageNum
|
||||||
|
"SYNC NTP", // title
|
||||||
|
ALIGNMENT_CENTER, // titleAlignment
|
||||||
|
"Sync with:\n" NTP_SERVER, // body
|
||||||
|
{ // Menu items
|
||||||
|
{
|
||||||
|
"Sync", // title
|
||||||
|
std::bind(&WatchFace::MenuNTPSyncSelected, this), // callback
|
||||||
|
1 // pageNum
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Back", // title
|
||||||
|
nullptr, // callback
|
||||||
|
0 // pageNum
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
m_menu.SetPages(menuPages);
|
||||||
|
m_menu.SetExitCallback(std::bind(&WatchFace::MenuExited, this));
|
||||||
|
}
|
||||||
|
|
||||||
void WatchFace::MenuExited()
|
void WatchFace::MenuExited()
|
||||||
{
|
{
|
||||||
if (m_inMenu) {
|
if (m_inMenu) {
|
||||||
|
@ -109,3 +137,16 @@ void WatchFace::MenuExited()
|
||||||
DrawWatchFace(false);
|
DrawWatchFace(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WatchFace::MenuNTPSyncSelected()
|
||||||
|
{
|
||||||
|
ConnectWiFi();
|
||||||
|
SyncNTPTime();
|
||||||
|
DisconnectWiFi();
|
||||||
|
|
||||||
|
if (m_inMenu) {
|
||||||
|
m_inMenu = false;
|
||||||
|
m_menu.Reset();
|
||||||
|
DrawWatchFace(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ private:
|
||||||
RTC_DATA_ATTR static bool m_inMenu;
|
RTC_DATA_ATTR static bool m_inMenu;
|
||||||
RTC_DATA_ATTR static Menu m_menu;
|
RTC_DATA_ATTR static Menu m_menu;
|
||||||
|
|
||||||
|
void SetupVolatileMenuStuff();
|
||||||
void DrawBatteryIcon();
|
void DrawBatteryIcon();
|
||||||
void MenuExited();
|
void MenuExited();
|
||||||
|
void MenuNTPSyncSelected();
|
||||||
};
|
};
|
|
@ -119,7 +119,7 @@ void Watchy::SyncNTPTime()
|
||||||
{
|
{
|
||||||
WiFiUDP ntpUDP;
|
WiFiUDP ntpUDP;
|
||||||
// GMT offset should be, RTC class will adjust to local time
|
// GMT offset should be, RTC class will adjust to local time
|
||||||
NTPClient timeClient(ntpUDP, "pool.ntp.org", 0);
|
NTPClient timeClient(ntpUDP, NTP_SERVER, 0);
|
||||||
timeClient.begin();
|
timeClient.begin();
|
||||||
bool success = timeClient.forceUpdate();
|
bool success = timeClient.forceUpdate();
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
#define MENU_BTN_PIN 26
|
#define MENU_BTN_PIN 26
|
||||||
#define BACK_BTN_PIN 25
|
#define BACK_BTN_PIN 25
|
||||||
#define DOWN_BTN_PIN 4
|
#define DOWN_BTN_PIN 4
|
||||||
|
@ -22,6 +24,8 @@
|
||||||
#define DISPLAY_WIDTH 200
|
#define DISPLAY_WIDTH 200
|
||||||
#define DISPLAY_HEIGHT 200
|
#define DISPLAY_HEIGHT 200
|
||||||
|
|
||||||
#define WIFI_SSID "<my ssid>>"
|
#define WIFI_SSID "<ssid>"
|
||||||
#define WIFI_PASS "<my pass>"
|
#define WIFI_PASS "<pass>"
|
||||||
#define TZ_OFFSET 3600 * 3
|
#define TZ_OFFSET 3600 * 3
|
||||||
|
|
||||||
|
#define NTP_SERVER "pool.ntp.org"
|
Loading…
Reference in a new issue