FL-528 GUI: View, ViewDispather. Dolphin: first start. (#276)
* GUI: view. Flooper-blooper fix compilation error. * GUI: view and viewdispatcher bones * GUI: view implementation, view models, view dispatcher * GUI: view navigation, model refinement. Power: use view, view dispatcher. * HAL Flash: proper page write. Dolphin: views. Power: views * Dolphin: transition idle scree to Views * Dolphin: input events on stats view. Format sources. * HAL: flash erase. Dolphin: permanent state storage. * Dolphin: first start welcome. HAL: flash operation status, errata 2.2.9 crutch.
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
#include "canvas_i.h"
|
||||
#include "icon.h"
|
||||
#include "icon_i.h"
|
||||
|
||||
#include <flipper.h>
|
||||
@@ -113,6 +112,14 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, Icon* icon) {
|
||||
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_get_data(icon));
|
||||
}
|
||||
|
||||
void canvas_draw_icon_name(Canvas* canvas, uint8_t x, uint8_t y, IconName name) {
|
||||
furi_assert(canvas);
|
||||
const IconData* data = assets_icons_get_data(name);
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, data->width, data->height, data->frames[0]);
|
||||
}
|
||||
|
||||
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
|
||||
furi_assert(canvas);
|
||||
x += canvas->offset_x;
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <u8g2.h>
|
||||
#include <gui/icon.h>
|
||||
#include <assets_icons_i.h>
|
||||
|
||||
typedef enum {
|
||||
ColorWhite = 0x00,
|
||||
@@ -46,10 +47,15 @@ void canvas_set_font(Canvas* canvas, Font font);
|
||||
void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str);
|
||||
|
||||
/*
|
||||
* Draw icon at position defined by x,y.
|
||||
* Draw stateful icon at position defined by x,y.
|
||||
*/
|
||||
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, Icon* icon);
|
||||
|
||||
/*
|
||||
* Draw stateless icon at position defined by x,y.
|
||||
*/
|
||||
void canvas_draw_icon_name(Canvas* canvas, uint8_t x, uint8_t y, IconName name);
|
||||
|
||||
/*
|
||||
* Draw xbm icon of width, height at position defined by x,y.
|
||||
*/
|
||||
|
140
applications/gui/view.c
Normal file
140
applications/gui/view.c
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "view_i.h"
|
||||
|
||||
View* view_alloc() {
|
||||
View* view = furi_alloc(sizeof(View));
|
||||
return view;
|
||||
}
|
||||
|
||||
void view_free(View* view) {
|
||||
furi_assert(view);
|
||||
view_free_model(view);
|
||||
free(view);
|
||||
}
|
||||
|
||||
void view_set_dispatcher(View* view, ViewDispatcher* view_dispatcher) {
|
||||
furi_assert(view);
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view->dispatcher == NULL);
|
||||
view->dispatcher = view_dispatcher;
|
||||
}
|
||||
|
||||
void view_set_draw_callback(View* view, ViewDrawCallback callback) {
|
||||
furi_assert(view);
|
||||
furi_assert(view->draw_callback == NULL);
|
||||
view->draw_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_input_callback(View* view, ViewInputCallback callback) {
|
||||
furi_assert(view);
|
||||
furi_assert(view->input_callback == NULL);
|
||||
view->input_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_previous_callback(View* view, ViewNavigationCallback callback) {
|
||||
furi_assert(view);
|
||||
view->previous_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_next_callback(View* view, ViewNavigationCallback callback) {
|
||||
furi_assert(view);
|
||||
view->next_callback = callback;
|
||||
}
|
||||
|
||||
void view_set_context(View* view, void* context) {
|
||||
furi_assert(view);
|
||||
furi_assert(context);
|
||||
view->context = context;
|
||||
}
|
||||
|
||||
void view_allocate_model(View* view, ViewModelType type, size_t size) {
|
||||
furi_assert(view);
|
||||
furi_assert(size > 0);
|
||||
furi_assert(view->model_type == ViewModelTypeNone);
|
||||
furi_assert(view->model == NULL);
|
||||
view->model_type = type;
|
||||
if(view->model_type == ViewModelTypeLockFree) {
|
||||
view->model = furi_alloc(size);
|
||||
} else if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = furi_alloc(sizeof(ViewModelLocking));
|
||||
model->mutex = osMutexNew(NULL);
|
||||
furi_check(model->mutex);
|
||||
model->data = furi_alloc(size);
|
||||
view->model = model;
|
||||
} else {
|
||||
furi_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void view_free_model(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->model_type == ViewModelTypeNone) {
|
||||
return;
|
||||
} else if(view->model_type == ViewModelTypeLockFree) {
|
||||
free(view->model);
|
||||
} else if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = view->model;
|
||||
furi_check(osMutexDelete(model->mutex) == osOK);
|
||||
free(model->data);
|
||||
free(model);
|
||||
view->model = NULL;
|
||||
} else {
|
||||
furi_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void* view_get_model(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = (ViewModelLocking*)(view->model);
|
||||
furi_check(osMutexAcquire(model->mutex, osWaitForever) == osOK);
|
||||
return model->data;
|
||||
}
|
||||
return view->model;
|
||||
}
|
||||
|
||||
void view_commit_model(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->model_type == ViewModelTypeLocking) {
|
||||
ViewModelLocking* model = (ViewModelLocking*)(view->model);
|
||||
furi_check(osMutexRelease(model->mutex) == osOK);
|
||||
}
|
||||
if(view->dispatcher) {
|
||||
view_dispatcher_update(view->dispatcher, view);
|
||||
}
|
||||
}
|
||||
|
||||
void view_draw(View* view, Canvas* canvas) {
|
||||
furi_assert(view);
|
||||
if(view->draw_callback) {
|
||||
void* data = view_get_model(view);
|
||||
view->draw_callback(canvas, data);
|
||||
view_commit_model(view);
|
||||
}
|
||||
}
|
||||
|
||||
bool view_input(View* view, InputEvent* event) {
|
||||
furi_assert(view);
|
||||
if(view->input_callback) {
|
||||
return view->input_callback(event, view->context);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t view_previous(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->previous_callback) {
|
||||
return view->previous_callback(view->context);
|
||||
} else {
|
||||
return VIEW_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t view_next(View* view) {
|
||||
furi_assert(view);
|
||||
if(view->next_callback) {
|
||||
return view->next_callback(view->context);
|
||||
} else {
|
||||
return VIEW_IGNORE;
|
||||
}
|
||||
}
|
129
applications/gui/view.h
Normal file
129
applications/gui/view.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <input/input.h>
|
||||
#include "canvas.h"
|
||||
|
||||
/* Hides drawing widget */
|
||||
#define VIEW_NONE 0xFFFFFFFF
|
||||
/* Ignore navigation event */
|
||||
#define VIEW_IGNORE 0xFFFFFFFE
|
||||
/* Deatch from gui, deallocate Views and ViewDispatcher
|
||||
* BE SUPER CAREFUL, deallocation happens automatically on GUI thread
|
||||
* You ARE NOT owning ViewDispatcher and Views instances
|
||||
*/
|
||||
#define VIEW_DESTROY 0xFFFFFFFA
|
||||
|
||||
/* View Draw callback
|
||||
* @param canvas, pointer to canvas
|
||||
* @param view_model, pointer to context
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*ViewDrawCallback)(Canvas* canvas, void* model);
|
||||
|
||||
/* View Input callback
|
||||
* @param event, pointer to input event data
|
||||
* @param context, pointer to context
|
||||
* @return true if event handled, false if event ignored
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef bool (*ViewInputCallback)(InputEvent* event, void* context);
|
||||
|
||||
/* View navigation callback
|
||||
* @param context, pointer to context
|
||||
* @return next view id
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef uint32_t (*ViewNavigationCallback)(void* context);
|
||||
|
||||
/* View model types */
|
||||
typedef enum {
|
||||
/* Model is not allocated */
|
||||
ViewModelTypeNone,
|
||||
/* Model consist of atomic types and/or partial update is not critical for rendering.
|
||||
* Lock free.
|
||||
*/
|
||||
ViewModelTypeLockFree,
|
||||
/* Model access is guarded with mutex.
|
||||
* Locking gui thread.
|
||||
*/
|
||||
ViewModelTypeLocking,
|
||||
} ViewModelType;
|
||||
|
||||
typedef struct View View;
|
||||
|
||||
/* Allocate and init View
|
||||
* @return pointer to View
|
||||
*/
|
||||
View* view_alloc();
|
||||
|
||||
/* Free View
|
||||
* @param pointer to View
|
||||
*/
|
||||
void view_free(View* view);
|
||||
|
||||
/* Set View Draw callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, draw callback
|
||||
*/
|
||||
void view_set_draw_callback(View* view, ViewDrawCallback callback);
|
||||
|
||||
/* Set View Draw callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
*/
|
||||
void view_set_input_callback(View* view, ViewInputCallback callback);
|
||||
|
||||
/* Set Navigation Previous callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
*/
|
||||
void view_set_previous_callback(View* view, ViewNavigationCallback callback);
|
||||
|
||||
/* Set Navigation Next callback
|
||||
* @param view, pointer to View
|
||||
* @param callback, input callback
|
||||
*/
|
||||
void view_set_next_callback(View* view, ViewNavigationCallback callback);
|
||||
|
||||
/* Set View Draw callback
|
||||
* @param view, pointer to View
|
||||
* @param context, context for callbacks
|
||||
*/
|
||||
void view_set_context(View* view, void* context);
|
||||
|
||||
/* Allocate view model.
|
||||
* @param view, pointer to View
|
||||
* @param type, View Model Type
|
||||
* @param size, size
|
||||
*/
|
||||
void view_allocate_model(View* view, ViewModelType type, size_t size);
|
||||
|
||||
/* Free view model data memory.
|
||||
* @param view, pointer to View
|
||||
*/
|
||||
void view_free_model(View* view);
|
||||
|
||||
/* Get view model data
|
||||
* @param view, pointer to View
|
||||
* @return pointer to model data
|
||||
* @warning Don't forget to commit model changes
|
||||
*/
|
||||
void* view_get_model(View* view);
|
||||
|
||||
/* Commit view model
|
||||
* @param view, pointer to View
|
||||
*/
|
||||
void view_commit_model(View* view);
|
||||
|
||||
/*
|
||||
* With clause for view model
|
||||
* @param view, View instance pointer
|
||||
* @param function_body a (){} lambda declaration,
|
||||
* executed within you parent function context.
|
||||
*/
|
||||
#define with_view_model(view, function_body) \
|
||||
{ \
|
||||
void* p = view_get_model(view); \
|
||||
({ void __fn__ function_body __fn__; })(p); \
|
||||
view_commit_model(view); \
|
||||
}
|
113
applications/gui/view_dispatcher.c
Normal file
113
applications/gui/view_dispatcher.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "view_dispatcher_i.h"
|
||||
|
||||
ViewDispatcher* view_dispatcher_alloc() {
|
||||
ViewDispatcher* view_dispatcher = furi_alloc(sizeof(ViewDispatcher));
|
||||
|
||||
view_dispatcher->widget = widget_alloc();
|
||||
widget_draw_callback_set(
|
||||
view_dispatcher->widget, view_dispatcher_draw_callback, view_dispatcher);
|
||||
widget_input_callback_set(
|
||||
view_dispatcher->widget, view_dispatcher_input_callback, view_dispatcher);
|
||||
widget_enabled_set(view_dispatcher->widget, false);
|
||||
|
||||
ViewDict_init(view_dispatcher->views);
|
||||
|
||||
return view_dispatcher;
|
||||
}
|
||||
|
||||
void view_dispatcher_free(ViewDispatcher* view_dispatcher) {
|
||||
// Detach from gui
|
||||
if(view_dispatcher->gui) {
|
||||
gui_remove_widget(view_dispatcher->gui, view_dispatcher->widget);
|
||||
}
|
||||
// Free views
|
||||
ViewDict_it_t it;
|
||||
ViewDict_it(it, view_dispatcher->views);
|
||||
while(!ViewDict_end_p(it)) {
|
||||
ViewDict_itref_t* ref = ViewDict_ref(it);
|
||||
view_free(ref->value);
|
||||
ViewDict_next(it);
|
||||
}
|
||||
ViewDict_clear(view_dispatcher->views);
|
||||
// Free dispatcher
|
||||
free(view_dispatcher);
|
||||
}
|
||||
|
||||
void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view);
|
||||
// Check if view id is not used and resgister view
|
||||
furi_check(ViewDict_get(view_dispatcher->views, view_id) == NULL);
|
||||
ViewDict_set_at(view_dispatcher->views, view_id, view);
|
||||
view_set_dispatcher(view, view_dispatcher);
|
||||
}
|
||||
|
||||
void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id) {
|
||||
furi_assert(view_dispatcher);
|
||||
if(view_id == VIEW_NONE) {
|
||||
view_dispatcher->current_view = NULL;
|
||||
widget_enabled_set(view_dispatcher->widget, false);
|
||||
} else if(view_id == VIEW_IGNORE) {
|
||||
} else if(view_id == VIEW_DESTROY) {
|
||||
view_dispatcher_free(view_dispatcher);
|
||||
} else {
|
||||
View** view_pp = ViewDict_get(view_dispatcher->views, view_id);
|
||||
furi_check(view_pp != NULL);
|
||||
view_dispatcher->current_view = *view_pp;
|
||||
widget_enabled_set(view_dispatcher->widget, true);
|
||||
widget_update(view_dispatcher->widget);
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_attach_to_gui(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
Gui* gui,
|
||||
ViewDispatcherType type) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view_dispatcher->gui == NULL);
|
||||
furi_assert(gui);
|
||||
|
||||
if(type == ViewDispatcherTypeNone) {
|
||||
gui_add_widget(gui, view_dispatcher->widget, GuiLayerNone);
|
||||
} else if(type == ViewDispatcherTypeFullscreen) {
|
||||
gui_add_widget(gui, view_dispatcher->widget, GuiLayerFullscreen);
|
||||
} else if(type == ViewDispatcherTypeWindow) {
|
||||
gui_add_widget(gui, view_dispatcher->widget, GuiLayerMain);
|
||||
} else {
|
||||
furi_check(NULL);
|
||||
}
|
||||
view_dispatcher->gui = gui;
|
||||
}
|
||||
|
||||
void view_dispatcher_draw_callback(Canvas* canvas, void* context) {
|
||||
ViewDispatcher* view_dispatcher = context;
|
||||
if(view_dispatcher->current_view) {
|
||||
view_draw(view_dispatcher->current_view, canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_input_callback(InputEvent* event, void* context) {
|
||||
ViewDispatcher* view_dispatcher = context;
|
||||
bool is_consumed = false;
|
||||
if(view_dispatcher->current_view) {
|
||||
is_consumed = view_input(view_dispatcher->current_view, event);
|
||||
}
|
||||
if(!is_consumed && event->state) {
|
||||
uint32_t view_id = VIEW_IGNORE;
|
||||
if(event->input == InputBack) {
|
||||
view_id = view_previous(view_dispatcher->current_view);
|
||||
} else if(event->input == InputOk) {
|
||||
view_id = view_next(view_dispatcher->current_view);
|
||||
}
|
||||
view_dispatcher_switch_to_view(view_dispatcher, view_id);
|
||||
}
|
||||
}
|
||||
|
||||
void view_dispatcher_update(ViewDispatcher* view_dispatcher, View* view) {
|
||||
furi_assert(view_dispatcher);
|
||||
furi_assert(view);
|
||||
|
||||
if(view_dispatcher->current_view == view) {
|
||||
widget_update(view_dispatcher->widget);
|
||||
}
|
||||
}
|
45
applications/gui/view_dispatcher.h
Normal file
45
applications/gui/view_dispatcher.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "view.h"
|
||||
#include "gui.h"
|
||||
|
||||
/* ViewDispatcher widget placement */
|
||||
typedef enum {
|
||||
ViewDispatcherTypeNone, /* Special layer for internal use only */
|
||||
ViewDispatcherTypeWindow, /* Main widget layer, status bar is shown */
|
||||
ViewDispatcherTypeFullscreen /* Fullscreen widget layer */
|
||||
} ViewDispatcherType;
|
||||
|
||||
typedef struct ViewDispatcher ViewDispatcher;
|
||||
|
||||
/* Allocate ViewDispatcher
|
||||
* @return pointer to ViewDispatcher instance
|
||||
*/
|
||||
ViewDispatcher* view_dispatcher_alloc();
|
||||
|
||||
/* Free ViewDispatcher
|
||||
* @param pointer to View
|
||||
*/
|
||||
void view_dispatcher_free(ViewDispatcher* view_dispatcher);
|
||||
|
||||
/* Add view to ViewDispatcher
|
||||
* @param view_dispatcher, ViewDispatcher instance
|
||||
* @param view_id, View id to register
|
||||
* @param view, View instance
|
||||
*/
|
||||
void view_dispatcher_add_view(ViewDispatcher* view_dispatcher, uint32_t view_id, View* view);
|
||||
|
||||
/* Switch to View
|
||||
* @param view_dispatcher, ViewDispatcher instance
|
||||
* @param view_id, View id to register
|
||||
*/
|
||||
void view_dispatcher_switch_to_view(ViewDispatcher* view_dispatcher, uint32_t view_id);
|
||||
|
||||
/* Attach ViewDispatcher to GUI
|
||||
* @param view_dispatcher, ViewDispatcher instance
|
||||
* @param gui, GUI instance to attach to
|
||||
*/
|
||||
void view_dispatcher_attach_to_gui(
|
||||
ViewDispatcher* view_dispatcher,
|
||||
Gui* gui,
|
||||
ViewDispatcherType type);
|
24
applications/gui/view_dispatcher_i.h
Normal file
24
applications/gui/view_dispatcher_i.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "view_dispatcher.h"
|
||||
#include "view_i.h"
|
||||
#include <flipper_v2.h>
|
||||
#include <m-dict.h>
|
||||
|
||||
DICT_DEF2(ViewDict, uint32_t, M_DEFAULT_OPLIST, View*, M_PTR_OPLIST)
|
||||
|
||||
struct ViewDispatcher {
|
||||
Gui* gui;
|
||||
Widget* widget;
|
||||
ViewDict_t views;
|
||||
View* current_view;
|
||||
};
|
||||
|
||||
/* Widget Draw Callback */
|
||||
void view_dispatcher_draw_callback(Canvas* canvas, void* context);
|
||||
|
||||
/* Widget Input Callback */
|
||||
void view_dispatcher_input_callback(InputEvent* event, void* context);
|
||||
|
||||
/* View to ViewDispatcher update event */
|
||||
void view_dispatcher_update(ViewDispatcher* view_dispatcher, View* view);
|
36
applications/gui/view_i.h
Normal file
36
applications/gui/view_i.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "view.h"
|
||||
#include "view_dispatcher_i.h"
|
||||
#include <flipper_v2.h>
|
||||
|
||||
typedef struct {
|
||||
void* data;
|
||||
osMutexId_t mutex;
|
||||
} ViewModelLocking;
|
||||
|
||||
struct View {
|
||||
ViewDispatcher* dispatcher;
|
||||
ViewDrawCallback draw_callback;
|
||||
ViewInputCallback input_callback;
|
||||
ViewModelType model_type;
|
||||
ViewNavigationCallback previous_callback;
|
||||
ViewNavigationCallback next_callback;
|
||||
void* model;
|
||||
void* context;
|
||||
};
|
||||
|
||||
/* Set View dispatcher */
|
||||
void view_set_dispatcher(View* view, ViewDispatcher* view_dispatcher);
|
||||
|
||||
/* Draw Callback for View dispatcher */
|
||||
void view_draw(View* view, Canvas* canvas);
|
||||
|
||||
/* Input Callback for View dispatcher */
|
||||
bool view_input(View* view, InputEvent* event);
|
||||
|
||||
/* Previous Callback for View dispatcher */
|
||||
uint32_t view_previous(View* view);
|
||||
|
||||
/* Next Callback for View dispatcher */
|
||||
uint32_t view_next(View* view);
|
@@ -9,7 +9,7 @@ typedef struct Widget Widget;
|
||||
* Widget Draw callback
|
||||
* @warning called from GUI thread
|
||||
*/
|
||||
typedef void (*WidgetDrawCallback)(Canvas* api, void* context);
|
||||
typedef void (*WidgetDrawCallback)(Canvas* canvas, void* context);
|
||||
|
||||
/*
|
||||
* Widget Input callback
|
||||
|
Reference in New Issue
Block a user