From 0672ab0b778f75136c54177ec14ca0c2da0f1013 Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Sun, 4 Jun 2023 16:25:43 -0500 Subject: [PATCH] ViewDispatcher example --- ui/viewdispatcher_demo/README.md | 8 ++ ui/viewdispatcher_demo/application.fam | 18 +++ ui/viewdispatcher_demo/demo.c | 145 +++++++++++++++++++++++++ ui/viewdispatcher_demo/icon.png | Bin 0 -> 1914 bytes 4 files changed, 171 insertions(+) create mode 100644 ui/viewdispatcher_demo/README.md create mode 100644 ui/viewdispatcher_demo/application.fam create mode 100644 ui/viewdispatcher_demo/demo.c create mode 100644 ui/viewdispatcher_demo/icon.png diff --git a/ui/viewdispatcher_demo/README.md b/ui/viewdispatcher_demo/README.md new file mode 100644 index 0000000..6602de8 --- /dev/null +++ b/ui/viewdispatcher_demo/README.md @@ -0,0 +1,8 @@ +# viewdispatcher demo +This is a UI demo of the ViewDispatcher class. It is based off the wiki code for the [ViewDispatcher] (https://github.com/jamisonderek/flipper-zero-tutorials/wiki/User-Interface#viewdispatcher) section of the User-Interface page. + +The two [View](https://github.com/jamisonderek/flipper-zero-tutorials/wiki/User-Interface#view) objects registers draw and input callbacks and set different screen orientations. + +The draw callback does some basic text drawing on a [Canvas](https://github.com/jamisonderek/flipper-zero-tutorials/wiki/User-Interface#canvas). + +The input callback changes the X and Y position of a cursor "^" based on the key that is pressed. You can see that when the ViewPort orientation is rotated, both the screen and the keypress is translated automatically. Pressing the back button uses a [Message Queue](https://github.com/jamisonderek/flipper-zero-tutorials/wiki/Message-Queue) to signal that application should exit. Pressing OK button switches the [View](https://github.com/jamisonderek/flipper-zero-tutorials/wiki/User-Interface#view). diff --git a/ui/viewdispatcher_demo/application.fam b/ui/viewdispatcher_demo/application.fam new file mode 100644 index 0000000..db00e9f --- /dev/null +++ b/ui/viewdispatcher_demo/application.fam @@ -0,0 +1,18 @@ +App( + appid="viewdispatcher_demo", + name="ViewDispatcher demo", + apptype=FlipperAppType.EXTERNAL, + entry_point="viewdispatcher_demo_app", + cdefines=["VIEWDISPATCHER_DEMO_APP"], + requires=[ + "gui", + ], + stack_size=2 * 1024, + order=100, + fap_description = "ViewDispatcher demo", + fap_author = "codeallnight", + fap_weburl = "https://github.com/jamisonderek/Flipper-Zero-tutorials", + fap_category="UI", + fap_icon="icon.png", + fap_libs=["assets"], +) \ No newline at end of file diff --git a/ui/viewdispatcher_demo/demo.c b/ui/viewdispatcher_demo/demo.c new file mode 100644 index 0000000..eb4f7b8 --- /dev/null +++ b/ui/viewdispatcher_demo/demo.c @@ -0,0 +1,145 @@ +/* + +This is an example of using a ViewDispatcher. It is a simple Hello World program that +allows you to move a cursor around the screen with the arrow keys. Pressing the OK button +will switch between two Views (the views share the same callback functions), just have +different screen orientations. Pressing the the back button will exit the program. + +The code is from the Message Queue wiki page +(https://github.com/jamisonderek/flipper-zero-tutorials/wiki/Message-Queue) and +also the ViewDispatcher section of the User Interface wiki page +(https://github.com/jamisonderek/flipper-zero-tutorials/wiki/User-Interface#viewdispatcher). + +*/ + +#include +#include +#include +#include + +typedef enum { + MyEventTypeKey, + MyEventTypeDone, +} MyEventType; + +typedef struct { + MyEventType type; // The reason for this event. + InputEvent input; // This data is specific to keypress data. +} MyEvent; + +typedef enum { + MyViewId, + MyOtherViewId, +} ViewId; + +FuriMessageQueue* queue; +int x = 32; +int y = 48; +ViewId current_view; + +static void my_draw_callback(Canvas* canvas, void* context) { + UNUSED(context); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 5, 30, "Hello world"); + canvas_draw_str_aligned(canvas, x, y, AlignLeft, AlignTop, "^"); +} + +static bool my_input_callback(InputEvent* input_event, void* context) { + furi_assert(context); + bool handled = false; + // we set our callback context to be the view_dispatcher. + ViewDispatcher* view_dispatcher = context; + + if(input_event->type == InputTypeShort) { + if(input_event->key == InputKeyBack) { + // Default back handler. + handled = false; + } else if(input_event->key == InputKeyLeft) { + x--; + handled = true; + } else if(input_event->key == InputKeyRight) { + x++; + handled = true; + } else if(input_event->key == InputKeyUp) { + y--; + handled = true; + } else if(input_event->key == InputKeyDown) { + y++; + handled = true; + } else if(input_event->key == InputKeyOk) { + // switch the view! + view_dispatcher_send_custom_event(view_dispatcher, 42); + handled = true; + } + } + + return handled; +} + +bool view_dispatcher_navigation_event_callback(void* context) { + UNUSED(context); + // We did not handle the event, so return false. + return false; +} + +bool my_view_dispatcher_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + bool handled = false; + // we set our callback context to be the view_dispatcher. + ViewDispatcher* view_dispatcher = context; + + if(event == 42) { + if(current_view == MyViewId) { + current_view = MyOtherViewId; + } else { + current_view = MyViewId; + } + + view_dispatcher_switch_to_view(view_dispatcher, current_view); + handled = true; + } + + // NOTE: The return value is not currently used by the ViewDispatcher. + return handled; +} + +int32_t viewdispatcher_demo_app() { + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + + // For this demo, we just use view_dispatcher as our application context. + void* my_context = view_dispatcher; + + View* view1 = view_alloc(); + view_set_context(view1, my_context); + view_set_draw_callback(view1, my_draw_callback); + view_set_input_callback(view1, my_input_callback); + view_set_orientation(view1, ViewOrientationHorizontal); + + View* view2 = view_alloc(); + view_set_context(view2, my_context); + view_set_draw_callback(view2, my_draw_callback); + view_set_input_callback(view2, my_input_callback); + view_set_orientation(view2, ViewOrientationVertical); + + // set param 1 of custom event callback (impacts tick and navigation too). + view_dispatcher_set_event_callback_context(view_dispatcher, my_context); + view_dispatcher_set_navigation_event_callback( + view_dispatcher, view_dispatcher_navigation_event_callback); + view_dispatcher_set_custom_event_callback( + view_dispatcher, my_view_dispatcher_custom_event_callback); + view_dispatcher_enable_queue(view_dispatcher); + view_dispatcher_add_view(view_dispatcher, MyViewId, view1); + view_dispatcher_add_view(view_dispatcher, MyOtherViewId, view2); + + Gui* gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + current_view = MyViewId; + view_dispatcher_switch_to_view(view_dispatcher, current_view); + view_dispatcher_run(view_dispatcher); + + view_dispatcher_remove_view(view_dispatcher, MyViewId); + view_dispatcher_remove_view(view_dispatcher, MyOtherViewId); + furi_record_close(RECORD_GUI); + view_dispatcher_free(view_dispatcher); + return 0; +} \ No newline at end of file diff --git a/ui/viewdispatcher_demo/icon.png b/ui/viewdispatcher_demo/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fadea3257408fdd197dd33349e28336e7113b34f GIT binary patch literal 1914 zcmcIlO>g5w81@2-ps0IZJ`R?90m{Vo_}fahq)pjIT9>FB)NodX!l`PwTl!tdkXw;sZ9EkC%uEj{}At>0kr(y$%0CF#3|ul+@?!}{gko&N5{ z#RW`1`tjG#V0bm%`#6`R8&AvcwQn|Wya}jZqW)2F6x{Kth#Q270dGv>6ws2?+?u9@ z9`iyT@L`m+ls~@zL6IZYQVy*^3(_4viuTSke)oL8PtT93$CRyG>ub%a4-7Fch&+wQ zN$yWuN=59$yxdk5xq=kOEu~#rl#ha*ydyF$I}MDerfIU{HS~dP5yyK^#u~<|W~;h^ zbjLSsUpM91M*(}X+{{?$A9OyL*@IO}85Kq9tLkJjX-tfU$cC!!d7i3a6=MVll%FL9 znW7}$s7Yot9iG!HN{dJ&a!E@D;-qLP;Bp;HoG!SNe3mdsojN6{syDPUwmC2eo)3-V z1v)QwPr=H}=}U?8{#nY^1D=bMjPl)6o)jB%W9di~A|HwW@NMz;zdU4gVK6<(##N3O zRr#35z?}nDul!5>os5%0WPKsV^NRK67G-QSEcxxjC}Cofzf*cNJHR`n;4P)B9@0Fd zTYcT|P0KgkP0jE%t%e352u2FB8d$I>JX-}C1b#Qk3zAUY?X(ndsu4xZw?o(T1`a`H zh;5`9CPyxzpsRu9FdAyyz;5LiobL#FQf8t&U+b3%3LJ}U`h?h6HyB3_BMdo)siA@9 znuue|4IR(XL(7`8eV9eiLu9Ksfz~KB*^7qh&aL2wM^5Yj;F}a z?CyxM$O6G2yM`h&>W3nW3Fs$^$&jmQGMq~qL=8qDn7t^63Z7N%Quj{PG(7}o+m0?v>`Ecjw387B(&y-+hUFqGCM4tSAr#?i-Ki@Cl9SUb z!dOWk|7RFAHyx2=$l<1i4qOhbsg`%ra@CsZ}Mp7?Y&yo6_r#Q@?y&rS9&2=h61P&;J2dM?9|p literal 0 HcmV?d00001