X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Flibgame%2Fgadgets.c;h=2f980f3c1cf0065ec44b37312b6e6de90b5e0f05;hb=ee749a764df3dfa944c1f9de740ccbeb1cfdef40;hp=aacadafad4cfec7fd7d80e42199eae8294ebc91b;hpb=c781e660ed2fe6ad36c9eee0a77d64ee07163030;p=rocksndiamonds.git diff --git a/src/libgame/gadgets.c b/src/libgame/gadgets.c index aacadafa..2f980f3c 100644 --- a/src/libgame/gadgets.c +++ b/src/libgame/gadgets.c @@ -64,21 +64,6 @@ static int getNewGadgetID() return id; } -#if 0 -void DUMP_GADGET_MAP_STATE() -{ - struct GadgetInfo *gi = gadget_list_first_entry; - - while (gi != NULL) - { - printf("-XXX-1-> '%s': %s\n", - gi->info_text, (gi->mapped ? "mapped" : "not mapped")); - - gi = gi->next; - } -} -#endif - static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my) { struct GadgetInfo *gi; @@ -358,8 +343,9 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) /* gadget text value */ DrawTextToTextArea(gi->x + border_x, gi->y + border_y, - gi->textarea.value, font_nr, - gi->textarea.xsize, gi->textarea.ysize); + gi->textarea.value, font_nr, gi->textarea.xsize, + gi->textarea.xsize, gi->textarea.ysize, + BLIT_ON_BACKGROUND); cursor_letter = gi->textarea.value[gi->textarea.cursor_position]; cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' '); @@ -551,15 +537,15 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) { gi->selectbox.open = FALSE; - /* redraw closed selectbox */ - DrawGadget(gi, FALSE, FALSE); - /* restore background under selectbox */ BlitBitmap(gfx.field_save_buffer, drawto, gi->selectbox.x, gi->selectbox.y, gi->selectbox.width, gi->selectbox.height, gi->selectbox.x, gi->selectbox.y); + /* redraw closed selectbox */ + DrawGadget(gi, FALSE, FALSE); + redraw_selectbox = TRUE; } } @@ -588,7 +574,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) xpos, ypos); /* middle part of gadget */ - for (i=0; ibitmap, drawto, gd->x, gd->y + gi->border.ysize, gi->width, design_body, @@ -635,7 +621,7 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) xpos, ypos); /* middle part of gadget */ - for (i=0; ibitmap, drawto, gd->x + gi->border.xsize, gd->y, design_body, gi->height, @@ -680,6 +666,22 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct) gi->y > gfx.vy ? REDRAW_DOOR_2 : REDRAW_DOOR_3); } +static int get_minimal_size_for_numeric_input(int minmax_value) +{ + int min_size = 1; /* value needs at least one digit */ + int i; + + /* add number of digits needed for absolute value */ + for (i = 10; i <= ABS(minmax_value); i *= 10) + min_size++; + + /* if min/max value is negative, add one digit for minus sign */ + if (minmax_value < 0) + min_size++; + + return min_size; +} + static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) { int tag = first_tag; @@ -727,11 +729,11 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) break; case GDI_TYPE: - gi->type = va_arg(ap, unsigned long); + gi->type = va_arg(ap, unsigned int); break; case GDI_STATE: - gi->state = va_arg(ap, unsigned long); + gi->state = va_arg(ap, unsigned int); break; case GDI_ACTIVE: @@ -753,30 +755,33 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) break; case GDI_RADIO_NR: - gi->radio_nr = va_arg(ap, unsigned long); + gi->radio_nr = va_arg(ap, unsigned int); break; case GDI_NUMBER_VALUE: - gi->textinput.number_value = va_arg(ap, long); + gi->textinput.number_value = va_arg(ap, int); sprintf(gi->textinput.value, "%d", gi->textinput.number_value); + strcpy(gi->textinput.last_value, gi->textinput.value); gi->textinput.cursor_position = strlen(gi->textinput.value); break; case GDI_NUMBER_MIN: - gi->textinput.number_min = va_arg(ap, long); + gi->textinput.number_min = va_arg(ap, int); if (gi->textinput.number_value < gi->textinput.number_min) { gi->textinput.number_value = gi->textinput.number_min; sprintf(gi->textinput.value, "%d", gi->textinput.number_value); + strcpy(gi->textinput.last_value, gi->textinput.value); } break; case GDI_NUMBER_MAX: - gi->textinput.number_max = va_arg(ap, long); + gi->textinput.number_max = va_arg(ap, int); if (gi->textinput.number_value > gi->textinput.number_max) { gi->textinput.number_value = gi->textinput.number_max; sprintf(gi->textinput.value, "%d", gi->textinput.number_value); + strcpy(gi->textinput.last_value, gi->textinput.value); } break; @@ -788,12 +793,15 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) max_textsize = MIN(gi->textinput.size, MAX_GADGET_TEXTSIZE - 1); strncpy(gi->textinput.value, va_arg(ap, char *), max_textsize); + strcpy(gi->textinput.last_value, gi->textinput.value); + gi->textinput.value[max_textsize] = '\0'; gi->textinput.cursor_position = strlen(gi->textinput.value); /* same tag also used for other gadget definitions */ strcpy(gi->textbutton.value, gi->textinput.value); strcpy(gi->textarea.value, gi->textinput.value); + strcpy(gi->textarea.last_value, gi->textinput.value); } break; @@ -804,10 +812,17 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) gi->textinput.size = max_textsize; gi->textinput.value[max_textsize] = '\0'; + strcpy(gi->textinput.last_value, gi->textinput.value); /* same tag also used for other gadget definitions */ - strcpy(gi->textbutton.value, gi->textinput.value); - gi->textbutton.size = gi->textinput.size; + + gi->textarea.size = max_textsize; + gi->textarea.value[max_textsize] = '\0'; + strcpy(gi->textarea.last_value, gi->textinput.value); + + gi->textbutton.size = max_textsize; + gi->textbutton.value[max_textsize] = '\0'; + gi->selectbox.size = gi->textinput.size; } break; @@ -889,7 +904,7 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) break; case GDI_EVENT_MASK: - gi->event_mask = va_arg(ap, unsigned long); + gi->event_mask = va_arg(ap, unsigned int); break; case GDI_AREA_SIZE: @@ -984,6 +999,19 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) int border_xsize = gi->border.xsize; int border_ysize = gi->border.ysize; + if (gi->type == GD_TYPE_TEXT_INPUT_NUMERIC) + { + int number_min = gi->textinput.number_min; + int number_max = gi->textinput.number_max; + int min_size_min = get_minimal_size_for_numeric_input(number_min); + int min_size_max = get_minimal_size_for_numeric_input(number_max); + int min_size = MAX(min_size_min, min_size_max); + + /* expand gadget text input size, if maximal value is too large */ + if (gi->textinput.size < min_size) + gi->textinput.size = min_size; + } + gi->width = 2 * border_xsize + (gi->textinput.size + 1) * font_width; gi->height = 2 * border_ysize + font_height; } @@ -1025,7 +1053,11 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) getFontCharSource(font_nr, FONT_ASCII_CURSOR, &src_bitmap, &src_x, &src_y); src_x += font_width / 2; src_y += font_height / 2; - gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y); + + /* there may be esoteric cases with missing or too small font bitmap */ + if (src_bitmap != NULL && + src_x < src_bitmap->width && src_y < src_bitmap->height) + gi->selectbox.inverse_color = GetPixel(src_bitmap, src_x, src_y); /* always start with closed selectbox */ gi->selectbox.open = FALSE; @@ -1058,16 +1090,23 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap) if (gi->type & GD_TYPE_SCROLLBAR) { struct GadgetScrollbar *gs = &gi->scrollbar; + int scrollbar_size_cmp; if (gi->width == 0 || gi->height == 0 || gs->items_max == 0 || gs->items_visible == 0) Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)"); /* calculate internal scrollbar values */ + gs->size_min = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ? + gi->width : gi->height); gs->size_max = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ? gi->height : gi->width); - gs->size = gs->size_max * gs->items_visible / gs->items_max; - gs->position = gs->size_max * gs->item_position / gs->items_max; + + scrollbar_size_cmp = gs->size_max * gs->items_visible / gs->items_max; + gs->size = MAX(scrollbar_size_cmp, gs->size_min); + gs->size_max_cmp = (gs->size_max - (gs->size - scrollbar_size_cmp)); + + gs->position = gs->size_max_cmp * gs->item_position / gs->items_max; gs->position_max = gs->size_max - gs->size; gs->correction = gs->size_max / gs->items_max / 2; @@ -1266,21 +1305,26 @@ void RemapAllGadgets() MultiMapGadgets(MULTIMAP_ALL | MULTIMAP_REMAP); } -static boolean anyTextInputGadgetActive() +boolean anyTextInputGadgetActive() { return (last_gi && (last_gi->type & GD_TYPE_TEXT_INPUT) && last_gi->mapped); } -static boolean anyTextAreaGadgetActive() +boolean anyTextAreaGadgetActive() { return (last_gi && (last_gi->type & GD_TYPE_TEXT_AREA) && last_gi->mapped); } -static boolean anySelectboxGadgetActive() +boolean anySelectboxGadgetActive() { return (last_gi && (last_gi->type & GD_TYPE_SELECTBOX) && last_gi->mapped); } +boolean anyScrollbarGadgetActive() +{ + return (last_gi && (last_gi->type & GD_TYPE_SCROLLBAR) && last_gi->mapped); +} + boolean anyTextGadgetActive() { return (anyTextInputGadgetActive() || @@ -1288,8 +1332,27 @@ boolean anyTextGadgetActive() anySelectboxGadgetActive()); } +static boolean insideSelectboxLine(struct GadgetInfo *gi, int mx, int my) +{ + return(gi != NULL && + gi->type & GD_TYPE_SELECTBOX && + mx >= gi->x && mx < gi->x + gi->width && + my >= gi->y && my < gi->y + gi->height); +} + +static boolean insideSelectboxArea(struct GadgetInfo *gi, int mx, int my) +{ + return(gi != NULL && + gi->type & GD_TYPE_SELECTBOX && + mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width && + my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height); +} + void ClickOnGadget(struct GadgetInfo *gi, int button) { + if (!gi->mapped) + return; + /* simulate releasing mouse button over last gadget, if still pressed */ if (button_status) HandleGadgets(-1, -1, 0); @@ -1301,19 +1364,25 @@ void ClickOnGadget(struct GadgetInfo *gi, int button) HandleGadgets(gi->x, gi->y, 0); } -void HandleGadgets(int mx, int my, int button) +boolean HandleGadgets(int mx, int my, int button) { static struct GadgetInfo *last_info_gi = NULL; static unsigned long pressed_delay = 0; static int last_button = 0; static int last_mx = 0, last_my = 0; + static int pressed_mx = 0, pressed_my = 0; int scrollbar_mouse_pos = 0; struct GadgetInfo *new_gi, *gi; boolean press_event; boolean release_event; boolean mouse_moving; + boolean mouse_inside_select_line; + boolean mouse_inside_select_area; + boolean mouse_released_where_pressed; boolean gadget_pressed; boolean gadget_pressed_repeated; + boolean gadget_pressed_off_borders; + boolean gadget_pressed_inside_select_line; boolean gadget_moving; boolean gadget_moving_inside; boolean gadget_moving_off_borders; @@ -1326,7 +1395,7 @@ void HandleGadgets(int mx, int my, int button) /* check if there are any gadgets defined */ if (gadget_list_first_entry == NULL) - return; + return FALSE; /* simulated release of mouse button over last gadget */ if (mx == -1 && my == -1 && button == 0) @@ -1348,20 +1417,59 @@ void HandleGadgets(int mx, int my, int button) last_mx = mx; last_my = my; + if (press_event && new_gi != last_gi) + { + pressed_mx = mx; + pressed_my = my; + } + + mouse_released_where_pressed = + (release_event && mx == pressed_mx && my == pressed_my); + + mouse_inside_select_line = insideSelectboxLine(new_gi, mx, my); + mouse_inside_select_area = insideSelectboxArea(new_gi, mx, my); + + gadget_pressed_off_borders = (press_event && new_gi != last_gi); + + gadget_pressed_inside_select_line = + (press_event && new_gi != NULL && + new_gi->type & GD_TYPE_SELECTBOX && new_gi->selectbox.open && + insideSelectboxLine(new_gi, mx, my)); + /* if mouse button pressed outside text or selectbox gadget, deactivate it */ if (anyTextGadgetActive() && - button != 0 && !motion_status && new_gi != last_gi) + (gadget_pressed_off_borders || + (gadget_pressed_inside_select_line && !mouse_inside_select_area))) { - CheckRangeOfNumericInputGadget(last_gi); /* in case of numeric gadget */ + struct GadgetInfo *gi = last_gi; + boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_LEAVING); + + /* check if text gadget has changed its value */ + if (gi->type & GD_TYPE_TEXT_INPUT) + { + CheckRangeOfNumericInputGadget(gi); + + if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0) + strcpy(gi->textinput.last_value, gi->textinput.value); + else + gadget_changed = FALSE; + } - DrawGadget(last_gi, DG_UNPRESSED, last_gi->direct_draw); + /* selectbox does not change its value when closed by clicking outside */ + if (gi->type & GD_TYPE_SELECTBOX) + gadget_changed = FALSE; + + DrawGadget(gi, DG_UNPRESSED, gi->direct_draw); - last_gi->event.type = GD_EVENT_TEXT_LEAVING; + gi->event.type = GD_EVENT_TEXT_LEAVING; - if (last_gi->event_mask & GD_EVENT_TEXT_LEAVING) - last_gi->callback_action(last_gi); + if (gadget_changed && !(gi->type & GD_TYPE_SELECTBOX)) + gi->callback_action(gi); last_gi = NULL; + + if (gadget_pressed_inside_select_line) + new_gi = NULL; } gadget_pressed = @@ -1380,14 +1488,8 @@ void HandleGadgets(int mx, int my, int button) /* when handling selectbox, set additional state values */ if (gadget_released_inside && (last_gi->type & GD_TYPE_SELECTBOX)) { - struct GadgetInfo *gi = last_gi; - - gadget_released_inside_select_line = - (mx >= gi->x && mx < gi->x + gi->width && - my >= gi->y && my < gi->y + gi->height); - gadget_released_inside_select_area = - (mx >= gi->selectbox.x && mx < gi->selectbox.x + gi->selectbox.width && - my >= gi->selectbox.y && my < gi->selectbox.y + gi->selectbox.height); + gadget_released_inside_select_line = insideSelectboxLine(last_gi, mx, my); + gadget_released_inside_select_area = insideSelectboxArea(last_gi, mx, my); } else { @@ -1410,12 +1512,16 @@ void HandleGadgets(int mx, int my, int button) /* if mouse button released, no gadget needs to be handled anymore */ if (gadget_released) { - if (last_gi->type & GD_TYPE_SELECTBOX && - (gadget_released_inside_select_line || - gadget_released_off_borders)) /* selectbox stays open */ + if (gi->type & GD_TYPE_SELECTBOX && + (mouse_released_where_pressed || + !gadget_released_inside_select_area)) /* selectbox stays open */ + { gi->selectbox.stay_open = TRUE; - else if (!(last_gi->type & GD_TYPE_TEXT_INPUT || - last_gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */ + pressed_mx = 0; + pressed_my = 0; + } + else if (!(gi->type & GD_TYPE_TEXT_INPUT || + gi->type & GD_TYPE_TEXT_AREA)) /* text input stays open */ last_gi = NULL; } @@ -1469,7 +1575,7 @@ void HandleGadgets(int mx, int my, int button) if (gi->textarea.cursor_position != old_cursor_position) DrawGadget(gi, DG_PRESSED, gi->direct_draw); } - else if (gi->type & GD_TYPE_SELECTBOX) + else if (gi->type & GD_TYPE_SELECTBOX && gi->selectbox.open) { int old_index = gi->selectbox.current_index; @@ -1568,7 +1674,7 @@ void HandleGadgets(int mx, int my, int button) if (gs->item_position < 0) gs->item_position = 0; - if (gs->item_position > gs->items_max - gs->items_visible) + else if (gs->item_position > gs->items_max - gs->items_visible) gs->item_position = gs->items_max - gs->items_visible; if (old_item_position != gs->item_position) @@ -1590,7 +1696,7 @@ void HandleGadgets(int mx, int my, int button) /* don't handle this scrollbar anymore while mouse button pressed */ last_gi = NULL; - return; + return TRUE; } } @@ -1650,13 +1756,22 @@ void HandleGadgets(int mx, int my, int button) gs->position = scrollbar_mouse_pos - gs->drag_position; - if (gs->position < 0) + /* make sure to always precisely reach end positions when dragging */ + if (gs->position <= 0) + { gs->position = 0; - if (gs->position > gs->position_max) + gs->item_position = 0; + } + else if (gs->position >= gs->position_max) + { gs->position = gs->position_max; - - gs->item_position = - gs->items_max * (gs->position + gs->correction) / gs->size_max; + gs->item_position = gs->items_max - gs->items_visible; + } + else + { + gs->item_position = + gs->items_max * (gs->position + gs->correction) / gs->size_max_cmp; + } if (gs->item_position < 0) gs->item_position = 0; @@ -1685,14 +1800,20 @@ void HandleGadgets(int mx, int my, int button) if (gadget_released_inside) { boolean deactivate_gadget = TRUE; + boolean gadget_changed = TRUE; if (gi->type & GD_TYPE_SELECTBOX) { - if (gadget_released_inside_select_line || - gadget_released_off_borders) /* selectbox stays open */ + if (mouse_released_where_pressed || + !gadget_released_inside_select_area) /* selectbox stays open */ + { deactivate_gadget = FALSE; - else + gadget_changed = FALSE; + } + else if (gi->selectbox.index != gi->selectbox.current_index) gi->selectbox.index = gi->selectbox.current_index; + else + gadget_changed = FALSE; } if (deactivate_gadget && @@ -1703,8 +1824,10 @@ void HandleGadgets(int mx, int my, int button) gi->state = GD_BUTTON_UNPRESSED; gi->event.type = GD_EVENT_RELEASED; - if ((gi->event_mask & GD_EVENT_RELEASED) && deactivate_gadget) + if ((gi->event_mask & GD_EVENT_RELEASED) && gadget_changed) + { gi->callback_action(gi); + } } if (gadget_released_off_borders) @@ -1722,6 +1845,9 @@ void HandleGadgets(int mx, int my, int button) /* handle gadgets unmapped/mapped between pressing and releasing */ if (release_event && !gadget_released && new_gi) new_gi->state = GD_BUTTON_UNPRESSED; + + return (gadget_pressed || gadget_pressed_repeated || + gadget_released || gadget_moving); } static void insertCharIntoTextArea(struct GadgetInfo *gi, char c) @@ -1739,7 +1865,7 @@ static void insertCharIntoTextArea(struct GadgetInfo *gi, char c) setTextAreaCursorPosition(gi, gi->textarea.cursor_position + 1); } -void HandleGadgetsKeyInput(Key key) +boolean HandleGadgetsKeyInput(Key key) { struct GadgetInfo *gi = last_gi; @@ -1747,14 +1873,28 @@ void HandleGadgetsKeyInput(Key key) !(gi->type & GD_TYPE_TEXT_INPUT || gi->type & GD_TYPE_TEXT_AREA || gi->type & GD_TYPE_SELECTBOX)) - return; + return FALSE; if (key == KSYM_Return) /* valid for both text input and selectbox */ { + boolean gadget_changed = (gi->event_mask & GD_EVENT_TEXT_RETURN); + if (gi->type & GD_TYPE_TEXT_INPUT) + { CheckRangeOfNumericInputGadget(gi); + + if (strcmp(gi->textinput.value, gi->textinput.last_value) != 0) + strcpy(gi->textinput.last_value, gi->textinput.value); + else + gadget_changed = FALSE; + } else if (gi->type & GD_TYPE_SELECTBOX) - gi->selectbox.index = gi->selectbox.current_index; + { + if (gi->selectbox.index != gi->selectbox.current_index) + gi->selectbox.index = gi->selectbox.current_index; + else + gadget_changed = FALSE; + } if (gi->type & GD_TYPE_TEXT_AREA) { @@ -1771,7 +1911,7 @@ void HandleGadgetsKeyInput(Key key) last_gi = NULL; } - if (gi->event_mask & GD_EVENT_TEXT_RETURN) + if (gadget_changed) gi->callback_action(gi); } else if (gi->type & GD_TYPE_TEXT_INPUT) /* only valid for text input */ @@ -1899,4 +2039,6 @@ void HandleGadgetsKeyInput(Key key) DrawGadget(gi, DG_PRESSED, gi->direct_draw); } } + + return TRUE; }