add u8g2 and ui libs (#91)
* add u8g2 and ui libs * add display driver and usage example * not init display in test mode * change todo text * fix removed code * Target f2 (#107) * add ioc for flipperzero f2 * add generated f1 files to f2 * regenerate cubemx * invert initial state of led * blink backligh * shutdown backlight on idle
This commit is contained in:
589
lib/ui/ui.cpp
Normal file
589
lib/ui/ui.cpp
Normal file
@@ -0,0 +1,589 @@
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
#include "main.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "u8g2_support.h"
|
||||
#include "u8g2/u8g2.h"
|
||||
}
|
||||
|
||||
#include "ui.h"
|
||||
#include "events.h"
|
||||
|
||||
// function draw basic layout -- single bmp
|
||||
void draw_bitmap(const char* bitmap, u8g2_t* u8g2, ScreenArea area) {
|
||||
if(bitmap == NULL) {
|
||||
printf("[basic layout] no content\n");
|
||||
u8g2_SetFont(u8g2, u8g2_font_6x10_mf);
|
||||
u8g2_SetDrawColor(u8g2, 1);
|
||||
u8g2_SetFontMode(u8g2, 1);
|
||||
u8g2_DrawStr(u8g2, 2, 12, "no content");
|
||||
} else {
|
||||
u8g2_SetDrawColor(u8g2, 1);
|
||||
u8g2_DrawXBM(u8g2, 0, 0, area.x + area.width, area.y + area.height, (unsigned char*)bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_text(const char* text, u8g2_t* u8g2, ScreenArea area) {
|
||||
// TODO proper cleanup statusbar
|
||||
u8g2_SetDrawColor(u8g2, 0);
|
||||
u8g2_DrawBox(u8g2, 0, 0, area.x + area.width, area.y + area.height);
|
||||
|
||||
Block text_block = Block {
|
||||
width: area.width,
|
||||
height: area.height,
|
||||
margin_left: 0,
|
||||
margin_top: 0,
|
||||
padding_left: 3,
|
||||
padding_top: 7,
|
||||
background: 0,
|
||||
color: 1,
|
||||
font: (uint8_t*)u8g2_font_6x10_mf,
|
||||
};
|
||||
|
||||
draw_block(u8g2, text, text_block, area.x, area.y);
|
||||
}
|
||||
|
||||
// draw layout and switch between ui item by button and timer
|
||||
void LayoutComponent::handle(Event* event, Store* store, u8g2_t* u8g2, ScreenArea area) {
|
||||
switch(event->type) {
|
||||
// get button event
|
||||
case EventTypeButton:
|
||||
if(event->value.button.state) {
|
||||
for(size_t i = 0; i < this->actions_size; i++) {
|
||||
FlipperComponent* next = NULL;
|
||||
|
||||
switch(this->actions[i].action) {
|
||||
case LayoutActionUp:
|
||||
if(event->value.button.id == ButtonsUp) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
case LayoutActionDown:
|
||||
if(event->value.button.id == ButtonsDown) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
case LayoutActionLeft:
|
||||
if(event->value.button.id == ButtonsLeft) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
case LayoutActionRight:
|
||||
if(event->value.button.id == ButtonsRight) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
case LayoutActionOk:
|
||||
if(event->value.button.id == ButtonsOk) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
case LayoutActionBack:
|
||||
if(event->value.button.id == ButtonsBack) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
|
||||
// stub action
|
||||
case LayoutActionUsbDisconnect:
|
||||
if(event->value.button.id == ButtonsLeft) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
|
||||
case LayoutActionUsbConnect:
|
||||
if(event->value.button.id == ButtonsRight) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
if(next) {
|
||||
printf("[layout view] go to next item\n");
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
next->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EventTypeUsb: {
|
||||
printf("get usb event\n");
|
||||
|
||||
FlipperComponent* next = NULL;
|
||||
|
||||
if(event->value.usb == UsbEventConnect) {
|
||||
for(size_t i = 0; i < this->actions_size; i++) {
|
||||
if(this->actions[i].action == LayoutActionUsbConnect) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event->value.usb == UsbEventDisconnect) {
|
||||
for(size_t i = 0; i < this->actions_size; i++) {
|
||||
if(this->actions[i].action == LayoutActionUsbDisconnect) {
|
||||
next = this->actions[i].item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(next) {
|
||||
printf("[layout view] go to next item\n");
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
next->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
}
|
||||
} break;
|
||||
|
||||
// start component from prev
|
||||
case EventTypeUiNext:
|
||||
printf("[layout view] start component %lX\n", (uint32_t)this);
|
||||
|
||||
if(this->timeout > 0) {
|
||||
// TODO start timer
|
||||
}
|
||||
|
||||
// set current item to self
|
||||
store->current_component = this;
|
||||
|
||||
this->wait_time = 0;
|
||||
|
||||
// render layout
|
||||
this->dirty = true;
|
||||
break;
|
||||
|
||||
case EventTypeTick:
|
||||
this->wait_time += event->value.tick_delta;
|
||||
|
||||
if(this->wait_time > this->timeout) {
|
||||
for(size_t i = 0; i < this->actions_size; i++) {
|
||||
if(this->actions[i].action == LayoutActionTimeout ||
|
||||
this->actions[i].action == LayoutActionEndOfCycle
|
||||
) {
|
||||
if(this->actions[i].item != NULL) {
|
||||
printf("[layout view] go to next item\n");
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
this->actions[i].item->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this->dirty) {
|
||||
this->draw_fn(this->data, u8g2, area);
|
||||
|
||||
store->dirty_screen = true;
|
||||
|
||||
this->dirty = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void BlinkerComponent::handle(Event* event, Store* store, u8g2_t* u8g2, ScreenArea area) {
|
||||
switch(event->type) {
|
||||
// get button event
|
||||
case EventTypeButton:
|
||||
if(event->value.button.state && event->value.button.id == ButtonsBack) {
|
||||
if(this->prev && this->prev != this) {
|
||||
printf("[blinker view] go back\n");
|
||||
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
this->prev->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
|
||||
this->prev = NULL;
|
||||
|
||||
store->led = ColorBlack;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
} else {
|
||||
printf("[blinker view] no back/loop\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(event->value.button.state && event->value.button.id != ButtonsBack) {
|
||||
this->active = false;
|
||||
}
|
||||
|
||||
if(!event->value.button.state && event->value.button.id != ButtonsBack) {
|
||||
this->active = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// start component from prev
|
||||
case EventTypeUiNext:
|
||||
printf("[blinker view] start component %lX\n", (uint32_t)this);
|
||||
|
||||
if(this->prev == NULL) {
|
||||
this->prev = store->current_component;
|
||||
}
|
||||
|
||||
// set current item to self
|
||||
store->current_component = this;
|
||||
|
||||
this->dirty = true;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
break;
|
||||
|
||||
case EventTypeTick:
|
||||
if(this->active) {
|
||||
this->wait_time += event->value.tick_delta;
|
||||
|
||||
if(this->is_on) {
|
||||
if(this->wait_time > this->config.on_time) {
|
||||
this->wait_time = 0;
|
||||
this->is_on = false;
|
||||
}
|
||||
} else {
|
||||
if(this->wait_time > this->config.off_time) {
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
store->led = this->is_on ? this->config.on_color : this->config.off_color;
|
||||
} else {
|
||||
store->led = ColorBlack;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
}
|
||||
|
||||
if(this->dirty) {
|
||||
this->draw_fn(this->data, u8g2, area);
|
||||
|
||||
store->dirty_screen = true;
|
||||
|
||||
this->dirty = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
void BlinkerComponentOnBtn::handle(Event* event, Store* store, u8g2_t* u8g2, ScreenArea area) {
|
||||
switch(event->type) {
|
||||
// get button event
|
||||
case EventTypeButton:
|
||||
if(event->value.button.state && event->value.button.id == ButtonsBack) {
|
||||
if(this->prev && this->prev != this) {
|
||||
printf("[blinker view] go back\n");
|
||||
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
this->prev->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
|
||||
this->prev = NULL;
|
||||
|
||||
store->led = ColorBlack;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
} else {
|
||||
printf("[blinker view] no back/loop\n");
|
||||
}
|
||||
}
|
||||
|
||||
if(event->value.button.state && event->value.button.id != ButtonsBack) {
|
||||
this->active = true;
|
||||
}
|
||||
|
||||
if(!event->value.button.state && event->value.button.id != ButtonsBack) {
|
||||
this->active = false;
|
||||
}
|
||||
break;
|
||||
|
||||
// start component from prev
|
||||
case EventTypeUiNext:
|
||||
printf("[blinker view] start component %lX\n", (uint32_t)this);
|
||||
|
||||
if(this->prev == NULL) {
|
||||
this->prev = store->current_component;
|
||||
}
|
||||
|
||||
// set current item to self
|
||||
store->current_component = this;
|
||||
|
||||
this->dirty = true;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
break;
|
||||
|
||||
case EventTypeTick:
|
||||
if(this->active) {
|
||||
this->wait_time += event->value.tick_delta;
|
||||
|
||||
if(this->is_on) {
|
||||
if(this->wait_time > this->config.on_time) {
|
||||
this->wait_time = 0;
|
||||
this->is_on = false;
|
||||
}
|
||||
} else {
|
||||
if(this->wait_time > this->config.off_time) {
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
store->led = this->is_on ? this->config.on_color : this->config.off_color;
|
||||
} else {
|
||||
store->led = ColorBlack;
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
}
|
||||
|
||||
if(this->dirty) {
|
||||
this->draw_fn(this->data, u8g2, area);
|
||||
|
||||
store->dirty_screen = true;
|
||||
|
||||
this->dirty = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
#define MENU_DRAW_LINES 4
|
||||
|
||||
Point draw_block(u8g2_t* u8g2, const char* text, Block layout, uint8_t x, uint8_t y) {
|
||||
u8g2_SetDrawColor(u8g2, layout.background);
|
||||
u8g2_DrawBox(u8g2,
|
||||
x + layout.margin_left,
|
||||
y + layout.margin_top,
|
||||
layout.width, layout.height
|
||||
);
|
||||
|
||||
u8g2_SetDrawColor(u8g2, layout.color);
|
||||
u8g2_SetFont(u8g2, layout.font);
|
||||
if(text != NULL) {
|
||||
u8g2_DrawStr(u8g2,
|
||||
x + layout.margin_left + layout.padding_left,
|
||||
y + layout.margin_top + layout.padding_top,
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
x: x + layout.margin_left + layout.width,
|
||||
y: y + layout.margin_top + layout.height
|
||||
};
|
||||
}
|
||||
|
||||
void draw_menu(MenuCtx* ctx, u8g2_t* u8g2, ScreenArea area) {
|
||||
// u8g2_ClearBuffer(u8g2);
|
||||
// clear area
|
||||
u8g2_SetDrawColor(u8g2, 0);
|
||||
u8g2_DrawBox(u8g2, area.x, area.y, area.width, area.height);
|
||||
|
||||
u8g2_SetFontMode(u8g2, 1);
|
||||
|
||||
uint8_t list_start = ctx->current - ctx->cursor;
|
||||
uint8_t list_size = ctx->size > MENU_DRAW_LINES ? MENU_DRAW_LINES : ctx->size;
|
||||
|
||||
// draw header
|
||||
/*
|
||||
Point next = draw_block(u8g2, (const char*)data->name, Block {
|
||||
width: 128,
|
||||
height: 14,
|
||||
margin_left: 0,
|
||||
margin_top: 0,
|
||||
padding_left: 4,
|
||||
padding_top: 13,
|
||||
background: 1,
|
||||
color: 0,
|
||||
font: (uint8_t*)u8g2_font_helvB14_tf,
|
||||
}, area.x, area.y);
|
||||
*/
|
||||
|
||||
Point next = {area.x, area.y};
|
||||
|
||||
for(size_t i = 0; i < list_size; i++) {
|
||||
next = draw_block(u8g2, (const char*)ctx->list[list_start + i].name, Block {
|
||||
width: 128,
|
||||
height: 15,
|
||||
margin_left: 0,
|
||||
margin_top: i == 0 ? 2 : 0,
|
||||
padding_left: 2,
|
||||
padding_top: 12,
|
||||
background: i == ctx->cursor ? 1 : 0,
|
||||
color: i == ctx->cursor ? 0 : 1,
|
||||
font: (uint8_t*)u8g2_font_7x14_tf,
|
||||
}, area.x, next.y);
|
||||
}
|
||||
|
||||
// u8g2_font_7x14_tf
|
||||
// u8g2_font_profont12_tf smallerbut cute
|
||||
// u8g2_font_samim_12_t_all орочий
|
||||
}
|
||||
|
||||
void MenuCtx::handle(MenuEvent event) {
|
||||
uint8_t menu_size = this->size > MENU_DRAW_LINES ? MENU_DRAW_LINES : this->size;
|
||||
|
||||
switch(event) {
|
||||
case MenuEventDown: {
|
||||
if(this->current < (this->size - 1)) {
|
||||
this->current++;
|
||||
|
||||
if(this->cursor < menu_size - 2 || this->current == this->size - 1) {
|
||||
this->cursor++;
|
||||
}
|
||||
} else {
|
||||
this->current = 0;
|
||||
this->cursor = 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case MenuEventUp: {
|
||||
if(this->current > 0) {
|
||||
this->current--;
|
||||
|
||||
if(this->cursor > 1 || this->current == 0) {
|
||||
this->cursor--;
|
||||
}
|
||||
|
||||
} else {
|
||||
this->current = this->size - 1;
|
||||
this->cursor = menu_size - 1;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void MenuCtx::reset() {
|
||||
this->current = 0;
|
||||
this->cursor = 0;
|
||||
}
|
||||
|
||||
// draw numenu and handle navigation
|
||||
void MenuComponent::handle(Event* event, Store* store, u8g2_t* u8g2, ScreenArea area) {
|
||||
switch(event->type) {
|
||||
// get button event
|
||||
case EventTypeButton: {
|
||||
if(event->value.button.id == ButtonsOk && event->value.button.state) {
|
||||
if(this->ctx.current < this->ctx.size) {
|
||||
FlipperComponent* next_item = (FlipperComponent*)this->ctx.list[this->ctx.current].item;
|
||||
|
||||
if(next_item) {
|
||||
store->is_fullscreen = false;
|
||||
|
||||
printf("[layout view] go to %d item\n", this->ctx.current);
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
|
||||
next_item->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
} else {
|
||||
printf("[menu view] no item at %d\n", this->ctx.current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event->value.button.id == ButtonsDown && event->value.button.state) {
|
||||
this->ctx.handle(MenuEventDown);
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
if(event->value.button.id == ButtonsUp && event->value.button.state) {
|
||||
this->ctx.handle(MenuEventUp);
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
// go back item
|
||||
if(event->value.button.id == ButtonsBack && event->value.button.state) {
|
||||
if(this->prev && this->prev != this) {
|
||||
store->is_fullscreen = false;
|
||||
|
||||
printf("[menu view] go back\n");
|
||||
|
||||
this->ctx.reset();
|
||||
|
||||
Event send_event;
|
||||
send_event.type = EventTypeUiNext;
|
||||
this->prev->handle(
|
||||
&send_event,
|
||||
store,
|
||||
u8g2,
|
||||
area
|
||||
);
|
||||
|
||||
this->prev = NULL;
|
||||
} else {
|
||||
printf("[menu view] no back/loop\n");
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
// start component from prev
|
||||
case EventTypeUiNext:
|
||||
printf("[menu view] start component %lX (size %d)\n", (uint32_t)this, this->ctx.size);
|
||||
|
||||
// set prev item
|
||||
if(this->prev == NULL) {
|
||||
printf("[menu view] set prev element to %lX\n", (uint32_t)store->current_component);
|
||||
this->prev = store->current_component;
|
||||
}
|
||||
// set current item to self
|
||||
store->current_component = this;
|
||||
|
||||
store->is_fullscreen = true;
|
||||
|
||||
// render menu
|
||||
this->dirty = true;
|
||||
break;
|
||||
|
||||
case EventTypeTick:
|
||||
if(this->dirty) {
|
||||
draw_menu(&this->ctx, u8g2, area);
|
||||
store->dirty_screen = true;
|
||||
this->dirty = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
218
lib/ui/ui.h
Normal file
218
lib/ui/ui.h
Normal file
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "main.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "u8g2_support.h"
|
||||
#include "u8g2/u8g2.h"
|
||||
}
|
||||
|
||||
#include "events.h"
|
||||
|
||||
typedef struct {
|
||||
void* item;
|
||||
const char* name;
|
||||
} MenuItem;
|
||||
|
||||
#include "vendor.h"
|
||||
|
||||
typedef enum {
|
||||
LayoutActionUp,
|
||||
LayoutActionDown,
|
||||
LayoutActionLeft,
|
||||
LayoutActionRight,
|
||||
LayoutActionOk,
|
||||
LayoutActionBack,
|
||||
LayoutActionTimeout,
|
||||
LayoutActionUsbConnect,
|
||||
LayoutActionUsbDisconnect,
|
||||
LayoutActionEndOfCycle
|
||||
} LayoutAction;
|
||||
|
||||
typedef struct {
|
||||
FlipperComponent* item;
|
||||
LayoutAction action;
|
||||
} ActionItem;
|
||||
|
||||
void draw_text(const char* text, u8g2_t* u8g2, ScreenArea area);
|
||||
void draw_bitmap(const char* bitmap, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
class LayoutComponent: FlipperComponent {
|
||||
public:
|
||||
LayoutComponent(void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area), ActionItem* actions, size_t actions_size, uint32_t timeout, const char* data) {
|
||||
this->data = data;
|
||||
this->actions = actions;
|
||||
this->actions_size = actions_size;
|
||||
this->timeout = timeout;
|
||||
this->draw_fn = draw_fn;
|
||||
|
||||
this->dirty = true;
|
||||
|
||||
this->wait_time = 0;
|
||||
}
|
||||
|
||||
virtual void handle(Event* event, struct _Store* store, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
private:
|
||||
const char* data;
|
||||
ActionItem* actions;
|
||||
size_t actions_size;
|
||||
uint32_t timeout;
|
||||
|
||||
void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
uint32_t wait_time;
|
||||
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t on_time;
|
||||
Color on_color;
|
||||
uint32_t off_time;
|
||||
Color off_color;
|
||||
} BlinkerComponentConfig;
|
||||
|
||||
class BlinkerComponent: FlipperComponent {
|
||||
public:
|
||||
BlinkerComponent(
|
||||
void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area),
|
||||
BlinkerComponentConfig config,
|
||||
const char* data
|
||||
) {
|
||||
this->data = data;
|
||||
this->draw_fn = draw_fn;
|
||||
this->config = config;
|
||||
|
||||
this->dirty = true;
|
||||
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
this->prev = NULL;
|
||||
}
|
||||
|
||||
virtual void handle(Event* event, struct _Store* store, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
private:
|
||||
const char* data;
|
||||
BlinkerComponentConfig config;
|
||||
|
||||
void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
uint32_t wait_time;
|
||||
|
||||
bool is_on;
|
||||
bool active;
|
||||
|
||||
bool dirty;
|
||||
FlipperComponent* prev;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint32_t on_time;
|
||||
Color on_color;
|
||||
uint32_t off_time;
|
||||
Color off_color;
|
||||
} BlinkerComponentOnBtnConfig;
|
||||
|
||||
class BlinkerComponentOnBtn: FlipperComponent {
|
||||
public:
|
||||
BlinkerComponentOnBtn(
|
||||
void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area),
|
||||
BlinkerComponentOnBtnConfig config,
|
||||
const char* data
|
||||
) {
|
||||
this->data = data;
|
||||
this->draw_fn = draw_fn;
|
||||
this->config = config;
|
||||
|
||||
this->dirty = true;
|
||||
|
||||
this->wait_time = 0;
|
||||
this->is_on = true;
|
||||
this->active = false;
|
||||
this->prev = NULL;
|
||||
}
|
||||
|
||||
virtual void handle(Event* event, struct _Store* store, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
private:
|
||||
const char* data;
|
||||
BlinkerComponentOnBtnConfig config;
|
||||
|
||||
void (*draw_fn)(const char* text, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
uint32_t wait_time;
|
||||
|
||||
bool is_on;
|
||||
bool active;
|
||||
|
||||
bool dirty;
|
||||
FlipperComponent* prev;
|
||||
};
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
MenuEventUp,
|
||||
MenuEventDown
|
||||
} MenuEvent;
|
||||
|
||||
class MenuCtx {
|
||||
public:
|
||||
size_t size;
|
||||
size_t current;
|
||||
uint8_t cursor;
|
||||
MenuItem* list;
|
||||
|
||||
void handle(MenuEvent event);
|
||||
void reset();
|
||||
};
|
||||
|
||||
void draw_menu(MenuCtx* ctx, u8g2_t* u8g2, ScreenArea area);
|
||||
|
||||
class MenuComponent: FlipperComponent {
|
||||
public:
|
||||
MenuComponent(MenuItem* list, size_t size, const char* name) {
|
||||
this->ctx.size = size;
|
||||
this->ctx.current = 0;
|
||||
this->ctx.cursor = 0;
|
||||
|
||||
this->ctx.list = list;
|
||||
|
||||
this->name = name;
|
||||
this->prev = NULL;
|
||||
|
||||
this->dirty = true;
|
||||
}
|
||||
|
||||
|
||||
const char* name;
|
||||
FlipperComponent* prev;
|
||||
MenuCtx ctx;
|
||||
|
||||
|
||||
bool dirty;
|
||||
|
||||
virtual void handle(Event* event, struct _Store* store, u8g2_t* u8g2, ScreenArea area);
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
} Point;
|
||||
|
||||
typedef struct {
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
uint8_t margin_left;
|
||||
uint8_t margin_top;
|
||||
uint8_t padding_left;
|
||||
uint8_t padding_top;
|
||||
uint8_t background;
|
||||
uint8_t color;
|
||||
uint8_t* font;
|
||||
} Block;
|
||||
|
||||
Point draw_block(u8g2_t* u8g2, const char* text, Block layout, uint8_t x, uint8_t y);
|
Reference in New Issue
Block a user