[FL-1234] Keyboards redesign (#501)
* text input: keyboard redesign,; input: tune repeat keypress timing; archive: fix max string width when scrollbar is shown * byte input: redesign * byte/text input: long press back to activate backspace added * archive: pass absolute path to app * format sources * better keyboard timings Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									7f16c9fe5a
								
							
						
					
					
						commit
						d5df0483a3
					
				| @ -130,7 +130,6 @@ static void set_file_type(ArchiveFile_t* file, FileInfo* file_info) { | |||||||
| 
 | 
 | ||||||
| static bool archive_get_filenames(ArchiveApp* archive) { | static bool archive_get_filenames(ArchiveApp* archive) { | ||||||
|     furi_assert(archive); |     furi_assert(archive); | ||||||
| 
 |  | ||||||
|     FS_Dir_Api* dir_api = &archive->fs_api->dir; |     FS_Dir_Api* dir_api = &archive->fs_api->dir; | ||||||
|     ArchiveFile_t item; |     ArchiveFile_t item; | ||||||
|     FileInfo file_info; |     FileInfo file_info; | ||||||
| @ -169,7 +168,6 @@ static bool archive_get_filenames(ArchiveApp* archive) { | |||||||
|                     files_array_push_back(model->files, item); |                     files_array_push_back(model->files, item); | ||||||
|                     ArchiveFile_t_clear(&item); |                     ArchiveFile_t_clear(&item); | ||||||
|                 } |                 } | ||||||
| 
 |  | ||||||
|             } else { |             } else { | ||||||
|                 dir_api->close(&directory); |                 dir_api->close(&directory); | ||||||
|                 string_clear(name); |                 string_clear(name); | ||||||
| @ -329,8 +327,15 @@ static void archive_file_menu_callback(ArchiveApp* archive) { | |||||||
|     switch(model->menu_idx) { |     switch(model->menu_idx) { | ||||||
|     case 0: |     case 0: | ||||||
|         if((selected->type != ArchiveFileTypeFolder && selected->type != ArchiveFileTypeUnknown)) { |         if((selected->type != ArchiveFileTypeFolder && selected->type != ArchiveFileTypeUnknown)) { | ||||||
|  |             string_t full_path; | ||||||
|  |             string_init_set(full_path, archive->browser.path); | ||||||
|  |             string_cat(full_path, "/"); | ||||||
|  |             string_cat(full_path, selected->name); | ||||||
|  | 
 | ||||||
|             archive_open_app( |             archive_open_app( | ||||||
|                 archive, flipper_app_name[selected->type], string_get_cstr(selected->name)); |                 archive, flipper_app_name[selected->type], string_get_cstr(full_path)); | ||||||
|  | 
 | ||||||
|  |             string_clear(full_path); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
|     case 1: |     case 1: | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ static void draw_list(Canvas* canvas, ArchiveViewModel* model) { | |||||||
|         string_set(str_buff, file->name); |         string_set(str_buff, file->name); | ||||||
| 
 | 
 | ||||||
|         if(is_known_app(file->type)) trim_file_ext(str_buff); |         if(is_known_app(file->type)) trim_file_ext(str_buff); | ||||||
|         elements_string_fit_width(canvas, str_buff, MAX_LEN_PX); |         elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); | ||||||
| 
 | 
 | ||||||
|         if(model->idx == idx) { |         if(model->idx == idx) { | ||||||
|             archive_draw_frame(canvas, i, scrollbar); |             archive_draw_frame(canvas, i, scrollbar); | ||||||
|  | |||||||
| @ -150,10 +150,13 @@ static char byte_input_get_nibble_text(uint8_t byte, bool high_nibble) { | |||||||
|  * @param model  |  * @param model  | ||||||
|  */ |  */ | ||||||
| static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { | static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { | ||||||
|     const uint8_t text_x = 3; |     const uint8_t text_x = 8; | ||||||
|     const uint8_t text_y = 25; |     const uint8_t text_y = 25; | ||||||
| 
 | 
 | ||||||
|     elements_slightly_rounded_frame(canvas, 1, 14, 126, 15); |     elements_slightly_rounded_frame(canvas, 6, 14, 116, 15); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_icon_name(canvas, 2, 19, I_ButtonLeftSmall_3x5); | ||||||
|  |     canvas_draw_icon_name(canvas, 123, 19, I_ButtonRightSmall_3x5); | ||||||
| 
 | 
 | ||||||
|     for(uint8_t i = model->first_visible_byte; |     for(uint8_t i = model->first_visible_byte; | ||||||
|         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); |         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); | ||||||
| @ -234,12 +237,15 @@ static void byte_input_draw_input(Canvas* canvas, ByteInputModel* model) { | |||||||
|  * @param model  |  * @param model  | ||||||
|  */ |  */ | ||||||
| static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { | static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model) { | ||||||
|     const uint8_t text_x = 3; |     const uint8_t text_x = 7; | ||||||
|     const uint8_t text_y = 25; |     const uint8_t text_y = 25; | ||||||
| 
 | 
 | ||||||
|     canvas_draw_box(canvas, 0, 12, 128, 19); |     canvas_draw_box(canvas, 0, 12, 127, 19); | ||||||
|     canvas_invert_color(canvas); |     canvas_invert_color(canvas); | ||||||
|     elements_slightly_rounded_frame(canvas, 1, 14, 126, 15); | 
 | ||||||
|  |     elements_slightly_rounded_frame(canvas, 6, 14, 115, 15); | ||||||
|  |     canvas_draw_icon_name(canvas, 2, 19, I_ButtonLeftSmall_3x5); | ||||||
|  |     canvas_draw_icon_name(canvas, 122, 19, I_ButtonRightSmall_3x5); | ||||||
| 
 | 
 | ||||||
|     for(uint8_t i = model->first_visible_byte; |     for(uint8_t i = model->first_visible_byte; | ||||||
|         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); |         i < model->first_visible_byte + MIN(model->bytes_count, max_drawable_bytes); | ||||||
| @ -247,7 +253,7 @@ static void byte_input_draw_input_selected(Canvas* canvas, ByteInputModel* model | |||||||
|         uint8_t byte_position = i - model->first_visible_byte; |         uint8_t byte_position = i - model->first_visible_byte; | ||||||
| 
 | 
 | ||||||
|         if(i == model->selected_byte) { |         if(i == model->selected_byte) { | ||||||
|             canvas_draw_box(canvas, text_x + byte_position * 14, text_y - 9, 15, 11); |             canvas_draw_box(canvas, text_x + 1 + byte_position * 14, text_y - 9, 13, 11); | ||||||
|             canvas_invert_color(canvas); |             canvas_invert_color(canvas); | ||||||
|             canvas_draw_glyph( |             canvas_draw_glyph( | ||||||
|                 canvas, |                 canvas, | ||||||
| @ -406,6 +412,17 @@ static void byte_input_call_changed_callback(ByteInputModel* model) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * @brief Clear selected byte  | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static void byte_input_clear_selected_byte(ByteInputModel* model) { | ||||||
|  |     model->bytes[model->selected_byte] = 0; | ||||||
|  |     model->selected_high_nibble = true; | ||||||
|  |     byte_input_dec_selected_byte(model); | ||||||
|  |     byte_input_call_changed_callback(model); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * @brief Handle up button |  * @brief Handle up button | ||||||
|  *  |  *  | ||||||
| @ -478,10 +495,7 @@ static void byte_input_handle_ok(ByteInputModel* model) { | |||||||
|         if(value == enter_symbol) { |         if(value == enter_symbol) { | ||||||
|             byte_input_call_input_callback(model); |             byte_input_call_input_callback(model); | ||||||
|         } else if(value == backspace_symbol) { |         } else if(value == backspace_symbol) { | ||||||
|             model->bytes[model->selected_byte] = 0; |             byte_input_clear_selected_byte(model); | ||||||
|             model->selected_high_nibble = true; |  | ||||||
|             byte_input_dec_selected_byte(model); |  | ||||||
|             byte_input_call_changed_callback(model); |  | ||||||
|         } else { |         } else { | ||||||
|             byte_input_set_nibble( |             byte_input_set_nibble( | ||||||
|                 model->bytes, model->selected_byte, value, model->selected_high_nibble); |                 model->bytes, model->selected_byte, value, model->selected_high_nibble); | ||||||
| @ -647,6 +661,16 @@ static bool byte_input_view_input_callback(InputEvent* event, void* context) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if((event->type == InputTypeLong || event->type == InputTypeRepeat) && | ||||||
|  |        event->key == InputKeyBack) { | ||||||
|  |         with_view_model( | ||||||
|  |             byte_input->view, (ByteInputModel * model) { | ||||||
|  |                 byte_input_clear_selected_byte(model); | ||||||
|  |                 return true; | ||||||
|  |             }); | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -42,10 +42,10 @@ static const TextInputKey keyboard_keys_row_1[] = { | |||||||
|     {'i', 64, 8}, |     {'i', 64, 8}, | ||||||
|     {'o', 73, 8}, |     {'o', 73, 8}, | ||||||
|     {'p', 82, 8}, |     {'p', 82, 8}, | ||||||
|     {'7', 91, 8}, |     {'0', 91, 8}, | ||||||
|     {'8', 100, 8}, |     {'1', 100, 8}, | ||||||
|     {'9', 109, 8}, |     {'2', 110, 8}, | ||||||
|     {'_', 118, 8}, |     {'3', 120, 8}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const TextInputKey keyboard_keys_row_2[] = { | static const TextInputKey keyboard_keys_row_2[] = { | ||||||
| @ -58,10 +58,10 @@ static const TextInputKey keyboard_keys_row_2[] = { | |||||||
|     {'j', 55, 20}, |     {'j', 55, 20}, | ||||||
|     {'k', 64, 20}, |     {'k', 64, 20}, | ||||||
|     {'l', 73, 20}, |     {'l', 73, 20}, | ||||||
|     {'4', 82, 20}, |     {BACKSPACE_KEY, 82, 12}, | ||||||
|     {'5', 91, 20}, |     {'4', 100, 20}, | ||||||
|     {'6', 100, 20}, |     {'5', 110, 20}, | ||||||
|     {BACKSPACE_KEY, 110, 12}, |     {'6', 120, 20}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const TextInputKey keyboard_keys_row_3[] = { | static const TextInputKey keyboard_keys_row_3[] = { | ||||||
| @ -72,11 +72,11 @@ static const TextInputKey keyboard_keys_row_3[] = { | |||||||
|     {'b', 37, 32}, |     {'b', 37, 32}, | ||||||
|     {'n', 46, 32}, |     {'n', 46, 32}, | ||||||
|     {'m', 55, 32}, |     {'m', 55, 32}, | ||||||
|     {'0', 64, 32}, |     {'_', 64, 32}, | ||||||
|     {'1', 73, 32}, |     {ENTER_KEY, 74, 23}, | ||||||
|     {'2', 82, 32}, |     {'7', 100, 32}, | ||||||
|     {'3', 91, 32}, |     {'8', 110, 32}, | ||||||
|     {ENTER_KEY, 102, 23}, |     {'9', 120, 32}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static uint8_t get_row_size(uint8_t row_index) { | static uint8_t get_row_size(uint8_t row_index) { | ||||||
| @ -127,24 +127,41 @@ static const char char_to_uppercase(const char letter) { | |||||||
|     return (letter - 0x20); |     return (letter - 0x20); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void text_input_backspace_cb(TextInputModel* model) { | ||||||
|  |     uint8_t text_length = strlen(model->text); | ||||||
|  |     if(text_length > 0) { | ||||||
|  |         model->text[text_length - 1] = 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | static void text_input_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|     TextInputModel* model = _model; |     TextInputModel* model = _model; | ||||||
|     uint8_t text_length = strlen(model->text); |     uint8_t text_length = strlen(model->text); | ||||||
|     uint8_t needed_string_width = canvas_width(canvas) - 4 - 7 - 4; |     uint8_t needed_string_width = canvas_width(canvas) - 8; | ||||||
|  |     uint8_t start_pos = 4; | ||||||
|  | 
 | ||||||
|     char* text = model->text; |     char* text = model->text; | ||||||
| 
 | 
 | ||||||
|     canvas_clear(canvas); |     canvas_clear(canvas); | ||||||
|     canvas_set_color(canvas, ColorBlack); |     canvas_set_color(canvas, ColorBlack); | ||||||
| 
 | 
 | ||||||
|     canvas_draw_str(canvas, 2, 8, model->header); |     canvas_draw_str(canvas, 2, 8, model->header); | ||||||
|     elements_slightly_rounded_frame(canvas, 1, 12, 122, 15); |     elements_slightly_rounded_frame(canvas, 1, 12, 126, 15); | ||||||
|  | 
 | ||||||
|  |     if(canvas_string_width(canvas, text) > needed_string_width) { | ||||||
|  |         canvas_draw_str(canvas, start_pos, 22, "..."); | ||||||
|  |         start_pos += 6; | ||||||
|  |         needed_string_width -= 8; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { |     while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { | ||||||
|         text++; |         text++; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     canvas_draw_str(canvas, 4, 22, text); |     canvas_draw_str(canvas, start_pos, 22, text); | ||||||
|     canvas_draw_str(canvas, 4 + canvas_string_width(canvas, text) + 1, 22, "|"); | 
 | ||||||
|  |     canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 22, "|"); | ||||||
|  |     canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 22, "|"); | ||||||
| 
 | 
 | ||||||
|     canvas_set_font(canvas, FontKeyboard); |     canvas_set_font(canvas, FontKeyboard); | ||||||
| 
 | 
 | ||||||
| @ -220,6 +237,9 @@ static void text_input_handle_up(TextInput* text_input) { | |||||||
|         text_input->view, (TextInputModel * model) { |         text_input->view, (TextInputModel * model) { | ||||||
|             if(model->selected_row > 0) { |             if(model->selected_row > 0) { | ||||||
|                 model->selected_row--; |                 model->selected_row--; | ||||||
|  |                 if(model->selected_column > get_row_size(model->selected_row) - 5) { | ||||||
|  |                     model->selected_column = model->selected_column + 1; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
|         }); |         }); | ||||||
| @ -230,8 +250,8 @@ static void text_input_handle_down(TextInput* text_input) { | |||||||
|         text_input->view, (TextInputModel * model) { |         text_input->view, (TextInputModel * model) { | ||||||
|             if(model->selected_row < keyboard_row_count - 1) { |             if(model->selected_row < keyboard_row_count - 1) { | ||||||
|                 model->selected_row++; |                 model->selected_row++; | ||||||
|                 if(model->selected_column > get_row_size(model->selected_row) - 1) { |                 if(model->selected_column > get_row_size(model->selected_row) - 4) { | ||||||
|                     model->selected_column = get_row_size(model->selected_row) - 1; |                     model->selected_column = model->selected_column - 1; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             return true; |             return true; | ||||||
| @ -273,9 +293,7 @@ static void text_input_handle_ok(TextInput* text_input) { | |||||||
|                     model->callback(model->callback_context, model->text); |                     model->callback(model->callback_context, model->text); | ||||||
|                 } |                 } | ||||||
|             } else if(selected == BACKSPACE_KEY) { |             } else if(selected == BACKSPACE_KEY) { | ||||||
|                 if(text_length > 0) { |                 text_input_backspace_cb(model); | ||||||
|                     model->text[text_length - 1] = 0; |  | ||||||
|                 } |  | ||||||
|             } else if(text_length < model->max_text_length) { |             } else if(text_length < model->max_text_length) { | ||||||
|                 if(text_length == 0 && char_is_lowercase(selected)) { |                 if(text_length == 0 && char_is_lowercase(selected)) { | ||||||
|                     selected = char_to_uppercase(selected); |                     selected = char_to_uppercase(selected); | ||||||
| @ -319,6 +337,17 @@ static bool text_input_view_input_callback(InputEvent* event, void* context) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if((event->type == InputTypeLong || event->type == InputTypeRepeat) && | ||||||
|  |        event->key == InputKeyBack) { | ||||||
|  |         with_view_model( | ||||||
|  |             text_input->view, (TextInputModel * model) { | ||||||
|  |                 text_input_backspace_cb(model); | ||||||
|  |                 return true; | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|  |         consumed = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -12,8 +12,8 @@ | |||||||
| #include <api-hal-gpio.h> | #include <api-hal-gpio.h> | ||||||
| 
 | 
 | ||||||
| #define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2) | #define INPUT_DEBOUNCE_TICKS_HALF (INPUT_DEBOUNCE_TICKS / 2) | ||||||
| #define INPUT_PRESS_TICKS 200 | #define INPUT_PRESS_TICKS 150 | ||||||
| #define INPUT_LONG_PRESS_COUNTS 4 | #define INPUT_LONG_PRESS_COUNTS 2 | ||||||
| #define INPUT_THREAD_FLAG_ISR 0x00000001 | #define INPUT_THREAD_FLAG_ISR 0x00000001 | ||||||
| 
 | 
 | ||||||
| /* Input pin state */ | /* Input pin state */ | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 its your bedtime
						its your bedtime