Adam PIppin
3 years ago
13 changed files with 558 additions and 50 deletions
@ -0,0 +1,11 @@ |
|||
#include "Module_Menu.h" |
|||
|
|||
#define MENU_EAT_OFFSET 3 |
|||
|
|||
// Maximum number of items any menu can contain
|
|||
#define MODULE_MENU_MAX_OPTIONS 16 |
|||
// How many menu items to show on screen at once
|
|||
#define MODULE_MENU_SHOW_OPTIONS 8 |
|||
// How long menu labels can be
|
|||
#define MODULE_MENU_MAX_OPTION_LENGTH 15 |
|||
|
@ -0,0 +1,25 @@ |
|||
#ifndef MODULE_MENU_H |
|||
#define MODULE_MENU_H |
|||
|
|||
#include "watchos.h" |
|||
|
|||
struct MenuItem; |
|||
|
|||
class Module_Menu : public Module |
|||
{ |
|||
ButtonState* m_button[MODULE_INPUT_BUTTONS]; |
|||
|
|||
void poll(); |
|||
|
|||
public: |
|||
Module_Input(); |
|||
void initialize(); |
|||
void suspend(); |
|||
int getId(); |
|||
void tick(); |
|||
|
|||
bool isAnyPressed(); |
|||
bool isPressed(byte button); |
|||
}; |
|||
|
|||
#endif |
@ -0,0 +1,312 @@ |
|||
#include "watchos.h" |
|||
#include "Module_EAT.h" |
|||
#include "Module_UI.h" |
|||
#include "watchos_fonts.h" |
|||
#include "Events.h" |
|||
|
|||
|
|||
|
|||
struct MenuItem |
|||
{ |
|||
byte id; |
|||
char label[MENU_MAX_OPTION_LENGTH]; |
|||
int window_hwnd; |
|||
}; |
|||
|
|||
/*
|
|||
* EAT Layout |
|||
* byte selected_idx |
|||
* byte view_offset |
|||
* |
|||
* each entry: |
|||
* byte id |
|||
* char[31] label |
|||
*/ |
|||
|
|||
class Task_Menu : public Task |
|||
{ |
|||
private: |
|||
int m_hwnd; |
|||
Module_UI* ui; |
|||
Module_EAT* eat; |
|||
MenuItem* m_item[MENU_MAX_OPTIONS]; |
|||
int m_hwnd_menu_option[MENU_SHOW_OPTIONS]; |
|||
int m_prev_root_hwnd; |
|||
|
|||
int m_view_offset = 0; |
|||
int m_selected = 0; |
|||
|
|||
bool m_result_ok = false; |
|||
bool m_result_back = false; |
|||
|
|||
public: |
|||
Task_Menu() |
|||
{ |
|||
ui = (Module_UI*)Kernel::get()->getModule(MODULE_UI); |
|||
m_hwnd = ui->createWindow((Task*)this, -1, 0); |
|||
ui->setLayoutMode(m_hwnd, MODULE_UI_LAYOUT_MODE_SPLIT_HORIZONTAL); |
|||
for (int i = 0; i < MENU_SHOW_OPTIONS; i++) |
|||
{ |
|||
m_hwnd_menu_option[i] = ui->createWindow((Task*)this, m_hwnd, 0); |
|||
} |
|||
|
|||
for (int i = 0; i < MENU_MAX_OPTIONS; i++) |
|||
{ |
|||
m_item[i] = nullptr; |
|||
} |
|||
|
|||
eat = (Module_EAT*)Kernel::get()->getModule(MODULE_EAT); |
|||
int item_length = (1 + MENU_MAX_OPTION_LENGTH); |
|||
if (!eat->allocate(MENU_EAT_ID, MENU_EAT_OFFSET + item_length * MENU_MAX_OPTIONS)) |
|||
{ |
|||
m_selected = eat->read(MENU_EAT_ID, 0); |
|||
m_view_offset = eat->read(MENU_EAT_ID, 1); |
|||
m_prev_root_hwnd = eat->read(MENU_EAT_ID, 2); |
|||
|
|||
byte id; |
|||
for (int i = 0; i < MENU_MAX_OPTIONS; i++) |
|||
{ |
|||
id = eat->read(MENU_EAT_ID, MENU_EAT_OFFSET + (i * item_length)); |
|||
if (id != 0xFF) |
|||
{ |
|||
m_item[i] = new MenuItem(); |
|||
m_item[i]->id = id; |
|||
for (int j = 0; j < MENU_MAX_OPTION_LENGTH; j++) |
|||
{ |
|||
m_item[i]->label[j] = (char)eat->read(MENU_EAT_ID, MENU_EAT_OFFSET + (i * item_length) + j + 1); |
|||
if (m_item[i]->label[j] == '\0') |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void initialize() |
|||
{ |
|||
ui->setDirty(m_hwnd); |
|||
for (int i = 0; i < MENU_SHOW_OPTIONS; i++) |
|||
{ |
|||
ui->setDirty(m_hwnd_menu_option[i]); |
|||
} |
|||
} |
|||
|
|||
void tick(unsigned int signal) |
|||
{ |
|||
Event* e; |
|||
int prev_selected = -1; |
|||
byte scroll_direction = 128; // not const'ing this... 128=middle, 0=up, 255=down
|
|||
while (this->hasEvent()) |
|||
{ |
|||
e = this->popEvent(); |
|||
|
|||
if (ui->getRootWindow() != m_hwnd) continue; |
|||
|
|||
if (e->source == MODULE_INPUT && e->event == MODULE_INPUT_EVENT_PRESS) |
|||
{ |
|||
if ((e->param1 & MODULE_INPUT_BUTTON_UP) != 0) |
|||
{ |
|||
Kernel::debug("Scroll up"); |
|||
prev_selected = m_selected; |
|||
m_selected -= 1; |
|||
if (m_selected < 0) |
|||
m_selected = 0; |
|||
else |
|||
scroll_direction = 0; |
|||
|
|||
} |
|||
if ((e->param1 & MODULE_INPUT_BUTTON_DOWN) != 0) |
|||
{ |
|||
Kernel::debug("Scroll down"); |
|||
prev_selected = m_selected; |
|||
m_selected += 1; |
|||
if (m_item[m_selected] == nullptr) |
|||
m_selected -= 1; |
|||
else |
|||
scroll_direction = 255; |
|||
} |
|||
if ((e->param1 & MODULE_INPUT_BUTTON_OK) != 0) |
|||
{ |
|||
Kernel::debug("OK"); |
|||
hide(); |
|||
m_result_ok = true; |
|||
return; |
|||
} |
|||
if ((e->param1 & MODULE_INPUT_BUTTON_BACK) != 0) |
|||
{ |
|||
Kernel::debug("Back"); |
|||
m_result_back = true; |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (scroll_direction == 0 && m_selected < m_view_offset) |
|||
{ |
|||
m_view_offset -= MENU_SHOW_OPTIONS; |
|||
if (m_view_offset < 0) m_view_offset = 0; |
|||
} |
|||
else if (scroll_direction == 255 && m_selected >= m_view_offset + MENU_SHOW_OPTIONS) |
|||
{ |
|||
m_view_offset += MENU_SHOW_OPTIONS; |
|||
} |
|||
|
|||
// if (scroll)
|
|||
// elseif (dirty individual items)
|
|||
|
|||
// Drawing individual menu items is sloooow, let's just refresh everything.
|
|||
/*
|
|||
if (prev_selected >= 0 && m_selected != prev_selected) |
|||
{ |
|||
if (prev_selected - m_view_offset >= 0 && prev_selected - m_view_offset < MENU_MAX_OPTIONS) |
|||
{ |
|||
ui->setDirty(m_hwnd_menu_option[prev_selected - m_view_offset]); |
|||
sprintf(msg, "Marking dirty idx %d hwnd %d", prev_selected - m_view_offset, m_hwnd_menu_option[prev_selected - m_view_offset]); |
|||
Kernel::debug(msg); |
|||
} |
|||
if (m_selected - m_view_offset >= 0 && m_selected - m_view_offset < MENU_MAX_OPTIONS) |
|||
{ |
|||
ui->setDirty(m_hwnd_menu_option[m_selected - m_view_offset]); |
|||
sprintf(msg, "Marking dirty idx %d hwnd %d", m_selected - m_view_offset, m_hwnd_menu_option[m_selected - m_view_offset]); |
|||
Kernel::debug(msg); |
|||
} |
|||
} |
|||
*/ |
|||
|
|||
if (prev_selected >= 0 && m_selected != prev_selected) |
|||
{ |
|||
ui->setDirty(m_hwnd); |
|||
for (int i = 0; i < MENU_SHOW_OPTIONS; i++) |
|||
{ |
|||
ui->setDirty(m_hwnd_menu_option[i]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void suspend() |
|||
{ |
|||
eat->write(MENU_EAT_ID, 0, (byte)m_selected); |
|||
eat->write(MENU_EAT_ID, 1, (byte)m_view_offset); |
|||
eat->write(MENU_EAT_ID, 2, (byte)m_prev_root_hwnd); |
|||
|
|||
int item_length = (1 + MENU_MAX_OPTION_LENGTH); |
|||
for (int i = 0; i < MENU_MAX_OPTIONS; i++) |
|||
{ |
|||
if (m_item[i] == nullptr) |
|||
{ |
|||
eat->write(MENU_EAT_ID, MENU_EAT_OFFSET + (i * item_length), 0xFF); |
|||
} |
|||
else |
|||
{ |
|||
eat->write(MENU_EAT_ID, MENU_EAT_OFFSET + (i * item_length), m_item[i]->id); |
|||
for (int j = 0; j < MENU_MAX_OPTION_LENGTH && m_item[i]->label[j] != '\0'; j++) |
|||
{ |
|||
eat->write(MENU_EAT_ID, MENU_EAT_OFFSET + (i * item_length) + j + 1, (byte)(m_item[i]->label[j])); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void draw(int hwnd, int x, int y, int width, int height) |
|||
{ |
|||
if (hwnd == m_hwnd) |
|||
{ |
|||
// Root window
|
|||
ui->getGfx()->fillRect(x, y, width, height, COLOUR_SECONDARY); |
|||
} |
|||
else |
|||
{ |
|||
// Menu option
|
|||
for (int i = 0; i < MENU_SHOW_OPTIONS; i++) |
|||
{ |
|||
if (m_hwnd_menu_option[i] == hwnd) |
|||
{ |
|||
int idx = m_view_offset + i; |
|||
if (m_item[idx] != nullptr) |
|||
{ |
|||
ui->getGfx()->setFont(&FreeMono12pt7b); |
|||
if (idx == m_selected) |
|||
{ |
|||
ui->getGfx()->fillRect(x, y, width, height, COLOUR_PRIMARY); |
|||
ui->getGfx()->setTextColor(COLOUR_SECONDARY); |
|||
} |
|||
else |
|||
{ |
|||
ui->getGfx()->fillRect(x, y, width, height, COLOUR_SECONDARY); |
|||
ui->getGfx()->setTextColor(COLOUR_PRIMARY); |
|||
} |
|||
ui->getGfx()->setCursor(x, y + height - (int)((height - 12) / 2)); |
|||
ui->getGfx()->print(m_item[idx]->label); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
void addItem(byte id, char* label) |
|||
{ |
|||
for (int i = 0; i < MENU_MAX_OPTIONS; i++) |
|||
{ |
|||
if (m_item[i] == nullptr) |
|||
{ |
|||
m_item[i] = new MenuItem(); |
|||
m_item[i]->id = id; |
|||
strcpy(m_item[i]->label, label); |
|||
return; |
|||
} |
|||
} |
|||
Kernel::panic("Tried to add more options to menu than MENU_MAX_OPTIONS."); |
|||
|
|||
} |
|||
|
|||
void clear() |
|||
{ |
|||
for (int i = 0; i < MENU_MAX_OPTIONS; i++) |
|||
{ |
|||
if (m_item[i] != nullptr) |
|||
{ |
|||
delete m_item[i]; |
|||
m_item[i] = nullptr; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void show() |
|||
{ |
|||
m_prev_root_hwnd = ui->getRootWindow(); |
|||
ui->setRootWindow(m_hwnd); |
|||
} |
|||
|
|||
void hide() |
|||
{ |
|||
ui->setRootWindow(m_prev_root_hwnd); |
|||
m_prev_root_hwnd = NULL; |
|||
} |
|||
|
|||
bool isSelected() |
|||
{ |
|||
return m_result_ok; |
|||
} |
|||
|
|||
bool isCancelled() |
|||
{ |
|||
return m_result_back; |
|||
} |
|||
|
|||
bool isDone() |
|||
{ |
|||
return m_result_ok || m_result_back; |
|||
} |
|||
|
|||
byte getSelectedId() |
|||
{ |
|||
return m_item[m_selected]->id; |
|||
} |
|||
|
|||
}; |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue