1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Add date/time input module

ofw pr 4261 by aaronjamt
This commit is contained in:
MX
2025-12-01 06:06:30 +03:00
parent a7561bee98
commit 6abd2b0e9f
13 changed files with 908 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# Date/Time Input {#example_date_time_input}
Simple view that allows the user to adjust a date and/or time.
## Source code
Source code for this example can be found [here](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/examples/example_date_time_input).
## General principle
Callbacks can be defined for every time a value is edited (useful for application-specific bounds checking or validation) and for when the user is done editing (back button is pressed). The provided DateTime object is used both as the initial value and as the place where the result is stored.
The fields which the user is allowed to edit can be defined using `date_time_input_set_editable_fields()`. Disabled fields are shown but aren't able to be selected and don't have an outer box. If all fields are disabled, the view is read-only and no cursor will be shown.

View File

@@ -0,0 +1,9 @@
App(
appid="example_date_time_input",
name="Example: Date/Time Input",
apptype=FlipperAppType.EXTERNAL,
entry_point="example_date_time_input",
requires=["gui"],
stack_size=1 * 1024,
fap_category="Examples",
)

View File

@@ -0,0 +1,79 @@
#include "example_date_time_input.h"
bool example_date_time_input_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
ExampleDateTimeInput* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool example_date_time_input_back_event_callback(void* context) {
furi_assert(context);
ExampleDateTimeInput* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static ExampleDateTimeInput* example_date_time_input_alloc() {
ExampleDateTimeInput* app = malloc(sizeof(ExampleDateTimeInput));
app->gui = furi_record_open(RECORD_GUI);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&example_date_time_input_scene_handlers, app);
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
view_dispatcher_set_custom_event_callback(
app->view_dispatcher, example_date_time_input_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, example_date_time_input_back_event_callback);
app->date_time_input = date_time_input_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ExampleDateTimeInputViewIdDateTimeInput,
date_time_input_get_view(app->date_time_input));
app->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
ExampleDateTimeInputViewIdShowDateTime,
dialog_ex_get_view(app->dialog_ex));
// Fill in current date & time
furi_hal_rtc_get_datetime(&app->date_time);
app->edit_date = false;
app->edit_time = false;
return app;
}
static void example_date_time_input_free(ExampleDateTimeInput* app) {
furi_assert(app);
view_dispatcher_remove_view(app->view_dispatcher, ExampleDateTimeInputViewIdShowDateTime);
dialog_ex_free(app->dialog_ex);
view_dispatcher_remove_view(app->view_dispatcher, ExampleDateTimeInputViewIdDateTimeInput);
date_time_input_free(app->date_time_input);
scene_manager_free(app->scene_manager);
view_dispatcher_free(app->view_dispatcher);
furi_record_close(RECORD_GUI);
app->gui = NULL;
free(app);
}
int32_t example_date_time_input(void* p) {
UNUSED(p);
ExampleDateTimeInput* app = example_date_time_input_alloc();
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
scene_manager_next_scene(app->scene_manager, ExampleDateTimeInputSceneShowDateTime);
view_dispatcher_run(app->view_dispatcher);
example_date_time_input_free(app);
return 0;
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <gui/scene_manager.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/date_time_input.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <input/input.h>
#include "scenes/example_date_time_input_scene.h"
typedef struct ExampleDateTimeInputShowDateTime ExampleDateTimeInputShowDateTime;
typedef enum {
ExampleDateTimeInputViewIdShowDateTime,
ExampleDateTimeInputViewIdDateTimeInput,
} ExampleDateTimeInputViewId;
typedef struct {
Gui* gui;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
DateTimeInput* date_time_input;
DialogEx* dialog_ex;
DateTime date_time;
bool edit_date;
bool edit_time;
} ExampleDateTimeInput;

View File

@@ -0,0 +1,31 @@
#include "example_date_time_input_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const example_date_time_input_on_enter_handlers[])(void*) = {
#include "example_date_time_input_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_event handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
bool (*const example_date_time_input_on_event_handlers[])(void* context, SceneManagerEvent event) =
{
#include "example_date_time_input_scene_config.h"
};
#undef ADD_SCENE
// Generate scene on_exit handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
void (*const example_date_time_input_on_exit_handlers[])(void* context) = {
#include "example_date_time_input_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers example_date_time_input_scene_handlers = {
.on_enter_handlers = example_date_time_input_on_enter_handlers,
.on_event_handlers = example_date_time_input_on_event_handlers,
.on_exit_handlers = example_date_time_input_on_exit_handlers,
.scene_num = ExampleDateTimeInputSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) ExampleDateTimeInputScene##id,
typedef enum {
#include "example_date_time_input_scene_config.h"
ExampleDateTimeInputSceneNum,
} ExampleDateTimeInputScene;
#undef ADD_SCENE
extern const SceneManagerHandlers example_date_time_input_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "example_date_time_input_scene_config.h"
#undef ADD_SCENE
// Generate scene on_event handlers declaration
#define ADD_SCENE(prefix, name, id) \
bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
#include "example_date_time_input_scene_config.h"
#undef ADD_SCENE
// Generate scene on_exit handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
#include "example_date_time_input_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,2 @@
ADD_SCENE(example_date_time_input, input_date_time, InputDateTime)
ADD_SCENE(example_date_time_input, show_date_time, ShowDateTime)

View File

@@ -0,0 +1,47 @@
#include "../example_date_time_input.h"
void example_date_time_input_scene_input_date_time_callback(void* context) {
ExampleDateTimeInput* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, 0);
}
void example_date_time_input_scene_input_date_time_on_enter(void* context) {
furi_assert(context);
ExampleDateTimeInput* app = context;
DateTimeInput* date_time_input = app->date_time_input;
date_time_input_set_result_callback(
date_time_input,
NULL,
example_date_time_input_scene_input_date_time_callback,
context,
&app->date_time);
date_time_input_set_editable_fields(
date_time_input,
app->edit_date,
app->edit_date,
app->edit_date,
app->edit_time,
app->edit_time,
app->edit_time);
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleDateTimeInputViewIdDateTimeInput);
}
bool example_date_time_input_scene_input_date_time_on_event(void* context, SceneManagerEvent event) {
ExampleDateTimeInput* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) { //Back button pressed
scene_manager_previous_scene(app->scene_manager);
return true;
}
return consumed;
}
void example_date_time_input_scene_input_date_time_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,94 @@
#include "../example_date_time_input.h"
static void
example_date_time_input_scene_confirm_dialog_callback(DialogExResult result, void* context) {
ExampleDateTimeInput* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
static void example_date_time_input_scene_update_view(void* context) {
ExampleDateTimeInput* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_set_header(dialog_ex, "The date and time are", 64, 0, AlignCenter, AlignTop);
uint8_t hour = app->date_time.hour;
char label_hour[4] = "";
if(furi_hal_rtc_get_locale_timeformat() == FuriHalRtcLocaleTimeFormat12h) {
if(hour < 12) {
snprintf(label_hour, sizeof(label_hour), " AM");
} else {
snprintf(label_hour, sizeof(label_hour), " PM");
}
hour %= 12;
if(hour == 0) hour = 12;
}
char buffer[29] = {};
snprintf(
buffer,
sizeof(buffer),
"%04d-%02d-%02d\n%02d:%02d:%02d%s",
app->date_time.year,
app->date_time.month,
app->date_time.day,
hour,
app->date_time.minute,
app->date_time.second,
label_hour);
dialog_ex_set_text(dialog_ex, buffer, 64, 29, AlignCenter, AlignCenter);
dialog_ex_set_left_button_text(dialog_ex, "Date");
dialog_ex_set_right_button_text(dialog_ex, "Time");
dialog_ex_set_center_button_text(dialog_ex, "Both");
dialog_ex_set_result_callback(
dialog_ex, example_date_time_input_scene_confirm_dialog_callback);
dialog_ex_set_context(dialog_ex, app);
}
void example_date_time_input_scene_show_date_time_on_enter(void* context) {
furi_assert(context);
ExampleDateTimeInput* app = context;
example_date_time_input_scene_update_view(app);
view_dispatcher_switch_to_view(app->view_dispatcher, ExampleDateTimeInputViewIdShowDateTime);
}
bool example_date_time_input_scene_show_date_time_on_event(void* context, SceneManagerEvent event) {
ExampleDateTimeInput* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
app->edit_date = true;
app->edit_time = true;
scene_manager_next_scene(app->scene_manager, ExampleDateTimeInputSceneInputDateTime);
consumed = true;
break;
case DialogExResultLeft:
app->edit_date = true;
app->edit_time = false;
scene_manager_next_scene(app->scene_manager, ExampleDateTimeInputSceneInputDateTime);
consumed = true;
break;
case DialogExResultRight:
app->edit_date = false;
app->edit_time = true;
scene_manager_next_scene(app->scene_manager, ExampleDateTimeInputSceneInputDateTime);
consumed = true;
break;
default:
break;
}
}
return consumed;
}
void example_date_time_input_scene_show_date_time_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -35,5 +35,6 @@ App(
"modules/submenu.h", "modules/submenu.h",
"modules/widget_elements/widget_element.h", "modules/widget_elements/widget_element.h",
"modules/empty_screen.h", "modules/empty_screen.h",
"modules/date_time_input.h",
], ],
) )

View File

@@ -0,0 +1,479 @@
#include "date_time_input.h"
#include "furi_hal_rtc.h"
#include <furi.h>
#include <assets_icons.h>
#define get_state(m, r, c, f) \
((m)->editable.f ? ((m)->row == (r) && (m)->column == (c) ? \
((m)->editing ? EditStateActiveEditing : EditStateActive) : \
EditStateNone) : \
EditStateDisabled)
#define ROW_0_Y (9)
#define ROW_0_H (20)
#define ROW_1_Y (40)
#define ROW_1_H (20)
#define ROW_COUNT 2
#define COLUMN_COUNT 3
struct DateTimeInput {
View* view;
};
typedef struct {
DateTime* datetime;
uint8_t row;
uint8_t column;
bool editing;
struct {
bool year;
bool month;
bool day;
bool hour;
bool minute;
bool second;
} editable;
DateTimeChangedCallback changed_callback;
DateTimeDoneCallback done_callback;
void* callback_context;
} DateTimeInputModel;
typedef enum {
EditStateNone,
EditStateActive,
EditStateActiveEditing,
EditStateDisabled
} EditState;
static inline void date_time_input_cleanup_date(DateTime* dt) {
uint8_t day_per_month =
datetime_get_days_per_month(datetime_is_leap_year(dt->year), dt->month);
if(dt->day > day_per_month) {
dt->day = day_per_month;
}
}
static inline void date_time_input_draw_block(
Canvas* canvas,
int32_t x,
int32_t y,
size_t w,
size_t h,
Font font,
EditState state,
const char* text) {
furi_assert(canvas);
furi_assert(text);
canvas_set_color(canvas, ColorBlack);
if(state != EditStateDisabled) {
if(state != EditStateNone) {
if(state == EditStateActiveEditing) {
canvas_draw_icon(canvas, x + w / 2 - 2, y - 1 - 3, &I_SmallArrowUp_3x5);
canvas_draw_icon(canvas, x + w / 2 - 2, y + h + 1, &I_SmallArrowDown_3x5);
}
canvas_draw_rbox(canvas, x, y, w, h, 1);
canvas_set_color(canvas, ColorWhite);
} else {
canvas_draw_rframe(canvas, x, y, w, h, 1);
}
}
canvas_set_font(canvas, font);
canvas_draw_str_aligned(canvas, x + w / 2, y + h / 2, AlignCenter, AlignCenter, text);
if(state != EditStateNone) {
canvas_set_color(canvas, ColorBlack);
}
}
static inline void date_time_input_draw_text(
Canvas* canvas,
int32_t x,
int32_t y,
size_t w,
size_t h,
Font font,
EditState state,
const char* text) {
furi_assert(canvas);
furi_assert(text);
canvas_set_color(canvas, ColorBlack);
if(state != EditStateDisabled && state != EditStateNone) {
canvas_set_color(canvas, ColorWhite);
}
canvas_set_font(canvas, font);
canvas_draw_str_aligned(canvas, x + w / 2, y + h / 2, AlignCenter, AlignCenter, text);
if(state != EditStateNone) {
canvas_set_color(canvas, ColorBlack);
}
}
static void date_time_input_draw_hour_24hr_callback(Canvas* canvas, DateTimeInputModel* model) {
char buffer[4];
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, ROW_1_Y - 2, " H H M M S S");
canvas_set_font(canvas, FontPrimary);
snprintf(buffer, sizeof(buffer), "%02u", model->datetime->hour);
date_time_input_draw_block(
canvas, 30, ROW_1_Y, 28, ROW_1_H, FontBigNumbers, get_state(model, 1, 0, hour), buffer);
canvas_draw_box(canvas, 60, ROW_1_Y + ROW_1_H - 7, 2, 2);
canvas_draw_box(canvas, 60, ROW_1_Y + ROW_1_H - 7 - 6, 2, 2);
}
static void date_time_input_draw_hour_12hr_callback(Canvas* canvas, DateTimeInputModel* model) {
char buffer[4];
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, ROW_1_Y - 2, " H H M M S S");
canvas_set_font(canvas, FontPrimary);
uint8_t hour = model->datetime->hour % 12;
// Show 12:00 instead of 00:00 for 12-hour time
if(hour == 0) hour = 12;
// Placeholder spaces to make room for AM/PM since FontBigNumbers can't draw letters
date_time_input_draw_block(
canvas, 8, ROW_1_Y, 50, ROW_1_H, FontBigNumbers, get_state(model, 1, 0, hour), buffer);
canvas_draw_box(canvas, 60, ROW_1_Y + ROW_1_H - 7, 2, 2);
canvas_draw_box(canvas, 60, ROW_1_Y + ROW_1_H - 7 - 6, 2, 2);
snprintf(buffer, sizeof(buffer), "%02u", hour);
date_time_input_draw_text(
canvas, 8, ROW_1_Y, 30, ROW_1_H, FontBigNumbers, get_state(model, 1, 0, hour), buffer);
// The AM and PM text shift by 1 pixel so compensate to make them line up
if(model->datetime->hour < 12) {
date_time_input_draw_text(
canvas, 30, ROW_1_Y + 3, 30, ROW_1_H, FontPrimary, get_state(model, 1, 0, hour), "AM");
} else {
date_time_input_draw_text(
canvas, 31, ROW_1_Y + 3, 30, ROW_1_H, FontPrimary, get_state(model, 1, 0, hour), "PM");
}
}
static void date_time_input_draw_time_callback(Canvas* canvas, DateTimeInputModel* model) {
furi_check(model->datetime);
char buffer[4];
// Draw hour depending on RTC time format
if(furi_hal_rtc_get_locale_timeformat() == FuriHalRtcLocaleTimeFormat24h) {
date_time_input_draw_hour_24hr_callback(canvas, model);
} else {
date_time_input_draw_hour_12hr_callback(canvas, model);
}
snprintf(buffer, sizeof(buffer), "%02u", model->datetime->minute);
date_time_input_draw_block(
canvas, 64, ROW_1_Y, 28, ROW_1_H, FontBigNumbers, get_state(model, 1, 1, minute), buffer);
canvas_draw_box(canvas, 94, ROW_1_Y + ROW_1_H - 7, 2, 2);
canvas_draw_box(canvas, 94, ROW_1_Y + ROW_1_H - 7 - 6, 2, 2);
snprintf(buffer, sizeof(buffer), "%02u", model->datetime->second);
date_time_input_draw_block(
canvas, 98, ROW_1_Y, 28, ROW_1_H, FontBigNumbers, get_state(model, 1, 2, second), buffer);
}
static void date_time_input_draw_date_callback(Canvas* canvas, DateTimeInputModel* model) {
furi_check(model->datetime);
char buffer[6];
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 0, ROW_0_Y - 2, " Y Y Y Y M M D D");
canvas_set_font(canvas, FontPrimary);
snprintf(buffer, sizeof(buffer), "%04u", model->datetime->year);
date_time_input_draw_block(
canvas, 2, ROW_0_Y, 56, ROW_0_H, FontBigNumbers, get_state(model, 0, 0, year), buffer);
snprintf(buffer, sizeof(buffer), "%02u", model->datetime->month);
date_time_input_draw_block(
canvas, 64, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 1, month), buffer);
canvas_draw_box(canvas, 64 - 5, ROW_0_Y + (ROW_0_H / 2), 4, 2);
snprintf(buffer, sizeof(buffer), "%02u", model->datetime->day);
date_time_input_draw_block(
canvas, 98, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 2, day), buffer);
canvas_draw_box(canvas, 98 - 5, ROW_0_Y + (ROW_0_H / 2), 4, 2);
}
static void date_time_input_view_draw_callback(Canvas* canvas, void* _model) {
DateTimeInputModel* model = _model;
canvas_clear(canvas);
date_time_input_draw_time_callback(canvas, model);
date_time_input_draw_date_callback(canvas, model);
}
static inline bool is_allowed_to_edit(DateTimeInputModel* model) {
return (model->row == 0 && ((model->column == 0 && model->editable.year) |
(model->column == 1 && model->editable.month) |
(model->column == 2 && model->editable.day))) ||
((model->row == 1) && ((model->column == 0 && model->editable.hour) |
(model->column == 1 && model->editable.minute) |
(model->column == 2 && model->editable.second)));
}
static bool date_time_input_navigation_callback(InputEvent* event, DateTimeInputModel* model) {
if(event->key == InputKeyUp) {
if(model->row > 0) model->row--;
if(!is_allowed_to_edit(model)) model->row++;
} else if(event->key == InputKeyDown) {
if(model->row < ROW_COUNT - 1) model->row++;
if(!is_allowed_to_edit(model)) model->row--;
} else if(event->key == InputKeyOk) {
model->editing = !model->editing;
} else if(event->key == InputKeyRight) {
if(model->column < COLUMN_COUNT - 1) model->column++;
while(model->column < COLUMN_COUNT - 1 && !is_allowed_to_edit(model))
model->column++;
while(model->column > 0 && !is_allowed_to_edit(model))
model->column--;
} else if(event->key == InputKeyLeft) {
if(model->column > 0) model->column--;
while(model->column > 0 && !is_allowed_to_edit(model))
model->column--;
while(model->column < COLUMN_COUNT - 1 && !is_allowed_to_edit(model))
model->column++;
} else if(event->key == InputKeyBack && model->editing) {
model->editing = false;
} else if(event->key == InputKeyBack && model->done_callback) {
model->done_callback(model->callback_context);
} else {
return false;
}
return true;
}
static bool date_time_input_time_callback(InputEvent* event, DateTimeInputModel* model) {
furi_check(model->datetime);
if(event->key == InputKeyUp) {
if(model->column == 0) {
model->datetime->hour++;
model->datetime->hour = model->datetime->hour % 24;
} else if(model->column == 1) {
model->datetime->minute++;
model->datetime->minute = model->datetime->minute % 60;
} else if(model->column == 2) {
model->datetime->second++;
model->datetime->second = model->datetime->second % 60;
} else {
furi_crash();
}
} else if(event->key == InputKeyDown) {
if(model->column == 0) {
if(model->datetime->hour > 0) {
model->datetime->hour--;
} else {
model->datetime->hour = 23;
}
model->datetime->hour = model->datetime->hour % 24;
} else if(model->column == 1) {
if(model->datetime->minute > 0) {
model->datetime->minute--;
} else {
model->datetime->minute = 59;
}
model->datetime->minute = model->datetime->minute % 60;
} else if(model->column == 2) {
if(model->datetime->second > 0) {
model->datetime->second--;
} else {
model->datetime->second = 59;
}
model->datetime->second = model->datetime->second % 60;
} else {
furi_crash();
}
} else {
return date_time_input_navigation_callback(event, model);
}
return true;
}
static bool date_time_input_date_callback(InputEvent* event, DateTimeInputModel* model) {
furi_check(model->datetime);
if(event->key == InputKeyUp) {
if(model->column == 0) {
if(model->datetime->year < 2099) {
model->datetime->year++;
}
} else if(model->column == 1) {
if(model->datetime->month < 12) {
model->datetime->month++;
}
} else if(model->column == 2) {
if(model->datetime->day < 31) model->datetime->day++;
} else {
furi_crash();
}
} else if(event->key == InputKeyDown) {
if(model->column == 0) {
if(model->datetime->year > 1980) {
model->datetime->year--;
}
} else if(model->column == 1) {
if(model->datetime->month > 1) {
model->datetime->month--;
}
} else if(model->column == 2) {
if(model->datetime->day > 1) {
model->datetime->day--;
}
} else {
furi_crash();
}
} else {
return date_time_input_navigation_callback(event, model);
}
date_time_input_cleanup_date(model->datetime);
return true;
}
static bool date_time_input_view_input_callback(InputEvent* event, void* context) {
DateTimeInput* instance = context;
bool consumed = false;
with_view_model(
instance->view,
DateTimeInputModel * model,
{
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
if(model->editing) {
if(model->row == 0) {
consumed = date_time_input_date_callback(event, model);
} else if(model->row == 1) {
consumed = date_time_input_time_callback(event, model);
} else {
furi_crash();
}
if(model->changed_callback) {
model->changed_callback(model->callback_context);
}
} else {
consumed = date_time_input_navigation_callback(event, model);
}
}
},
true);
return consumed;
}
/** Reset all input-related data in model
*
* @param model The model
*/
static void date_time_input_reset_model_input_data(DateTimeInputModel* model) {
model->row = 0;
model->column = 0;
model->datetime = NULL;
model->editable.year = true;
model->editable.month = true;
model->editable.day = true;
model->editable.hour = true;
model->editable.minute = true;
model->editable.second = true;
}
DateTimeInput* date_time_input_alloc(void) {
DateTimeInput* date_time_input = malloc(sizeof(DateTimeInput));
date_time_input->view = view_alloc();
view_allocate_model(date_time_input->view, ViewModelTypeLocking, sizeof(DateTimeInputModel));
view_set_context(date_time_input->view, date_time_input);
view_set_draw_callback(date_time_input->view, date_time_input_view_draw_callback);
view_set_input_callback(date_time_input->view, date_time_input_view_input_callback);
with_view_model(
date_time_input->view,
DateTimeInputModel * model,
{
model->changed_callback = NULL;
model->callback_context = NULL;
date_time_input_reset_model_input_data(model);
},
true);
return date_time_input;
}
void date_time_input_free(DateTimeInput* date_time_input) {
furi_check(date_time_input);
view_free(date_time_input->view);
free(date_time_input);
}
View* date_time_input_get_view(DateTimeInput* date_time_input) {
furi_check(date_time_input);
return date_time_input->view;
}
void date_time_input_set_result_callback(
DateTimeInput* date_time_input,
DateTimeChangedCallback changed_callback,
DateTimeDoneCallback done_callback,
void* callback_context,
DateTime* current_datetime) {
furi_check(date_time_input);
with_view_model(
date_time_input->view,
DateTimeInputModel * model,
{
date_time_input_reset_model_input_data(model);
model->changed_callback = changed_callback;
model->done_callback = done_callback;
model->callback_context = callback_context;
model->datetime = current_datetime;
},
true);
}
void date_time_input_set_editable_fields(
DateTimeInput* date_time_input,
bool year,
bool month,
bool day,
bool hour,
bool minute,
bool second) {
furi_check(date_time_input);
with_view_model(
date_time_input->view,
DateTimeInputModel * model,
{
model->editable.year = year;
model->editable.month = month;
model->editable.day = day;
model->editable.hour = hour;
model->editable.minute = minute;
model->editable.second = second;
// Select first editable field
model->row = 0;
model->column = 0;
while(!is_allowed_to_edit(model)) {
// Cycle to next column and wrap around at end
model->column = (model->column + 1) % COLUMN_COUNT;
// If the column is 0, we wrapped, so go to next row
if(model->column == 0) model->row++;
// If we passed the last row, give up
if(model->row >= ROW_COUNT) break;
};
},
true);
}

View File

@@ -0,0 +1,82 @@
/**
* @file date_time_input.h
* GUI: DateTimeInput view module API
*/
#pragma once
#include <gui/view.h>
#include <datetime.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Date/time input anonymous structure */
typedef struct DateTimeInput DateTimeInput;
/** callback that is executed on value change */
typedef void (*DateTimeChangedCallback)(void* context);
/** callback that is executed on back button press */
typedef void (*DateTimeDoneCallback)(void* context);
/** Allocate and initialize date/time input
*
* This screen used to input a date and time
*
* @return DateTimeInput instance
*/
DateTimeInput* date_time_input_alloc(void);
/** Deinitialize and free date/time input
*
* @param date_time_input Date/time input instance
*/
void date_time_input_free(DateTimeInput* date_time_input);
/** Get date/time input view
*
* @param date_time_input Date/time input instance
*
* @return View instance that can be used for embedding
*/
View* date_time_input_get_view(DateTimeInput* date_time_input);
/** Set date/time input result callback
*
* @param date_time_input date/time input instance
* @param changed_callback changed callback fn
* @param done_callback finished callback fn
* @param callback_context callback context
* @param datetime date/time value
*/
void date_time_input_set_result_callback(
DateTimeInput* date_time_input,
DateTimeChangedCallback changed_callback,
DateTimeDoneCallback done_callback,
void* callback_context,
DateTime* datetime);
/** Set date/time fields which can be edited
*
* @param date_time_input date/time input instance
* @param year whether to allow editing the year
* @param month whether to allow editing the month
* @param day whether to allow editing the day
* @param hour whether to allow editing the hour
* @param minute whether to allow editing the minute
* @param second whether to allow editing the second
*/
void date_time_input_set_editable_fields(
DateTimeInput* date_time_input,
bool year,
bool month,
bool day,
bool hour,
bool minute,
bool second);
#ifdef __cplusplus
}
#endif

View File

@@ -14,6 +14,7 @@ Header,+,applications/services/gui/icon_i.h,,
Header,+,applications/services/gui/modules/button_menu.h,, Header,+,applications/services/gui/modules/button_menu.h,,
Header,+,applications/services/gui/modules/button_panel.h,, Header,+,applications/services/gui/modules/button_panel.h,,
Header,+,applications/services/gui/modules/byte_input.h,, Header,+,applications/services/gui/modules/byte_input.h,,
Header,+,applications/services/gui/modules/date_time_input.h,,
Header,+,applications/services/gui/modules/dialog_ex.h,, Header,+,applications/services/gui/modules/dialog_ex.h,,
Header,+,applications/services/gui/modules/empty_screen.h,, Header,+,applications/services/gui/modules/empty_screen.h,,
Header,+,applications/services/gui/modules/file_browser.h,, Header,+,applications/services/gui/modules/file_browser.h,,
@@ -951,6 +952,11 @@ Function,+,crypto1_reset,void,Crypto1*
Function,+,crypto1_word,uint32_t,"Crypto1*, uint32_t, int" Function,+,crypto1_word,uint32_t,"Crypto1*, uint32_t, int"
Function,-,ctermid,char*,char* Function,-,ctermid,char*,char*
Function,-,cuserid,char*,char* Function,-,cuserid,char*,char*
Function,+,date_time_input_alloc,DateTimeInput*,
Function,+,date_time_input_free,void,DateTimeInput*
Function,+,date_time_input_get_view,View*,DateTimeInput*
Function,+,date_time_input_set_editable_fields,void,"DateTimeInput*, _Bool, _Bool, _Bool, _Bool, _Bool, _Bool"
Function,+,date_time_input_set_result_callback,void,"DateTimeInput*, DateTimeChangedCallback, DateTimeDoneCallback, void*, DateTime*"
Function,+,datetime_datetime_to_timestamp,uint32_t,DateTime* Function,+,datetime_datetime_to_timestamp,uint32_t,DateTime*
Function,+,datetime_get_days_per_month,uint8_t,"_Bool, uint8_t" Function,+,datetime_get_days_per_month,uint8_t,"_Bool, uint8_t"
Function,+,datetime_get_days_per_year,uint16_t,uint16_t Function,+,datetime_get_days_per_year,uint16_t,uint16_t
1 entry status name type params
14 Header + applications/services/gui/modules/button_menu.h
15 Header + applications/services/gui/modules/button_panel.h
16 Header + applications/services/gui/modules/byte_input.h
17 Header + applications/services/gui/modules/date_time_input.h
18 Header + applications/services/gui/modules/dialog_ex.h
19 Header + applications/services/gui/modules/empty_screen.h
20 Header + applications/services/gui/modules/file_browser.h
952 Function + crypto1_word uint32_t Crypto1*, uint32_t, int
953 Function - ctermid char* char*
954 Function - cuserid char* char*
955 Function + date_time_input_alloc DateTimeInput*
956 Function + date_time_input_free void DateTimeInput*
957 Function + date_time_input_get_view View* DateTimeInput*
958 Function + date_time_input_set_editable_fields void DateTimeInput*, _Bool, _Bool, _Bool, _Bool, _Bool, _Bool
959 Function + date_time_input_set_result_callback void DateTimeInput*, DateTimeChangedCallback, DateTimeDoneCallback, void*, DateTime*
960 Function + datetime_datetime_to_timestamp uint32_t DateTime*
961 Function + datetime_get_days_per_month uint8_t _Bool, uint8_t
962 Function + datetime_get_days_per_year uint16_t uint16_t