From 5415dbf4a3f65e87773dc1cdff96077fd80c2cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Cadete?= Date: Sat, 9 Mar 2024 12:05:55 +0000 Subject: [PATCH] Implement a list box ui component in preparation for the campaign manager There might be some issues, so as usual, testing is needed --- CMakeLists.txt | 1 + src/graphics/list_box.c | 266 +++++++++++++++++++++++++++ src/graphics/list_box.h | 55 ++++++ src/window/asset_previewer.c | 345 +++++++++++++---------------------- src/window/cck_selection.c | 95 ++++------ src/window/file_dialog.c | 210 +++++++-------------- 6 files changed, 557 insertions(+), 415 deletions(-) create mode 100644 src/graphics/list_box.c create mode 100644 src/graphics/list_box.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 01596d5df1..c56d2fb2a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -504,6 +504,7 @@ set(GRAPHICS_FILES ${PROJECT_SOURCE_DIR}/src/graphics/image.c ${PROJECT_SOURCE_DIR}/src/graphics/image_button.c ${PROJECT_SOURCE_DIR}/src/graphics/lang_text.c + ${PROJECT_SOURCE_DIR}/src/graphics/list_box.c ${PROJECT_SOURCE_DIR}/src/graphics/menu.c ${PROJECT_SOURCE_DIR}/src/graphics/panel.c ${PROJECT_SOURCE_DIR}/src/graphics/rich_text.c diff --git a/src/graphics/list_box.c b/src/graphics/list_box.c new file mode 100644 index 0000000000..154dba8100 --- /dev/null +++ b/src/graphics/list_box.c @@ -0,0 +1,266 @@ +#include "list_box.h" + +#include "core/direction.h" +#include "graphics/button.h" +#include "graphics/font.h" +#include "graphics/panel.h" +#include "graphics/text.h" +#include "graphics/window.h" +#include "input/scroll.h" + + +void list_box_init(list_box_type *list_box, int total_items) +{ + list_box->selected_index = LIST_BOX_NO_SELECTION; + list_box->total_items = total_items; + list_box->focus_button_id = LIST_BOX_NO_SELECTION; + + scrollbar_init(&list_box->scrollbar, 0, list_box->total_items); +} + +void list_box_update_total_items(list_box_type *list_box, int total_items) +{ + list_box->total_items = total_items; + scrollbar_update_total_elements(&list_box->scrollbar, total_items); + list_box_request_refresh(list_box); +} + +int list_box_get_total_items(const list_box_type *list_box) +{ + return list_box->total_items; +} + +void list_box_select_index(list_box_type *list_box, int index) +{ + if (index == list_box->selected_index) { + return; + } + list_box_request_refresh(list_box); + list_box->selected_index = index; + if (list_box->on_select) { + list_box->on_select(index, 0); + } +} + +int list_box_get_selected_index(const list_box_type *list_box) +{ + return list_box->selected_index; +} + +void list_box_show_index(list_box_type *list_box, int index) +{ + if (index == LIST_BOX_NO_SELECTION) { + return; + } + scrollbar_type *scrollbar = &list_box->scrollbar; + if (index >= list_box->total_items) { + scrollbar_reset(scrollbar, list_box->total_items - scrollbar->elements_in_view + 1); + } else { + scrollbar_reset(scrollbar, index); + } +} + +void list_box_show_selected_index(list_box_type *list_box) +{ + list_box_show_index(list_box, list_box->selected_index); +} + +int list_box_get_scroll_position(const list_box_type *list_box) +{ + return list_box->scrollbar.scroll_position; +} + +void list_box_request_refresh(list_box_type *list_box) +{ + list_box->refresh_requested = 1; + if (!list_box->draw_inner_panel) { + window_request_refresh(); + } +} + +static int get_actual_width_blocks(const list_box_type *list_box) +{ + int width_blocks = list_box->width_blocks; + if (!list_box->extend_to_hidden_scrollbar || list_box->total_items > list_box->scrollbar.elements_in_view) { + width_blocks -= 2; + } + return width_blocks; +} + +static void draw_scrollbar(list_box_type *list_box) +{ + scrollbar_type *scrollbar = &list_box->scrollbar; + + scrollbar->x = list_box->x + (list_box->width_blocks - 2) * BLOCK_SIZE + 4; + scrollbar->y = list_box->y; + scrollbar->on_scroll_callback = window_request_refresh; + scrollbar->has_y_margin = 1; + scrollbar->dot_padding = list_box->decorate_scrollbar ? 8 : 0; + + scrollbar->height = list_box->height_blocks * BLOCK_SIZE; + int scrollable_height_pixels = scrollbar->height; + scrollbar->scrollable_width = (list_box->width_blocks - 2) * BLOCK_SIZE; + if (list_box->draw_inner_panel) { + scrollable_height_pixels -= BLOCK_SIZE;; + } + scrollbar->elements_in_view = scrollable_height_pixels / list_box->item_height; + + scrollbar_update_total_elements(scrollbar, list_box->total_items); + + if (list_box->decorate_scrollbar && list_box->total_items > scrollbar->elements_in_view) { + inner_panel_draw(scrollbar->x + 4, scrollbar->y + 32, 2, scrollbar->height / BLOCK_SIZE - 4); + } + scrollbar_draw(&list_box->scrollbar); +} + +void list_box_draw(list_box_type *list_box) +{ + draw_scrollbar(list_box); + + if(!list_box->refresh_requested) { + return; + } + list_box->refresh_requested = 0; + + int width_blocks = get_actual_width_blocks(list_box); + int padding = 0; + + if (list_box->draw_inner_panel) { + padding = BLOCK_SIZE / 2; + inner_panel_draw(list_box->x, list_box->y, width_blocks, list_box->height_blocks); + } + + if (list_box->draw_item) { + list_box_item item = { + .x = list_box->x + padding, + .y = list_box->y + padding, + .width = width_blocks * BLOCK_SIZE - padding * 2, + .height = list_box->item_height + }; + + int elements_in_view = list_box->scrollbar.elements_in_view; + int index = list_box->scrollbar.scroll_position; + + for (int i = 0; i < elements_in_view; i++, index++) { + if (index >= list_box->total_items) { + break; + } + item.index = index; + item.button_position = i; + item.is_selected = index == list_box->selected_index; + item.is_focused = list_box->focus_button_id == i; + list_box->draw_item(&item); + item.y += list_box->item_height; + } + } +} + +static int handle_arrow_keys(list_box_type *list_box, int direction) +{ + int delta; + switch (direction) { + case DIR_0_TOP: + case DIR_1_TOP_RIGHT: + case DIR_7_TOP_LEFT: + delta = -1; + break; + case DIR_4_BOTTOM: + case DIR_3_BOTTOM_RIGHT: + case DIR_5_BOTTOM_LEFT: + delta = 1; + break; + default: + return 0; + } + int max_index = list_box->total_items - 1; + if (list_box->selected_index == LIST_BOX_NO_SELECTION) { + if (delta == 1) { + list_box->selected_index = 0; + } else { + list_box->selected_index = max_index; + } + } else { + list_box->selected_index += delta; + if (list_box->selected_index == LIST_BOX_NO_SELECTION) { + list_box->selected_index = max_index; + } else if (list_box->selected_index > max_index) { + list_box->selected_index = 0; + } + } + list_box_request_refresh(list_box); + if (list_box->on_select) { + list_box->on_select(list_box->selected_index, 0); + } + scrollbar_type *scrollbar = &list_box->scrollbar; + if (list_box->selected_index > scrollbar->scroll_position + scrollbar->elements_in_view - 1) { + scrollbar_reset(scrollbar, list_box->selected_index - scrollbar->elements_in_view + 1); + } else if (list_box->selected_index < scrollbar->scroll_position) { + scrollbar_reset(scrollbar, list_box->selected_index); + } + return 1; +} + +static int get_button_id_from_position(const list_box_type *list_box, int x, int y) +{ + int padding = list_box->draw_inner_panel ? BLOCK_SIZE / 2 : 0; + int width_blocks = get_actual_width_blocks(list_box); + if (x < list_box->x + padding || x > list_box->x + width_blocks * BLOCK_SIZE - padding || y < list_box->y) { + return LIST_BOX_NO_SELECTION; + } + int button_id = (y - padding / 2 - list_box->y) / list_box->item_height; + if (button_id < 0 || button_id >= list_box->scrollbar.elements_in_view || + button_id + list_box->scrollbar.scroll_position >= list_box->total_items) { + return LIST_BOX_NO_SELECTION; + } + return button_id; +} + +int list_box_handle_input(list_box_type *list_box, const mouse *m, int in_dialog) +{ + scrollbar_type *scrollbar = &list_box->scrollbar; + + if (scrollbar_handle_mouse(scrollbar, m, in_dialog) || + handle_arrow_keys(list_box, scroll_for_menu(m))) { + list_box_request_refresh(list_box); + return 1; + } + int old_focus_button_id = list_box->focus_button_id; + list_box->focus_button_id = get_button_id_from_position(list_box, m->x, m->y); + + if (old_focus_button_id != list_box->focus_button_id) { + list_box_request_refresh(list_box); + } + + if (!m->left.went_up || list_box->focus_button_id == LIST_BOX_NO_SELECTION) { + return 0; + } + + if (list_box->selected_index != list_box->focus_button_id + scrollbar->scroll_position) { + list_box->selected_index = list_box->focus_button_id + scrollbar->scroll_position; + list_box_request_refresh(list_box); + } + if (list_box->on_select) { + list_box->on_select(list_box->selected_index, m->left.double_click); + } + + return 1; +} + +void list_box_handle_tooltip(const list_box_type *list_box, tooltip_context *c) +{ + if (list_box->focus_button_id == LIST_BOX_NO_SELECTION || !list_box->handle_tooltip) { + return; + } + int padding = list_box->draw_inner_panel ? BLOCK_SIZE / 2 : 0; + list_box_item item = { + .x = list_box->x + padding, + .y = list_box->y + padding, + .width = get_actual_width_blocks(list_box) * BLOCK_SIZE - padding * 2, + .height = list_box->item_height, + .index = list_box->focus_button_id + list_box->scrollbar.scroll_position, + .button_position = list_box->focus_button_id, + .is_selected = item.index == list_box->selected_index, + .is_focused = 1 + }; + list_box->handle_tooltip(&item, c); +} diff --git a/src/graphics/list_box.h b/src/graphics/list_box.h new file mode 100644 index 0000000000..3c208a6ac6 --- /dev/null +++ b/src/graphics/list_box.h @@ -0,0 +1,55 @@ +#ifndef LIST_BOX_H +#define LIST_BOX_H + +#include "graphics/scrollbar.h" +#include "graphics/tooltip.h" +#include "input/mouse.h" + +#define LIST_BOX_NO_SELECTION -1 + +typedef struct { + int x; + int y; + int width; + int height; + int index; + int button_position; + int is_selected; + int is_focused; +} list_box_item; + +typedef struct { + int x; + int y; + int width_blocks; + int height_blocks; + int item_height; + int draw_inner_panel; + int extend_to_hidden_scrollbar; + int decorate_scrollbar; + void (*draw_item)(const list_box_item *item); + void (*on_select)(int index, int is_double_click); + void (*handle_tooltip)(const list_box_item *item, tooltip_context *c); + + /* Private elements */ + int total_items; + int selected_index; + scrollbar_type scrollbar; + int focus_button_id; + int refresh_requested; +} list_box_type; + +void list_box_init(list_box_type *list_box, int total_items); +void list_box_update_total_items(list_box_type *list_box, int total_items); +int list_box_get_total_items(const list_box_type *list_box); +void list_box_draw(list_box_type *list_box); +int list_box_handle_input(list_box_type *list_box, const mouse *m, int in_dialog); +void list_box_handle_tooltip(const list_box_type *list_box, tooltip_context *c); +void list_box_show_index(list_box_type *list_box, int index); +void list_box_show_selected_index(list_box_type *list_box); +void list_box_select_index(list_box_type *list_box, int index); +int list_box_get_selected_index(const list_box_type *list_box); +void list_box_request_refresh(list_box_type *list_box); +int list_box_get_scroll_position(const list_box_type *list_box); + +#endif // LIST_BOX_H diff --git a/src/window/asset_previewer.c b/src/window/asset_previewer.c index 9d2a8636aa..b5a863f30f 100644 --- a/src/window/asset_previewer.c +++ b/src/window/asset_previewer.c @@ -19,10 +19,10 @@ #include "graphics/graphics.h" #include "graphics/image.h" #include "graphics/lang_text.h" +#include "graphics/list_box.h" #include "graphics/panel.h" #include "graphics/renderer.h" #include "graphics/screen.h" -#include "graphics/scrollbar.h" #include "graphics/text.h" #include "graphics/window.h" #include "input/scroll.h" @@ -61,14 +61,12 @@ typedef enum { #define REFRESHED_INFO_TIME_MS 5000 -static void button_asset_entry(int index, int param2); +static void draw_asset_entry(const list_box_item *item); +static void select_asset(int index, int unused); +static void handle_tooltip(const list_box_item *item, tooltip_context *c); static void button_top(int option, int param2); static void button_toggle_animation_frames(int param1, int param2); -static scrollbar_type scrollbar = { - 8 + 15 * BLOCK_SIZE, 12 * BLOCK_SIZE, 0, 14 * BLOCK_SIZE, 13 * BLOCK_SIZE, window_invalidate, 0, 4 -}; - static generic_button buttons[NUM_BUTTONS] = { { 0, 25, 180, 20, button_top, button_none, BUTTON_CHANGE_ASSET_GROUP }, { 200, 25, 140, 20, button_top, button_none, BUTTON_CHANGE_TERRAIN }, @@ -78,7 +76,9 @@ static generic_button buttons[NUM_BUTTONS] = { { 530, 5, 80, 40, button_top, button_none, BUTTON_QUIT }, }; -static generic_button *asset_buttons; +static generic_button toggle_animation_button = { + 0, 0, 0, 20, button_toggle_animation_frames, button_none, 0, 0 +}; static const int ZOOM_VALUES[] = { 50, 100, 200, 400 }; #define TOTAL_ZOOM_VALUES (sizeof(ZOOM_VALUES) / sizeof(int)) @@ -88,6 +88,17 @@ typedef struct { int is_animation_frame; } asset_entry; +static list_box_type list_box = { + .width_blocks = 15, + .item_height = ASSET_BUTTON_SIZE, + .draw_inner_panel = 1, + .extend_to_hidden_scrollbar = 1, + .decorate_scrollbar = 1, + .draw_item = draw_asset_entry, + .on_select = select_asset, + .handle_tooltip = handle_tooltip +}; + static struct { const dir_listing *xml_files; const uint8_t **xml_file_names; @@ -101,11 +112,9 @@ static struct { uint8_t *encoded_asset_id; int encoded_asset_id_size; int focus_button_id; - int asset_button_id; + int animation_button_focused; int x_offset_top; asset_entry *entries; - int total_entries; - int selected_index; char *selected_asset_id; int hide_animation_frames; int scale; @@ -118,19 +127,19 @@ static struct { int showing_refresh_info; } data; -static void update_entries(void) +static int update_entries(void) { - data.total_entries = 0; + int total_entries = 0; free(data.entries); data.entries = 0; if (!data.active_group) { - return; + return 0; } int total_images = data.active_group->last_image_index - data.active_group->first_image_index + 1; data.entries = malloc(sizeof(asset_entry) * total_images); if (!data.entries) { log_error("Not enough memory", 0, 0); - return; + return 0; } memset(data.entries, 0, sizeof(asset_entry) * total_images); int current_asset = 0; @@ -147,19 +156,13 @@ static void update_entries(void) animation_sprites = img->animation->num_sprites; } } - data.total_entries++; + total_entries++; } + return total_entries; } -static void select_index(int index) +static void select_asset(int index, int unused) { - if (data.total_entries == 0) { - data.selected_index = 0; - free(data.selected_asset_id); - data.selected_asset_id = 0; - return; - } - data.selected_index = index; const asset_image *img = asset_image_get_from_id(data.active_group->first_image_index + data.entries[index].index); free(data.selected_asset_id); data.selected_asset_id = 0; @@ -170,57 +173,12 @@ static void select_index(int index) strncpy(data.selected_asset_id, img->id, id_length); } } -} - -static void scroll_to_index(int index, int force) -{ - if (index < scrollbar.scroll_position || force) { - scrollbar.scroll_position = index; - } else if (index >= scrollbar.scroll_position + scrollbar.elements_in_view) { - scrollbar.scroll_position = index - scrollbar.elements_in_view + 1; - } - if (scrollbar.scroll_position > scrollbar.max_scroll_position) { - scrollbar.scroll_position = scrollbar.max_scroll_position; - } -} - -static void set_max_asset_buttons(void) -{ - int asset_list_height = (screen_height() - 16 * BLOCK_SIZE) - screen_height() % BLOCK_SIZE; - int max_asset_buttons = asset_list_height / ASSET_BUTTON_SIZE; - if (scrollbar.elements_in_view == max_asset_buttons) { - return; - } - free(asset_buttons); - asset_buttons = malloc((max_asset_buttons + 1) * sizeof(generic_button)); - if (!asset_buttons) { - return; + if (data.animation.enabled) { + game_animation_init(); + data.animation.frame = 1; + data.animation.reversed = 0; } - for (int i = 0; i < max_asset_buttons; i++) { - generic_button *btn = &asset_buttons[i]; - btn->x = 0; - btn->y = i * ASSET_BUTTON_SIZE; - btn->width = 13 * BLOCK_SIZE; - btn->height = ASSET_BUTTON_SIZE; - btn->left_click_handler = button_asset_entry; - btn->right_click_handler = button_none; - btn->parameter1 = i; - btn->parameter2 = 0; - } - generic_button *last_btn = &asset_buttons[max_asset_buttons]; - last_btn->x = 0; - last_btn->y = asset_list_height + 8; - last_btn->width = 13 * BLOCK_SIZE; - last_btn->height = 20; - last_btn->left_click_handler = button_toggle_animation_frames; - last_btn->right_click_handler = button_none; - last_btn->parameter1 = 0; - last_btn->parameter2 = 0; - - scrollbar.height = asset_list_height; - scrollbar.elements_in_view = max_asset_buttons; - scrollbar_update_total_elements(&scrollbar, data.total_entries); - scroll_to_index(scrollbar.scroll_position, 0); + window_invalidate(); } static void load_assets(int changed) @@ -228,12 +186,11 @@ static void load_assets(int changed) assets_load_single_group(data.xml_files->files[data.active_group_index].name, data.main_atlas->buffers, data.main_atlas->image_widths); data.active_group = group_get_current(); - update_entries(); + int total_entries = update_entries(); + list_box_update_total_items(&list_box, total_entries); if (changed) { - select_index(0); - set_max_asset_buttons(); + list_box_select_index(&list_box, 0); } - scrollbar_init(&scrollbar, 0, data.total_entries); } static int load_climate(int force) @@ -334,6 +291,7 @@ static int init(void) create_selection_lists(); random_generate_pool(); sound_music_play_editor(); + list_box_init(&list_box, 0); return load_climate(1); } @@ -359,7 +317,7 @@ static void draw_terrain_background(void) static inline int get_current_asset_index(void) { - return data.entries[data.selected_index].index + data.active_group->first_image_index; + return data.entries[list_box_get_selected_index(&list_box)].index + data.active_group->first_image_index; } static void draw_asset(void) @@ -416,12 +374,11 @@ static void draw_background(void) draw_terrain_background(); } - if (data.total_entries > 0) { + if (list_box_get_total_items(&list_box) > 0) { draw_asset(); } data.x_offset_top = (screen_width() - 39 * BLOCK_SIZE) / 2; - set_max_asset_buttons(); outer_panel_draw(data.x_offset_top, 8, 40, 7); lang_text_draw_centered(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_TITLE, @@ -458,6 +415,24 @@ static void draw_background(void) draw_refreshed_info(); } + int outer_height_blocks = (screen_height() - 11 * BLOCK_SIZE) / BLOCK_SIZE; + + outer_panel_draw(8, 10 * BLOCK_SIZE, list_box.width_blocks + 3, outer_height_blocks); + lang_text_draw(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_ASSET, + 32, 11 * BLOCK_SIZE, FONT_NORMAL_BLACK); + list_box.height_blocks = outer_height_blocks - 4; + list_box.x = 24; + list_box.y = 12 * BLOCK_SIZE; + list_box_request_refresh(&list_box); + toggle_animation_button.x = list_box.x + 2; + toggle_animation_button.y = list_box.y + list_box.height_blocks * BLOCK_SIZE; + toggle_animation_button.width = list_box.width_blocks * BLOCK_SIZE; + if (!data.hide_animation_frames) { + text_draw(string_from_ascii("x"), toggle_animation_button.x + 6, toggle_animation_button.y + 3, + FONT_NORMAL_BLACK, COLOR_MASK_NONE); + } + lang_text_draw(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_SHOW_ANIMATION_FRAMES, + toggle_animation_button.x + 28, toggle_animation_button.y + 4, FONT_NORMAL_BLACK); } static void encode_asset_id(const char *id) @@ -475,62 +450,40 @@ static void encode_asset_id(const char *id) encoding_from_utf8(id, data.encoded_asset_id, size_needed); } -static void draw_asset_list(int x_offset, int y_offset) +static void draw_asset_entry(const list_box_item *item) { - int outer_width_blocks = scrollbar.elements_in_view < data.total_entries ? 18 : 16; - int outer_height_blocks = (screen_height() - y_offset - BLOCK_SIZE) / BLOCK_SIZE; - - outer_panel_draw(x_offset, y_offset, outer_width_blocks, outer_height_blocks); - lang_text_draw(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_ASSET, - x_offset + 24, y_offset + 16, FONT_NORMAL_BLACK); - scrollbar_draw(&scrollbar); - inner_panel_draw(x_offset + 16, y_offset + 32, 14, outer_height_blocks - 4); - x_offset += 20; - y_offset += 24; - if (!data.active_group || !data.total_entries) { - lang_text_draw_centered(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_NO_ASSETS, x_offset - 4, y_offset + 8 + - ((outer_height_blocks - 3) * BLOCK_SIZE - 20) / 2, 14 * BLOCK_SIZE, FONT_NORMAL_GREEN); - } else { - int images_in_list = data.total_entries; - if (images_in_list > scrollbar.elements_in_view) { - images_in_list = scrollbar.elements_in_view; - } - for (int i = 0; i < images_in_list; i++) { - y_offset += ASSET_BUTTON_SIZE; - int current_index = i + scrollbar.scroll_position; - int current_image = data.entries[current_index].index; - font_t font = current_index == data.selected_index ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; - if (i == (data.asset_button_id - 1)) { - button_border_draw(x_offset + 3, y_offset - 5, 13 * BLOCK_SIZE, ASSET_BUTTON_SIZE + 2, 1); - } - const asset_image *img = asset_image_get_from_id(data.active_group->first_image_index + current_image); - int width = text_draw_number(current_image + 1, '@', "", x_offset, y_offset, font, COLOR_MASK_NONE); - const uint8_t *asset_name; - if (img->id) { - encode_asset_id(img->id); - if (data.encoded_asset_id) { - asset_name = data.encoded_asset_id; - } else { - asset_name = lang_get_string(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_UNNAMED_ASSET); - } - } else { - asset_name = lang_get_string(CUSTOM_TRANSLATION, data.entries[current_index].is_animation_frame ? - TR_WINDOW_ASSET_PREVIEWER_ANIMATION_FRAME : TR_WINDOW_ASSET_PREVIEWER_UNNAMED_ASSET); - } - width += text_draw(string_from_ascii("-"), x_offset + width, y_offset, font, COLOR_MASK_NONE); - text_draw_ellipsized(asset_name, x_offset + width, y_offset, 13 * BLOCK_SIZE - width, - font, COLOR_MASK_NONE); + int current_image = data.entries[item->index].index; + const asset_image *img = asset_image_get_from_id(data.active_group->first_image_index + current_image); + font_t font = item->is_selected ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; + int width = text_draw_number(current_image + 1, '@', "", item->x, item->y, font, COLOR_MASK_NONE); + const uint8_t *asset_name; + if (img->id) { + encode_asset_id(img->id); + if (data.encoded_asset_id) { + asset_name = data.encoded_asset_id; + } else { + asset_name = lang_get_string(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_UNNAMED_ASSET); } + } else { + asset_name = lang_get_string(CUSTOM_TRANSLATION, data.entries[item->index].is_animation_frame ? + TR_WINDOW_ASSET_PREVIEWER_ANIMATION_FRAME : TR_WINDOW_ASSET_PREVIEWER_UNNAMED_ASSET); } - const generic_button *last_btn = &asset_buttons[scrollbar.elements_in_view]; - y_offset = 10 * BLOCK_SIZE + 42; - button_border_draw(x_offset, y_offset + last_btn->y, 20, 20, - data.asset_button_id == scrollbar.elements_in_view + 1); - if (!data.hide_animation_frames) { - text_draw(string_from_ascii("x"), x_offset + 6, y_offset + last_btn->y + 3, FONT_NORMAL_BLACK, COLOR_MASK_NONE); + width += text_draw(string_from_ascii("-"), item->x + width, item->y, font, COLOR_MASK_NONE); + text_draw_ellipsized(asset_name, item->x + width, item->y, item->width - width, font, COLOR_MASK_NONE); + if (item->is_focused) { + button_border_draw(item->x + 3, item->y - 5, item->width, item->height + 2, 1); } - lang_text_draw(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_SHOW_ANIMATION_FRAMES, - x_offset + 28, y_offset + last_btn->y + 4, FONT_NORMAL_BLACK); +} + +static void draw_asset_list(void) +{ + list_box_draw(&list_box); + if (!data.active_group || !list_box_get_total_items(&list_box)) { + lang_text_draw_centered(CUSTOM_TRANSLATION, TR_WINDOW_ASSET_PREVIEWER_NO_ASSETS, list_box.x, list_box.y + + (list_box.height_blocks * BLOCK_SIZE - 20) / 2, list_box.width_blocks * BLOCK_SIZE, FONT_NORMAL_GREEN); + } + button_border_draw(toggle_animation_button.x, toggle_animation_button.y, + 20, toggle_animation_button.height, data.animation_button_focused); } static void advance_animation_frame(const image *img) @@ -584,7 +537,7 @@ static void draw_foreground(void) button_border_draw(x_offset, btn->y + 60, width, btn->height, is_focused); } - draw_asset_list(8, 10 * BLOCK_SIZE); + draw_asset_list(); if (data.showing_refresh_info && time_get_millis() - data.last_refresh > REFRESHED_INFO_TIME_MS) { data.showing_refresh_info = 0; @@ -596,7 +549,7 @@ static void draw_foreground(void) window_invalidate(); } - if (data.total_entries == 0) { + if (list_box_get_total_items(&list_box) == 0) { return; } @@ -610,61 +563,25 @@ static void draw_foreground(void) } } -static void handle_arrow_keys(int direction) -{ - int delta; - switch (direction) { - case DIR_0_TOP: - case DIR_1_TOP_RIGHT: - case DIR_7_TOP_LEFT: - delta = -1; - break; - case DIR_4_BOTTOM: - case DIR_3_BOTTOM_RIGHT: - case DIR_5_BOTTOM_LEFT: - delta = 1; - break; - default: - return; - } - int new_index = data.selected_index + delta; - if (new_index < 0 || new_index >= data.total_entries) { - return; - } - select_index(new_index); - scroll_to_index(data.selected_index, 0); - window_invalidate(); -} - static void handle_input(const mouse *m, const hotkeys *h) { if (h->f5_pressed) { button_top(BUTTON_REFRESH, 0); } - handle_arrow_keys(scroll_for_menu(m)); - if (scrollbar_handle_mouse(&scrollbar, m, 0)) { + if (list_box_handle_input(&list_box, m, 0)) { return; } if (!generic_buttons_handle_mouse(m, data.x_offset_top + 16, 60, buttons, NUM_BUTTONS, &data.focus_button_id)) { - generic_buttons_handle_mouse(m, 28, 10 * BLOCK_SIZE + 42, asset_buttons, - scrollbar.elements_in_view + 1, &data.asset_button_id); + generic_buttons_handle_mouse(m, 0, 0, &toggle_animation_button, 1, &data.animation_button_focused); } } -static void get_tooltip(tooltip_context *c) +static void handle_tooltip(const list_box_item *item, tooltip_context *c) { - if (!data.asset_button_id) { - return; - } - - int current_index = data.asset_button_id - 1 + scrollbar.scroll_position; - if (current_index >= data.total_entries) { - return; - } - font_t font = (current_index == data.selected_index - 1) ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; + font_t font = item->is_selected ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; const asset_image *img = - asset_image_get_from_id(data.active_group->first_image_index + data.entries[current_index].index); + asset_image_get_from_id(data.active_group->first_image_index + data.entries[item->index].index); if (!img || !img->id) { return; } @@ -673,15 +590,20 @@ static void get_tooltip(tooltip_context *c) return; } int text_width = text_get_width(data.encoded_asset_id, font); - int base_width = text_get_number_width(current_index + 1, '@', "", font); + int base_width = text_get_number_width(data.entries[item->index].index + 1, '@', "", font); base_width += text_get_width(string_from_ascii(" - "), font); - if (text_width > 13 * BLOCK_SIZE - base_width) { + if (text_width > item->width - base_width) { c->precomposed_text = data.encoded_asset_id; c->type = TOOLTIP_BUTTON; } } +static void get_tooltip(tooltip_context *c) +{ + list_box_handle_tooltip(&list_box, c); +} + static void change_asset_group(int group) { if (data.active_group_index != group) { @@ -710,24 +632,6 @@ static void set_zoom(int value) } } -static void button_asset_entry(int index, int param2) -{ - if (!data.active_group) { - return; - } - index += scrollbar.scroll_position; - if (index == data.selected_index || index >= data.total_entries) { - return; - } - select_index(index); - if (data.animation.enabled) { - game_animation_init(); - data.animation.frame = 1; - data.animation.reversed = 0; - } - window_invalidate(); -} - static void confirm_exit(int accepted, int checked) { if (accepted) { @@ -737,33 +641,36 @@ static void confirm_exit(int accepted, int checked) static void recalculate_selected_index(void) { - if (data.selected_asset_id) { - if (data.selected_index < data.total_entries) { + int selected_index = list_box_get_selected_index(&list_box); + int total_entries = list_box_get_total_items(&list_box); + if (selected_index != LIST_BOX_NO_SELECTION) { + if (selected_index < total_entries) { const asset_image *img = asset_image_get_from_id(get_current_asset_index()); if (img->id && strcmp(data.selected_asset_id, img->id) == 0) { return; } } - for (int i = 0; i < data.total_entries; i++) { + for (int i = 0; i < total_entries; i++) { const asset_image *img = asset_image_get_from_id(data.active_group->first_image_index + data.entries[i].index); - if (img->id && strcmp(data.selected_asset_id, img->id) == 0) { - select_index(i); + if (img->id && data.selected_asset_id && strcmp(data.selected_asset_id, img->id) == 0) { + list_box_select_index(&list_box, i); return; } } } - if (data.selected_index >= data.total_entries) { - select_index(data.total_entries - 1); + if (selected_index >= total_entries) { + list_box_select_index(&list_box, total_entries - 1); } else { - select_index(data.selected_index); + list_box_select_index(&list_box, selected_index); } } static void refresh_window(void) { - int asset_index = data.total_entries > 0 ? data.entries[scrollbar.scroll_position].index : 0; + int asset_index = list_box_get_total_items(&list_box) > 0 ? + data.entries[list_box_get_scroll_position(&list_box)].index : 0; int group_changed = update_asset_groups_list(); load_assets(group_changed); if (group_changed) { @@ -773,13 +680,14 @@ static void refresh_window(void) window_invalidate(); data.last_refresh = time_get_millis(); data.showing_refresh_info = 0; - for (int i = 0; i < data.total_entries; i++) { + int total_entries = list_box_get_total_items(&list_box); + for (int i = 0; i < total_entries; i++) { if (data.entries[i].index == asset_index) { - scroll_to_index(i, 1); + list_box_show_index(&list_box, i); return; } } - scroll_to_index(data.total_entries, 1); + list_box_show_index(&list_box, total_entries); } static void button_top(int option, int param2) @@ -823,31 +731,32 @@ static void button_toggle_animation_frames(int param1, int param2) data.hide_animation_frames ^= 1; int asset_index = 0; int is_animation_frame = 0; - if (data.total_entries > 0) { - asset_index = data.entries[scrollbar.scroll_position].index; - is_animation_frame = data.entries[scrollbar.scroll_position].is_animation_frame; - if (data.entries[data.selected_index].is_animation_frame && data.hide_animation_frames) { - for (int i = data.selected_index - 1; i >= 0; i--) { + if (list_box_get_total_items(&list_box) > 0) { + asset_index = data.entries[list_box_get_scroll_position(&list_box)].index; + is_animation_frame = data.entries[list_box_get_scroll_position(&list_box)].is_animation_frame; + if (data.entries[list_box_get_selected_index(&list_box)].is_animation_frame && data.hide_animation_frames) { + for (int i = list_box_get_selected_index(&list_box) - 1; i >= 0; i--) { if (!data.entries[i].is_animation_frame) { - select_index(i); + list_box_select_index(&list_box, i); break; } } } } - update_entries(); - scrollbar_update_total_elements(&scrollbar, data.total_entries); + int total_entries = update_entries(); + list_box_update_total_items(&list_box, total_entries); recalculate_selected_index(); window_invalidate(); - for (int i = 0; i < data.total_entries; i++) { + for (int i = 0; i < list_box_get_total_items(&list_box); i++) { if (data.entries[i].index == asset_index || (is_animation_frame && data.hide_animation_frames && data.entries[i].index > asset_index)) { - scroll_to_index(i, 1); + list_box_show_index(&list_box, i); return; } } - scroll_to_index(data.total_entries, 1); + + list_box_show_index(&list_box, 0); } int window_asset_previewer_show(void) diff --git a/src/window/cck_selection.c b/src/window/cck_selection.c index f83fea8b28..c461392271 100644 --- a/src/window/cck_selection.c +++ b/src/window/cck_selection.c @@ -12,9 +12,9 @@ #include "graphics/image.h" #include "graphics/image_button.h" #include "graphics/lang_text.h" +#include "graphics/list_box.h" #include "graphics/panel.h" #include "graphics/screen.h" -#include "graphics/scrollbar.h" #include "graphics/text.h" #include "graphics/window.h" #include "input/input.h" @@ -31,11 +31,11 @@ #define BACKGROUND_WIDTH 1024 #define BACKGROUND_HEIGHT 768 -static void button_select_item(int index, int param2); +static void select_scenario(int index, int is_double_click); static void button_start_scenario(int param1, int param2); static void button_back(int param1, int param2); static void button_toggle_minimap(int param1, int param2); -static void on_scroll(void); +static void draw_scenario_item(const list_box_item *item); static image_button start_button = { 600, 440, 27, 27, IB_NORMAL, GROUP_SIDEBAR_BUTTONS, 56, button_start_scenario, button_none, 1, 0, 1 }; @@ -45,30 +45,21 @@ static image_button back_button = static generic_button toggle_minimap_button = { 570, 87, 39, 28, button_toggle_minimap, button_none, 0, 0 }; -static generic_button file_buttons[] = { - {18, 220, 252, 16, button_select_item, button_none, 0, 0}, - {18, 236, 252, 16, button_select_item, button_none, 1, 0}, - {18, 252, 252, 16, button_select_item, button_none, 2, 0}, - {18, 268, 252, 16, button_select_item, button_none, 3, 0}, - {18, 284, 252, 16, button_select_item, button_none, 4, 0}, - {18, 300, 252, 16, button_select_item, button_none, 5, 0}, - {18, 316, 252, 16, button_select_item, button_none, 6, 0}, - {18, 332, 252, 16, button_select_item, button_none, 7, 0}, - {18, 348, 252, 16, button_select_item, button_none, 8, 0}, - {18, 364, 252, 16, button_select_item, button_none, 9, 0}, - {18, 380, 252, 16, button_select_item, button_none, 10, 0}, - {18, 396, 252, 16, button_select_item, button_none, 11, 0}, - {18, 412, 252, 16, button_select_item, button_none, 12, 0}, - {18, 428, 252, 16, button_select_item, button_none, 13, 0}, - {18, 444, 252, 16, button_select_item, button_none, 14, 0}, +static list_box_type list_box = { + .x = 16, + .y = 210, + .width_blocks = 18, + .height_blocks = 16, + .item_height = 16, + .draw_inner_panel = 1, + .extend_to_hidden_scrollbar = 1, + .decorate_scrollbar = 1, + .draw_item = draw_scenario_item, + .on_select = select_scenario }; -static scrollbar_type scrollbar = {276, 210, 256, 260, MAX_SCENARIOS, on_scroll, 1, 8, 1}; - static struct { - int focus_button_id; int focus_toggle_button; - int selected_item; int show_minimap; char selected_scenario_filename[FILE_NAME_MAX]; uint8_t selected_scenario_display[FILE_NAME_MAX]; @@ -81,30 +72,24 @@ static void init(void) { data.scenarios = dir_find_files_with_extension(".", "map"); data.scenarios = dir_append_files_with_extension("mapx"); - data.focus_button_id = 0; data.focus_toggle_button = 0; data.show_minimap = 0; - button_select_item(0, 0); - scrollbar_init(&scrollbar, 0, data.scenarios->num_files); + list_box_init(&list_box, data.scenarios->num_files); + list_box_select_index(&list_box, 0); } -static void draw_scenario_list(void) +static void draw_scenario_item(const list_box_item *item) { - inner_panel_draw(16, 210, 16, 16); char file[FILE_NAME_MAX]; uint8_t displayable_file[FILE_NAME_MAX]; - for (int i = 0; i < MAX_SCENARIOS; i++) { - font_t font = FONT_NORMAL_GREEN; - if (data.focus_button_id == i + 1) { - font = FONT_NORMAL_WHITE; - } else if (!data.focus_button_id && data.selected_item == i + scrollbar.scroll_position) { - font = FONT_NORMAL_WHITE; - } - strcpy(file, data.scenarios->files[i + scrollbar.scroll_position].name); - encoding_from_utf8(file, displayable_file, FILE_NAME_MAX); - file_remove_extension((char *) displayable_file); - text_ellipsize(displayable_file, font, 240); - text_draw(displayable_file, 24, 220 + 16 * i, font, 0); + font_t font = item->is_selected ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; + strcpy(file, data.scenarios->files[item->index].name); + encoding_from_utf8(file, displayable_file, FILE_NAME_MAX); + file_remove_extension((char *) displayable_file); + text_ellipsize(displayable_file, font, item->width); + text_draw(displayable_file, item->x, item->y, font, 0); + if (item->is_focused) { + button_border_draw(item->x - 4, item->y - 4, item->width + 6, item->height + 4, 1); } } @@ -226,8 +211,7 @@ static void draw_background(void) image_draw(image_group(GROUP_CCK_BACKGROUND), (WINDOW_WIDTH - BACKGROUND_WIDTH) / 2, (WINDOW_HEIGHT - BACKGROUND_HEIGHT) / 2, COLOR_MASK_NONE, SCALE_NONE); graphics_reset_clip_rectangle(); - inner_panel_draw(280, 242, 2, 12); - draw_scenario_list(); + list_box_request_refresh(&list_box); draw_scenario_info(); graphics_reset_dialog(); } @@ -241,8 +225,7 @@ static void draw_foreground(void) toggle_minimap_button.x, toggle_minimap_button.y, toggle_minimap_button.width, toggle_minimap_button.height, data.focus_toggle_button); - scrollbar_draw(&scrollbar); - draw_scenario_list(); + list_box_draw(&list_box); graphics_reset_dialog(); } @@ -250,12 +233,10 @@ static void handle_input(const mouse *m, const hotkeys *h) { const mouse *m_dialog = mouse_in_dialog(m); data.focus_toggle_button = 0; - data.focus_button_id = 0; - if (scrollbar_handle_mouse(&scrollbar, m_dialog, 1) || - image_buttons_handle_mouse(m_dialog, 0, 0, &start_button, 1, 0) || + if (image_buttons_handle_mouse(m_dialog, 0, 0, &start_button, 1, 0) || image_buttons_handle_mouse(m_dialog, 0, 0, &back_button, 1, 0) || generic_buttons_handle_mouse(m_dialog, 0, 0, &toggle_minimap_button, 1, &data.focus_toggle_button) || - generic_buttons_handle_mouse(m_dialog, 0, 0, file_buttons, MAX_SCENARIOS, &data.focus_button_id)) { + list_box_handle_input(&list_box, m_dialog, 1)) { return; } if (h->enter_pressed) { @@ -272,17 +253,17 @@ static void button_back(int param1, int param2) window_go_back(); } -static void button_select_item(int index, int param2) +static void select_scenario(int index, int is_double_click) { - if (index >= data.scenarios->num_files) { - return; + if (strcmp(data.selected_scenario_filename, data.scenarios->files[index].name) != 0) { + strcpy(data.selected_scenario_filename, data.scenarios->files[index].name); + game_file_io_read_scenario_info(data.selected_scenario_filename, &data.info); + encoding_from_utf8(data.selected_scenario_filename, data.selected_scenario_display, FILE_NAME_MAX); + file_remove_extension((char *) data.selected_scenario_display); + window_invalidate(); + } else if (is_double_click) { + button_start_scenario(0, 0); } - data.selected_item = scrollbar.scroll_position + index; - strcpy(data.selected_scenario_filename, data.scenarios->files[data.selected_item].name); - game_file_io_read_scenario_info(data.selected_scenario_filename, &data.info); - encoding_from_utf8(data.selected_scenario_filename, data.selected_scenario_display, FILE_NAME_MAX); - file_remove_extension((char *) data.selected_scenario_display); - window_invalidate(); } static void button_start_scenario(int param1, int param2) diff --git a/src/window/file_dialog.c b/src/window/file_dialog.c index 3148494560..a0b2c71c4e 100644 --- a/src/window/file_dialog.c +++ b/src/window/file_dialog.c @@ -20,12 +20,11 @@ #include "graphics/image.h" #include "graphics/image_button.h" #include "graphics/lang_text.h" +#include "graphics/list_box.h" #include "graphics/panel.h" -#include "graphics/scrollbar.h" #include "graphics/text.h" #include "graphics/window.h" #include "input/input.h" -#include "input/scroll.h" #include "platform/file_manager.h" #include "scenario/editor.h" #include "scenario/custom_messages_export_xml.h" @@ -53,45 +52,20 @@ static const time_millis NOT_EXIST_MESSAGE_TIMEOUT = 500; static void button_toggle_sort_type(int param1, int param2); static void button_ok_cancel(int is_ok, int param2); -static void button_select_file(int index, int param2); -static void on_scroll(void); static void input_box_changed(int is_addition_at_end); +static void draw_file(const list_box_item *item); +static void select_file(int index, int is_double_click); +static void file_tooltip(const list_box_item *item, tooltip_context *c); static image_button image_buttons[] = { {536, 440, 39, 26, IB_NORMAL, GROUP_OK_CANCEL_SCROLL_BUTTONS, 0, button_ok_cancel, button_none, 1, 0, 1}, {584, 440, 39, 26, IB_NORMAL, GROUP_OK_CANCEL_SCROLL_BUTTONS, 4, button_ok_cancel, button_none, 0, 0, 1}, }; -static generic_button file_buttons[] = { - {32, 88 + 16 * 0, 256, 16, button_select_file, button_none, 0, 0}, - {32, 88 + 16 * 1, 256, 16, button_select_file, button_none, 1, 0}, - {32, 88 + 16 * 2, 256, 16, button_select_file, button_none, 2, 0}, - {32, 88 + 16 * 3, 256, 16, button_select_file, button_none, 3, 0}, - {32, 88 + 16 * 4, 256, 16, button_select_file, button_none, 4, 0}, - {32, 88 + 16 * 5, 256, 16, button_select_file, button_none, 5, 0}, - {32, 88 + 16 * 6, 256, 16, button_select_file, button_none, 6, 0}, - {32, 88 + 16 * 7, 256, 16, button_select_file, button_none, 7, 0}, - {32, 88 + 16 * 8, 256, 16, button_select_file, button_none, 8, 0}, - {32, 88 + 16 * 9, 256, 16, button_select_file, button_none, 9, 0}, - {32, 88 + 16 * 10, 256, 16, button_select_file, button_none, 10, 0}, - {32, 88 + 16 * 11, 256, 16, button_select_file, button_none, 11, 0}, - {32, 88 + 16 * 12, 256, 16, button_select_file, button_none, 12, 0}, - {32, 88 + 16 * 13, 256, 16, button_select_file, button_none, 13, 0}, - {32, 88 + 16 * 14, 256, 16, button_select_file, button_none, 14, 0}, - {32, 88 + 16 * 15, 256, 16, button_select_file, button_none, 15, 0}, - {32, 88 + 16 * 16, 256, 16, button_select_file, button_none, 16, 0}, - {32, 88 + 16 * 17, 256, 16, button_select_file, button_none, 17, 0}, - {32, 88 + 16 * 18, 256, 16, button_select_file, button_none, 18, 0}, - {32, 88 + 16 * 19, 256, 16, button_select_file, button_none, 19, 0}, - {32, 88 + 16 * 20, 256, 16, button_select_file, button_none, 20, 0}, -}; - static generic_button sort_by_button[] = { {16, 437, 288, 26, button_toggle_sort_type, button_none, 0, 0} }; -static scrollbar_type scrollbar = { 304, 80, 350, 256, NUM_FILES_IN_VIEW, on_scroll, 1 }; - typedef struct { const char *extension; const char *path; @@ -102,10 +76,8 @@ static struct { time_millis message_not_exist_start_time; file_type type; file_dialog_type dialog_type; - int focus_button_id; - int double_click; - const dir_listing *file_list; + const dir_listing *file_list; dir_listing filtered_file_list; uint8_t filter_text[FILTER_TEXT_SIZE]; enum { @@ -124,7 +96,6 @@ static struct { } info; savegame_load_status savegame_info_status; int redraw_full_window; - int selected_index; } data; static input_box main_input = { @@ -138,6 +109,20 @@ static input_box main_input = { .on_change = input_box_changed }; +static list_box_type list_box = { + .x = 16, + .y = 80, + .width_blocks = 20, + .height_blocks = 22, + .item_height = 16, + .decorate_scrollbar = 0, + .draw_inner_panel = 1, + .extend_to_hidden_scrollbar = 0, + .draw_item = draw_file, + .on_select = select_file, + .handle_tooltip = file_tooltip, +}; + static const int MISSION_ID_TO_CITY_ID[] = { 0, 3, 2, 1, 7, 10, 18, 4, 30, 6, 12, 14, 16, 27, 31, 23, 36, 38, 28, 25 }; @@ -210,11 +195,11 @@ static int select_correct_index(void) } for (int i = 0; i < data.filtered_file_list.num_files; i++) { if (strcmp(data.filtered_file_list.files[i].name, data.selected_file) == 0) { - data.selected_index = i + 1; + list_box_select_index(&list_box, i); return i; } } - data.selected_index = 0; + list_box_select_index(&list_box, LIST_BOX_NO_SELECTION); return 0; } @@ -235,8 +220,6 @@ static void init(file_type type, file_dialog_type dialog_type) data.dialog_type = dialog_type; data.message_not_exist_start_time = 0; - data.double_click = 0; - data.focus_button_id = 0; if (strlen(data.file_data->last_loaded_file) > 0) { strncpy(data.selected_file, data.file_data->last_loaded_file, FILE_NAME_MAX); @@ -276,7 +259,7 @@ static void init(file_type type, file_dialog_type dialog_type) data.file_list = dir_find_files_with_extension(data.file_data->path, data.file_data->extension); } init_filtered_file_list(); - scrollbar_init(&scrollbar, select_correct_index(), data.filtered_file_list.num_files); + list_box_init(&list_box, data.filtered_file_list.num_files); if (data.dialog_type == FILE_DIALOG_SAVE) { main_input.placeholder = 0; @@ -334,13 +317,25 @@ static void draw_background(void) data.redraw_full_window = 1; } +static void draw_file(const list_box_item *item) +{ + uint8_t file[FILE_NAME_MAX]; + font_t font = item->is_selected ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; + encoding_from_utf8(data.filtered_file_list.files[item->index].name, file, FILE_NAME_MAX); + text_ellipsize(file, font, item->width); + text_draw(file, item->x, item->y + 2, font, 0); + if (item->is_focused) { + button_border_draw(item->x - 4, item->y - 4, item->width + 6, item->height + 4, 1); + } +} + static void draw_foreground(void) { graphics_in_dialog(); if (data.redraw_full_window) { outer_panel_draw(0, 0, 40, 30); - inner_panel_draw(16, 80, 18, 22); + list_box_request_refresh(&list_box); // title if (data.message_not_exist_start_time @@ -426,22 +421,7 @@ static void draw_foreground(void) } data.redraw_full_window = 0; } - - // File list - uint8_t file[FILE_NAME_MAX]; - for (int i = 0; i < NUM_FILES_IN_VIEW && scrollbar.scroll_position + i < data.filtered_file_list.num_files; i++) { - font_t font = FONT_NORMAL_GREEN; - if (data.focus_button_id == i + 1) { - button_border_draw(24, 85 + 16 * i, MAX_FILE_WINDOW_TEXT_WIDTH + 8, 20, 1); - } - encoding_from_utf8(data.filtered_file_list.files[scrollbar.scroll_position + i].name, file, FILE_NAME_MAX); - if (data.selected_index == scrollbar.scroll_position + i + 1) { - font = FONT_NORMAL_WHITE; - } - text_ellipsize(file, font, MAX_FILE_WINDOW_TEXT_WIDTH); - text_draw(file, 32, 90 + 16 * i, font, 0); - } - + list_box_draw(&list_box); input_box_draw(&main_input); button_border_draw( sort_by_button[0].x, @@ -451,55 +431,11 @@ static void draw_foreground(void) data.sort_by_button_focused ); image_buttons_draw(0, 0, image_buttons, 2); - scrollbar_draw(&scrollbar); graphics_reset_dialog(); } -static int handle_arrow_keys(int direction) -{ - int delta; - switch (direction) { - case DIR_0_TOP: - case DIR_1_TOP_RIGHT: - case DIR_7_TOP_LEFT: - delta = -1; - break; - case DIR_4_BOTTOM: - case DIR_3_BOTTOM_RIGHT: - case DIR_5_BOTTOM_LEFT: - delta = 1; - break; - default: - return 0; - } - int new_index = 0; - if (data.selected_index == 0) { - if (delta == 1) { - new_index = 1; - } else { - new_index = data.filtered_file_list.num_files; - } - } else { - new_index = data.selected_index + delta; - if (new_index == 0) { - new_index = data.filtered_file_list.num_files; - } else if (new_index > data.filtered_file_list.num_files) { - new_index = 1; - } - } - button_select_file(new_index - scrollbar.scroll_position - 1, 0); - if (data.selected_index > scrollbar.scroll_position + NUM_FILES_IN_VIEW) { - scrollbar_reset(&scrollbar, data.selected_index - NUM_FILES_IN_VIEW); - } else if (data.selected_index <= scrollbar.scroll_position) { - scrollbar_reset(&scrollbar, data.selected_index - 1); - } - return 1; -} - static void handle_input(const mouse *m, const hotkeys *h) { - data.double_click = m->left.double_click; - if (input_box_is_accepted()) { button_ok_cancel(1, 0); return; @@ -513,14 +449,9 @@ static void handle_input(const mouse *m, const hotkeys *h) const mouse *m_dialog = mouse_in_dialog(m); - int focus_id = data.focus_button_id; - int scroll_position = scrollbar.scroll_position; - data.focus_button_id = 0; - if (scrollbar_handle_mouse(&scrollbar, m_dialog, 1) || - handle_arrow_keys(scroll_for_menu(m)) || - input_box_handle_mouse(m_dialog, &main_input) || + if (input_box_handle_mouse(m_dialog, &main_input) || + list_box_handle_input(&list_box, m_dialog, 1) || generic_buttons_handle_mouse(m_dialog, 0, 0, sort_by_button, 1, &data.sort_by_button_focused) || - generic_buttons_handle_mouse(m_dialog, 0, 0, file_buttons, NUM_FILES_IN_VIEW, &data.focus_button_id) || image_buttons_handle_mouse(m_dialog, 0, 0, image_buttons, 2, 0)) { data.redraw_full_window = 1; return; @@ -530,10 +461,6 @@ static void handle_input(const mouse *m, const hotkeys *h) window_go_back(); return; } - - if (focus_id != data.focus_button_id || scroll_position != scrollbar.scroll_position) { - data.redraw_full_window = 1; - } } static int further_filter_file_list(void) @@ -558,6 +485,7 @@ static int further_filter_file_list(void) } int result = data.filtered_file_list.num_files > current_index; data.filtered_file_list.num_files = current_index; + list_box_update_total_items(&list_box, data.filtered_file_list.num_files); return result; } @@ -597,9 +525,9 @@ static void input_box_changed(int is_addition_at_end) if (scroll_index >= 0 && platform_file_manager_compare_filename(data.selected_file, data.filtered_file_list.files[scroll_index].name) == 0) { - data.selected_index = scroll_index + 1; + list_box_select_index(&list_box, scroll_index); } else { - data.selected_index = 0; + list_box_select_index(&list_box, LIST_BOX_NO_SELECTION); } window_request_refresh(); } else { @@ -613,7 +541,7 @@ static void input_box_changed(int is_addition_at_end) scroll_index = select_correct_index(); data.redraw_full_window = 1; } - scrollbar_init(&scrollbar, scroll_index, data.filtered_file_list.num_files); + list_box_update_total_items(&list_box, data.filtered_file_list.num_files); } static const char *prepare_filename(void) @@ -746,25 +674,14 @@ static void button_ok_cancel(int is_ok, int param2) dir_append_files_with_extension(saved_game_data_expanded.extension); init_filtered_file_list(); - - if (data.selected_index > 0) { - if (data.selected_index > data.filtered_file_list.num_files) { - data.selected_index = data.filtered_file_list.num_files; - } - button_select_file(data.selected_index - scrollbar.scroll_position - 1, 0); - } - scrollbar_init(&scrollbar, select_correct_index(), data.filtered_file_list.num_files); + list_box_update_total_items(&list_box, data.filtered_file_list.num_files); + select_correct_index(); strncpy(data.file_data->last_loaded_file, data.selected_file, FILE_NAME_MAX); window_request_refresh(); } } } -static void on_scroll(void) -{ - data.message_not_exist_start_time = 0; -} - static void button_toggle_sort_type(int param1, int param2) { if (data.sort_type == SORT_BY_NAME) { @@ -773,23 +690,19 @@ static void button_toggle_sort_type(int param1, int param2) data.sort_type = SORT_BY_NAME; } sort_file_list(); - scrollbar_reset(&scrollbar, select_correct_index()); + select_correct_index(); + list_box_show_selected_index(&list_box); data.redraw_full_window = 1; } -static void button_select_file(int index, int param2) +static void select_file(int index, int is_double_click) { - int selection = index + scrollbar.scroll_position; - if (selection < 0 || selection >= data.filtered_file_list.num_files) { - if (data.double_click) { - data.double_click = 0; - } + if (index == LIST_BOX_NO_SELECTION) { return; } - if (strcmp(data.selected_file, data.filtered_file_list.files[selection].name) != 0) { - data.selected_index = selection + 1; + if (strcmp(data.selected_file, data.filtered_file_list.files[index].name) != 0) { data.message_not_exist_start_time = 0; - strncpy(data.selected_file, data.filtered_file_list.files[selection].name, FILE_NAME_MAX - 1); + strncpy(data.selected_file, data.filtered_file_list.files[index].name, FILE_NAME_MAX - 1); if (data.dialog_type == FILE_DIALOG_SAVE) { file_remove_extension(data.selected_file); } @@ -800,19 +713,36 @@ static void button_select_file(int index, int param2) } window_request_refresh(); } - if (data.dialog_type != FILE_DIALOG_DELETE && data.double_click) { - data.double_click = 0; + if (data.dialog_type != FILE_DIALOG_DELETE && is_double_click) { button_ok_cancel(1, 0); } } +static void file_tooltip(const list_box_item *item, tooltip_context *c) +{ + static uint8_t file[FILE_NAME_MAX]; + font_t font = item->is_selected ? FONT_NORMAL_WHITE : FONT_NORMAL_GREEN; + encoding_from_utf8(data.filtered_file_list.files[item->index].name, file, FILE_NAME_MAX); + if (text_get_width(file, font) > item->width) { + c->precomposed_text = file; + c->type = TOOLTIP_BUTTON; + } +} + +static void handle_tooltip(tooltip_context *c) +{ + // TODO fix tooltips on large words + // list_box_handle_tooltip(&list_box, c); +} + void window_file_dialog_show(file_type type, file_dialog_type dialog_type) { window_type window = { WINDOW_FILE_DIALOG, draw_background, draw_foreground, - handle_input + handle_input, + handle_tooltip }; init(type, dialog_type); window_show(&window);