-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathui.c
146 lines (130 loc) · 5.59 KB
/
ui.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
* See the LICENSE file for information about the license. */
#include "app.h"
/* =========================== Subview handling ================================
* Note that these are not the Flipper subviews, but the subview system
* implemented inside ProtoView.
* ========================================================================== */
/* Return the ID of the currently selected subview, of the current
* view. */
int ui_get_current_subview(ProtoViewApp *app) {
return app->current_subview[app->current_view];
}
/* Called by view rendering callback that has subviews, to show small triangles
* facing down/up if there are other subviews the user can access with up
* and down. */
void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app,
int last_subview)
{
int subview = ui_get_current_subview(app);
if (subview != 0)
canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop);
if (subview != last_subview-1)
canvas_draw_triangle(canvas,120,59,8,5,CanvasDirectionTopToBottom);
}
/* Handle up/down keys when we are in a subview. If the function catched
* such keypress, it returns true, so that the actual view input callback
* knows it can just return ASAP without doing anything. */
bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) {
int subview = ui_get_current_subview(app);
if (input.type == InputTypePress) {
if (input.key == InputKeyUp) {
if (subview != 0)
app->current_subview[app->current_view]--;
return true;
} else if (input.key == InputKeyDown) {
if (subview != last_subview-1)
app->current_subview[app->current_view]++;
return true;
}
}
return false;
}
/* ============================= Text input ====================================
* Normally we just use our own private UI widgets. However for the text input
* widget, that is quite complex, visualizes a keyboard and must be standardized
* for user coherent experience, we use the one provided by the Flipper
* framework. The following two functions allow to show the keyboard to get
* text and later dismiss it.
* ========================================================================== */
/* Show the keyboard, take the user input and store it into the specified
* 'buffer' of 'buflen' total bytes. When the user is done, the done_callback
* is called passing the application context to it. Such callback needs
* to do whatever it wants with the input buffer and dismissi the keyboard
* calling: dismiss_keyboard(app);
*
* Note: if the buffer is not a null-termined zero string, what it contains will
* be used as initial input for the user. */
void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
void (*done_callback)(void*))
{
app->show_text_input = true;
app->text_input_buffer = buffer;
app->text_input_buffer_len = buflen;
app->text_input_done_callback = done_callback;
}
void ui_dismiss_keyboard(ProtoViewApp *app) {
view_dispatcher_stop(app->view_dispatcher);
}
/* ================================= Alert ================================== */
/* Set an alert message to be shown over any currently active view, for
* the specified amount of time of 'ttl' milliseconds. */
void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) {
app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl);
snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text);
}
/* Cancel the alert before its time has elapsed. */
void ui_dismiss_alert(ProtoViewApp *app) {
app->alert_dismiss_time = 0;
}
/* Show the alert if an alert is set. This is called after the currently
* active view displayed its stuff, so we overwrite the screen with the
* alert message. */
void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) {
if (app->alert_dismiss_time == 0) {
/* No active alert. */
return;
} else if (app->alert_dismiss_time < furi_get_tick()) {
/* Alert just expired. */
ui_dismiss_alert(app);
return;
}
/* Show the alert. A box with black border and a text inside. */
canvas_set_font(canvas, FontPrimary);
uint8_t w = canvas_string_width(canvas, app->alert_text);
uint8_t h = 8; // Font height.
uint8_t text_x = 64-(w/2);
uint8_t text_y = 32+4;
uint8_t padding = 3;
canvas_set_color(canvas,ColorBlack);
canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2);
canvas_set_color(canvas,ColorWhite);
canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2);
canvas_set_color(canvas,ColorBlack);
canvas_draw_str(canvas,text_x,text_y,app->alert_text);
}
/* =========================== Canvas extensions ============================ */
void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color)
{
struct {
uint8_t x; uint8_t y;
} dir[8] = {
{-1,-1},
{0,-1},
{1,-1},
{1,0},
{1,1},
{0,1},
{-1,1},
{-1,0}
};
/* Rotate in all the directions writing the same string to create a
* border, then write the actual string in the other color in the
* middle. */
canvas_set_color(canvas, border_color);
for (int j = 0; j < 8; j++)
canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str);
canvas_set_color(canvas, text_color);
canvas_draw_str(canvas,x,y,str);
canvas_set_color(canvas, ColorBlack);
}