From 9f9143a5c672be43c3d8d6e56bd65c74db90aa9a Mon Sep 17 00:00:00 2001 From: Robert Jordan Date: Sat, 6 Jun 2015 17:04:53 -0400 Subject: [PATCH] textbox widget --- src/input.c | 14 ++++- src/interface/widget.c | 81 ++++++++++++++++++++++++++- src/interface/widget.h | 3 +- src/interface/window.c | 73 ++++++++++++++++++++++++ src/interface/window.h | 11 ++++ src/platform/shared.c | 8 ++- src/windows/editor_object_selection.c | 25 +++++++-- 7 files changed, 204 insertions(+), 11 deletions(-) diff --git a/src/input.c b/src/input.c index 8e0b4924f4..9d4ce11c6b 100644 --- a/src/input.c +++ b/src/input.c @@ -736,6 +736,12 @@ static void input_widget_left(int x, int y, rct_window *w, int widgetIndex) if (widgetIndex == -1) return; + if (windowClass != gCurrentTextBox.window.classification || + windowNumber != gCurrentTextBox.window.number || + widgetIndex != gCurrentTextBox.widget_index) { + window_cancel_textbox(); + } + widget = &w->widgets[widgetIndex]; switch (widget->type) { @@ -1173,8 +1179,10 @@ void game_handle_keyboard_input() // Reserve backtick for console if (key == SDL_SCANCODE_GRAVE) { - if (gConfigGeneral.debugging_tools || gConsoleOpen) + if (gConfigGeneral.debugging_tools || gConsoleOpen) { + window_cancel_textbox(); console_toggle(); + } continue; } else if (gConsoleOpen) { console_input(key); @@ -1195,7 +1203,7 @@ void game_handle_keyboard_input() if (w != NULL){ ((void(*)(int, rct_window*))w->event_handlers[WE_TEXT_INPUT])(key, w); } - else { + else if (!gUsingWidgetTextBox) { keyboard_shortcut_handle(key); } } @@ -1374,7 +1382,7 @@ void game_handle_key_scroll() rct_window *textWindow; textWindow = window_find_by_class(WC_TEXTINPUT); - if (textWindow) return; + if (textWindow || gUsingWidgetTextBox) return; scrollX = 0; scrollY = 0; diff --git a/src/interface/widget.c b/src/interface/widget.c index 34b2b412c7..d3df2d9e51 100644 --- a/src/interface/widget.c +++ b/src/interface/widget.c @@ -24,6 +24,8 @@ #include "../sprites.h" #include "widget.h" #include "window.h" +#include "../platform/platform.h" +#include "../localisation/localisation.h" static void widget_frame_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_resize_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); @@ -35,6 +37,7 @@ static void widget_text_unknown(rct_drawpixelinfo *dpi, rct_window *w, int widge static void widget_text(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_text_inset(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_text_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); +static void widget_text_box_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_groupbox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_caption_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); static void widget_checkbox_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex); @@ -160,6 +163,9 @@ void widget_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex) break; case WWT_25: break; + case WWT_TEXT_BOX: + widget_text_box_draw(dpi, w, widgetIndex); + break; } } @@ -1124,4 +1130,77 @@ void widget_set_checkbox_value(rct_window *w, int widgetIndex, int value) w->pressed_widgets |= (1ULL << widgetIndex); else w->pressed_widgets &= ~(1ULL << widgetIndex); -} \ No newline at end of file +} + +static void widget_text_box_draw(rct_drawpixelinfo *dpi, rct_window *w, int widgetIndex) +{ + rct_widget* widget; + int l, t, r, b; + uint8 colour; + int no_lines = 0; + int font_height = 0; + char wrapped_string[512]; + + // Get the widget + widget = &w->widgets[widgetIndex]; + + // Resolve the absolute ltrb + l = w->x + widget->left; + t = w->y + widget->top; + r = w->x + widget->right; + b = w->y + widget->bottom; + + // Get the colour + colour = w->colours[widget->colour]; + + + bool active = w->classification == gCurrentTextBox.window.classification && + w->number == gCurrentTextBox.window.number && + widgetIndex == gCurrentTextBox.widget_index; + + //gfx_fill_rect_inset(dpi, l, t, r, b, colour, 0x20 | (!active ? 0x40 : 0x00)); + gfx_fill_rect_inset(dpi, l, t, r, b, colour, 0x60); + + + if (!active) { + + if (w->widgets[widgetIndex].image != 0) { + strcpy(wrapped_string, (char*)w->widgets[widgetIndex].image); + gfx_wrap_string(wrapped_string, r - l - 5, &no_lines, &font_height); + gfx_draw_string(dpi, wrapped_string, w->colours[1], l + 2, t); + } + return; + } + + + strcpy(wrapped_string, gTextBoxInput); + + // String length needs to add 12 either side of box + // +13 for cursor when max length. + gfx_wrap_string(wrapped_string, r - l - 5 - 6, &no_lines, &font_height); + + + gfx_draw_string(dpi, wrapped_string, w->colours[1], l + 2, t); + + + int string_length = get_string_length(wrapped_string); + + // Make a copy of the string for measuring the width. + char temp_string[512] = { 0 }; + memcpy(temp_string, wrapped_string, min(string_length, gTextInputCursorPosition)); + int cur_x = l + gfx_get_string_width(temp_string) + 3; + + int width = 6; + if ((uint32)gTextInputCursorPosition < strlen(gTextBoxInput)){ + // Make a new 1 character wide string for measuring the width + // of the character that the cursor is under. + temp_string[1] = '\0'; + temp_string[0] = gTextBoxInput[gTextInputCursorPosition]; + width = max(gfx_get_string_width(temp_string) - 2, 4); + } + + if (gTextBoxFrameNo <= 15){ + uint8 colour = RCT2_ADDRESS(0x0141FC48, uint8)[w->colours[1] * 8]; + gfx_fill_rect(dpi, cur_x, t + 9, cur_x + width, t + 9, colour + 5); + } +} diff --git a/src/interface/widget.h b/src/interface/widget.h index 07ba54713a..323a82e385 100644 --- a/src/interface/widget.h +++ b/src/interface/widget.h @@ -50,7 +50,8 @@ typedef enum { WWT_CHECKBOX = 23, WWT_24, WWT_25, - WWT_LAST = 26, + WWT_TEXT_BOX = 26, + WWT_LAST = 27, } WINDOW_WIDGET_TYPES; #define WIDGETS_END WWT_LAST, 0, 0, 0, 0, 0, 0, 0 diff --git a/src/interface/window.c b/src/interface/window.c index 3a515c54dc..04d8c2df35 100644 --- a/src/interface/window.c +++ b/src/interface/window.c @@ -29,6 +29,8 @@ #include "widget.h" #include "window.h" #include "viewport.h" +#include "../localisation/string_ids.h" +#include "../localisation/localisation.h" #define RCT2_FIRST_WINDOW (RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window)) #define RCT2_LAST_WINDOW (RCT2_GLOBAL(RCT2_ADDRESS_NEW_WINDOW_PTR, rct_window*) - 1) @@ -39,6 +41,11 @@ rct_window* g_window_list = RCT2_ADDRESS(RCT2_ADDRESS_WINDOW_LIST, rct_window); uint8 TextInputDescriptionArgs[8]; +widget_identifier gCurrentTextBox = { { 255, 0 }, 0 }; +char gTextBoxInput[512] = { 0 }; +int gMaxTextBoxInputLength = 0; +int gTextBoxFrameNo = 0; +bool gUsingWidgetTextBox = 0; // converted from uint16 values at 0x009A41EC - 0x009A4230 // these are percentage coordinates of the viewport to center to, if a window is obscuring a location, the next is tried @@ -2177,4 +2184,70 @@ void textinput_cancel() window_event_textinput_call(w, RCT2_GLOBAL(RCT2_ADDRESS_TEXTINPUT_WIDGETINDEX, uint16), NULL); } } +} + +void window_start_textbox(rct_window *call_w, int call_widget, rct_string_id existing_text, uint32 existing_args, int maxLength) +{ + if (gUsingWidgetTextBox) + window_cancel_textbox(); + + gUsingWidgetTextBox = true; + gCurrentTextBox.window.classification = call_w->classification; + gCurrentTextBox.window.number = call_w->number; + gCurrentTextBox.widget_index = call_widget; + gTextBoxFrameNo = 0; + + gMaxTextBoxInputLength = maxLength; + + window_close_by_class(WC_TEXTINPUT); + + // Clear the text input buffer + memset(gTextBoxInput, 0, maxLength); + + // Enter in the the text input buffer any existing + // text. + if (existing_text != (rct_string_id)STR_NONE) + format_string(gTextBoxInput, existing_text, &existing_args); + + // In order to prevent strings that exceed the maxLength + // from crashing the game. + gTextBoxInput[maxLength - 1] = '\0'; + + platform_start_text_input(gTextBoxInput, maxLength); +} + +void window_cancel_textbox() +{ + if (gUsingWidgetTextBox) { + rct_window *w = window_find_by_number( + gCurrentTextBox.window.classification, + gCurrentTextBox.window.number + ); + gCurrentTextBox.window.classification = 255; + gCurrentTextBox.window.number = 0; + platform_stop_text_input(); + gUsingWidgetTextBox = false; + widget_invalidate(w, gCurrentTextBox.widget_index); + gCurrentTextBox.widget_index = WWT_LAST; + } +} + +void window_update_textbox_caret() +{ + gTextBoxFrameNo++; + if (gTextBoxFrameNo > 30) + gTextBoxFrameNo = 0; +} + +void window_update_textbox() +{ + if (gUsingWidgetTextBox) { + gTextBoxFrameNo = 0; + rct_window *w = window_find_by_number( + gCurrentTextBox.window.classification, + gCurrentTextBox.window.number + ); + widget_invalidate(w, gCurrentTextBox.widget_index); + window_event_textinput_call(w, gCurrentTextBox.widget_index, gTextBoxInput); + } } \ No newline at end of file diff --git a/src/interface/window.h b/src/interface/window.h index 9bae78e466..bb0a51021b 100644 --- a/src/interface/window.h +++ b/src/interface/window.h @@ -31,6 +31,10 @@ struct rct_window; union rct_window_event; extern uint8 TextInputDescriptionArgs[8]; +extern char gTextBoxInput[512]; +extern int gMaxTextBoxInputLength; +extern int gTextBoxFrameNo; +extern bool gUsingWidgetTextBox; typedef void wndproc(struct rct_window*, union rct_window_event*); @@ -47,6 +51,8 @@ typedef struct { int widget_index; } widget_identifier; +extern widget_identifier gCurrentTextBox; + /** * Widget structure * size: 0x10 @@ -605,6 +611,11 @@ void textinput_cancel(); void window_move_and_snap(rct_window *w, int newWindowX, int newWindowY, int snapProximity); int window_can_resize(rct_window *w); +void window_start_textbox(rct_window *call_w, int call_widget, rct_string_id existing_text, uint32 existing_args, int maxLength); +void window_cancel_textbox(); +void window_update_textbox_caret(); +void window_update_textbox(); + #ifdef _MSC_VER #define window_get_register(w) \ __asm mov w, esi diff --git a/src/platform/shared.c b/src/platform/shared.c index 28f0f42914..aafed93a35 100644 --- a/src/platform/shared.c +++ b/src/platform/shared.c @@ -389,6 +389,7 @@ void platform_process_messages() gTextInputCursorPosition--; gTextInputLength--; console_refresh_caret(); + window_update_textbox(); } if (e.key.keysym.sym == SDLK_END){ gTextInputCursorPosition = gTextInputLength; @@ -403,6 +404,10 @@ void platform_process_messages() gTextInput[gTextInputMaxLength - 1] = '\0'; gTextInputLength--; console_refresh_caret(); + window_update_textbox(); + } + if (e.key.keysym.sym == SDLK_RETURN && gTextInput) { + window_cancel_textbox(); } if (e.key.keysym.sym == SDLK_LEFT && gTextInput){ if (gTextInputCursorPosition) gTextInputCursorPosition--; @@ -432,7 +437,7 @@ void platform_process_messages() gTextInputCursorPosition++; } - + window_update_textbox(); } } break; @@ -479,6 +484,7 @@ void platform_process_messages() gTextInputCursorPosition++; console_refresh_caret(); + window_update_textbox(); } break; default: diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c index d71eda159a..a32f4bdf0f 100644 --- a/src/windows/editor_object_selection.c +++ b/src/windows/editor_object_selection.c @@ -115,7 +115,7 @@ static rct_widget window_editor_object_selection_widgets[] = { { WWT_FLATBTN, 1, 391, 504, 46, 159, 0xFFFFFFFF, STR_NONE }, { WWT_DROPDOWN_BUTTON, 0, 384, 595, 24, 35, STR_INSTALL_NEW_TRACK_DESIGN, STR_INSTALL_NEW_TRACK_DESIGN_TIP }, { WWT_DROPDOWN_BUTTON, 0, 350, 463, 23, 34, 5261, 5265 }, - { WWT_DROPDOWN_BUTTON, 1, 4, 214, 46, 57, STR_NONE, STR_NONE }, + { WWT_TEXT_BOX, 1, 4, 214, 46, 57, (uint32)_filter_string, STR_NONE }, { WWT_DROPDOWN_BUTTON, 1, 218, 287, 46, 57, 5277, STR_NONE }, { WIDGETS_END } }; @@ -130,6 +130,7 @@ static void window_editor_object_selection_close(); static void window_editor_object_selection_mouseup(); static void window_editor_object_selection_mousedown(int widgetIndex, rct_window*w, rct_widget* widget); static void window_editor_object_selection_dropdown(); +static void window_editor_object_selection_update(rct_window *w); static void window_editor_object_selection_scrollgetsize(); static void window_editor_object_selection_scroll_mousedown(); static void window_editor_object_selection_scroll_mouseover(); @@ -146,7 +147,7 @@ static void* window_editor_object_selection_events[] = { (void*)window_editor_object_selection_mousedown, (void*)window_editor_object_selection_dropdown, (void*)window_editor_object_selection_emptysub, - (void*)window_editor_object_selection_emptysub, + (void*)window_editor_object_selection_update, (void*)window_editor_object_selection_emptysub, (void*)window_editor_object_selection_emptysub, (void*)window_editor_object_selection_emptysub, @@ -356,7 +357,8 @@ static void window_editor_object_selection_mouseup() window_loadsave_open(LOADSAVETYPE_LOAD | LOADSAVETYPE_TRACK); break; case WIDX_FILTER_STRING_BUTTON: - window_text_input_open(w, widgetIndex, 5275, 5276, 1170, (uint32)_filter_string, 40); + //window_text_input_open(w, widgetIndex, 5275, 5276, 1170, (uint32)_filter_string, 40); + window_start_textbox(w, widgetIndex, 1170, (uint32)_filter_string, 40); break; case WIDX_FILTER_CLEAR_BUTTON: memset(_filter_string, 0, sizeof(_filter_string)); @@ -698,7 +700,7 @@ static void window_editor_object_selection_paint() rct_stex_entry* stex_entry = RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_TEXT_TEMP_CHUNK, rct_stex_entry*); - gfx_fill_rect_inset(dpi, + /*gfx_fill_rect_inset(dpi, w->x + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].left, w->y + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].top, w->x + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].right, @@ -716,7 +718,7 @@ static void window_editor_object_selection_paint() w->x + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].left + 1, w->y + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].top, w->x + window_editor_object_selection_widgets[WIDX_FILTER_STRING_BUTTON].right - ); + );*/ if (w->selected_list_item == -1 || stex_entry == NULL) return; @@ -1034,6 +1036,15 @@ static void editor_load_selected_objects() } } +static void window_editor_object_selection_update(rct_window *w) +{ + if (gCurrentTextBox.window.classification == w->classification && + gCurrentTextBox.window.number == w->number) { + window_update_textbox_caret(); + widget_invalidate(w, WIDX_FILTER_STRING_BUTTON); + } +} + static void window_editor_object_selection_textinput() { uint8 result; @@ -1045,6 +1056,10 @@ static void window_editor_object_selection_textinput() if (widgetIndex != WIDX_FILTER_STRING_BUTTON || !result) return; + + if (strcmp(_filter_string, text) == 0) + return; + if (strlen(text) == 0) { memset(_filter_string, 0, sizeof(_filter_string)); }