// (c) 1995-2014 by Artsoft Entertainment
// Holger Schemel
// info@artsoft.org
-// http://www.artsoft.org/
+// https://www.artsoft.org/
// ----------------------------------------------------------------------------
// gadgets.c
// ============================================================================
static struct GadgetInfo *last_info_gi = NULL;
static int next_free_gadget_id = 1;
static boolean gadget_id_wrapped = FALSE;
+static int gadget_screen_border_right = -1;
+static int gadget_screen_border_bottom = -1;
static void (*PlayGadgetSoundActivating)(void) = NULL;
static void (*PlayGadgetSoundSelecting)(void) = NULL;
PlayGadgetSoundSelecting = selecting_function;
}
+void InitGadgetScreenBorders(int border_right, int border_bottom)
+{
+ gadget_screen_border_right = border_right;
+ gadget_screen_border_bottom = border_bottom;
+}
+
+static int getGadgetScreenBorderRight(void)
+{
+ if (gadget_screen_border_right < gfx.sx ||
+ gadget_screen_border_right > gfx.sx + gfx.sxsize)
+ return gfx.sx + gfx.sxsize;
+
+ return gadget_screen_border_right;
+}
+
+static int getGadgetScreenBorderBottom(void)
+{
+ if (gadget_screen_border_bottom < gfx.sy ||
+ gadget_screen_border_bottom > gfx.sy + gfx.sysize)
+ return gfx.sy + gfx.sysize;
+
+ return gadget_screen_border_bottom;
+}
+
static struct GadgetInfo *getGadgetInfoFromGadgetID(int id)
{
struct GadgetInfo *gi = gadget_list_first_entry;
}
if (next_free_gadget_id <= 0) // cannot get new gadget id
- Error(ERR_EXIT, "too much gadgets -- this should not happen");
+ Fail("too much gadgets -- this should not happen");
return id;
}
return gi;
}
+ // full text areas may overlap other active gadgets, so check them first
+ for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
+ {
+ if (gi->mapped && gi->active &&
+ gi->type & GD_TYPE_TEXT_AREA && gi->textarea.full_open &&
+ mx >= gi->textarea.full_x && mx < gi->textarea.full_x + gi->width &&
+ my >= gi->textarea.full_y && my < gi->textarea.full_y + gi->height)
+ return gi;
+ }
+
// check all other gadgets
for (gi = gadget_list_first_entry; gi != NULL; gi = gi->next)
{
{
case GD_TYPE_NORMAL_BUTTON:
case GD_TYPE_CHECK_BUTTON:
+ case GD_TYPE_CHECK_BUTTON_2:
case GD_TYPE_RADIO_BUTTON:
BlitBitmapOnBackground(gd->bitmap, drawto,
border_x, gi->height, gi->x, gi->y);
// middle part of gadget
- for (i=0; i < gi->textbutton.size; i++)
+ for (i = 0; i < gi->textbutton.size; i++)
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
font_width, gi->height,
gi->x + border_x + i * font_width, gi->y);
char text[MAX_GADGET_TEXTSIZE + 1];
int font_nr = (pressed ? gi->font_active : gi->font);
int font_width = getFontWidth(font_nr);
+ int font_height = getFontHeight(font_nr);
+ struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
int border_x = gi->border.xsize;
int border_y = gi->border.ysize;
+ int text_x = gi->x + font->draw_xoffset;
+ int text_y = gi->y + font->draw_yoffset;
// left part of gadget
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
border_x, gi->height, gi->x, gi->y);
// middle part of gadget
- for (i=0; i < gi->textinput.size + 1; i++)
+ for (i = 0; i < gi->textinput.size + 1; i++)
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
font_width, gi->height,
gi->x + border_x + i * font_width, gi->y);
strcpy(text, gi->textinput.value);
strcat(text, " ");
+ // dirty workaround to erase text if input gadget font has draw offset
+ if (font->draw_xoffset != 0 || font->draw_yoffset != 0)
+ for (i = 0; i < gi->textinput.size + 1; i++)
+ BlitBitmapOnBackground(gd->bitmap, drawto,
+ gd->x + border_x, gd->y + border_y,
+ font_width, font_height,
+ text_x + border_x + i * font_width,
+ text_y + border_y);
+
// gadget text value
DrawTextExt(drawto,
gi->x + border_x, gi->y + border_y, text,
int border_x = gi->border.xsize;
int border_y = gi->border.ysize;
int gd_height = 2 * border_y + font_height;
+ int x = gi->x;
+ int y = gi->y;
+ int width = gi->width;
+ int height = gi->height;
+ int xsize = gi->textarea.xsize;
+ int ysize = gi->textarea.ysize;
+
+ if (gi->textarea.cropped)
+ {
+ if (pressed)
+ {
+ x = gi->textarea.full_x;
+ y = gi->textarea.full_y;
+
+ if (!gi->textarea.full_open)
+ {
+ gi->textarea.full_open = TRUE;
+
+ // save background under fully opened text area
+ BlitBitmap(drawto, gfx.field_save_buffer,
+ gi->textarea.full_x, gi->textarea.full_y,
+ gi->width, gi->height,
+ gi->textarea.full_x, gi->textarea.full_y);
+ }
+ }
+ else
+ {
+ width = gi->textarea.crop_width;
+ height = gi->textarea.crop_height;
+ xsize = gi->textarea.crop_xsize;
+ ysize = gi->textarea.crop_ysize;
+
+ if (gi->textarea.full_open)
+ {
+ gi->textarea.full_open = FALSE;
+
+ // restore background under fully opened text area
+ BlitBitmap(gfx.field_save_buffer, drawto,
+ gi->textarea.full_x, gi->textarea.full_y,
+ gi->width, gi->height,
+ gi->textarea.full_x, gi->textarea.full_y);
+ }
+ }
+ }
// top left part of gadget border
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y,
- border_x, border_y, gi->x, gi->y);
+ border_x, border_y, x, y);
// top middle part of gadget border
- for (i=0; i < gi->textarea.xsize; i++)
+ for (i = 0; i < xsize; i++)
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
font_width, border_y,
- gi->x + border_x + i * font_width, gi->y);
+ x + border_x + i * font_width, y);
// top right part of gadget border
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + gi->border.width - border_x, gd->y,
border_x, border_y,
- gi->x + gi->width - border_x, gi->y);
+ x + width - border_x, y);
// left and right part of gadget border for each row
- for (i=0; i < gi->textarea.ysize; i++)
+ for (i = 0; i < ysize; i++)
{
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
border_x, font_height,
- gi->x, gi->y + border_y + i * font_height);
+ x, y + border_y + i * font_height);
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + gi->border.width - border_x,
gd->y + border_y,
border_x, font_height,
- gi->x + gi->width - border_x,
- gi->y + border_y + i * font_height);
+ x + width - border_x,
+ y + border_y + i * font_height);
}
// bottom left part of gadget border
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x, gd->y + gd_height - border_y,
border_x, border_y,
- gi->x, gi->y + gi->height - border_y);
+ x, y + height - border_y);
// bottom middle part of gadget border
- for (i=0; i < gi->textarea.xsize; i++)
+ for (i = 0; i < xsize; i++)
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + border_x,
gd->y + gd_height - border_y,
font_width, border_y,
- gi->x + border_x + i * font_width,
- gi->y + gi->height - border_y);
+ x + border_x + i * font_width,
+ y + height - border_y);
// bottom right part of gadget border
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + gi->border.width - border_x,
gd->y + gd_height - border_y,
border_x, border_y,
- gi->x + gi->width - border_x,
- gi->y + gi->height - border_y);
+ x + width - border_x,
+ y + height - border_y);
ClearRectangleOnBackground(drawto,
- gi->x + border_x,
- gi->y + border_y,
- gi->width - 2 * border_x,
- gi->height - 2 * border_y);
+ x + border_x,
+ y + border_y,
+ width - 2 * border_x,
+ height - 2 * border_y);
// gadget text value
- DrawTextBuffer(gi->x + border_x, gi->y + border_y, gi->textarea.value,
- font_nr, gi->textarea.xsize, -1, gi->textarea.ysize, 0,
- BLIT_ON_BACKGROUND, FALSE, FALSE, FALSE);
+ DrawTextArea(x + border_x, y + border_y, gi->textarea.value,
+ font_nr, xsize, -1, ysize, 0,
+ BLIT_ON_BACKGROUND, FALSE, FALSE, FALSE);
cursor_letter = gi->textarea.value[gi->textarea.cursor_position];
cursor_string[0] = (cursor_letter != '\0' ? cursor_letter : ' ');
// draw cursor, if active
if (pressed)
DrawTextExt(drawto,
- gi->x + border_x + gi->textarea.cursor_x * font_width,
- gi->y + border_y + gi->textarea.cursor_y * font_height,
+ x + border_x + gi->textarea.cursor_x * font_width,
+ y + border_y + gi->textarea.cursor_y * font_height,
cursor_string,
font_nr, BLIT_INVERSE);
}
border_x, gi->height, gi->x, gi->y);
// middle part of gadget
- for (i=0; i < gi->selectbox.size; i++)
+ for (i = 0; i < gi->selectbox.size; i++)
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
font_width, gi->height,
gi->x + border_x + i * font_width, gi->y);
gi->selectbox.x, gi->selectbox.y);
// top middle part of gadget border
- for (i=0; i < gi->selectbox.size; i++)
+ for (i = 0; i < gi->selectbox.size; i++)
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x + border_x, gd->y,
font_width, border_y,
gi->selectbox.x + border_x + i * font_width,
gi->selectbox.y);
// left and right part of gadget border for each row
- for (i=0; i < gi->selectbox.num_values; i++)
+ for (i = 0; i < gi->selectbox.num_values; i++)
{
BlitBitmapOnBackground(gd->bitmap, drawto, gd->x, gd->y + border_y,
border_x, font_height,
gi->selectbox.y + box_height - border_y);
// bottom middle part of gadget border
- for (i=0; i < gi->selectbox.size; i++)
+ for (i = 0; i < gi->selectbox.size; i++)
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + border_x,
gd->y + gi->height - border_y,
gi->selectbox.height - 2 * border_y);
// selectbox text values
- for (i=0; i < gi->selectbox.num_values; i++)
+ for (i = 0; i < gi->selectbox.num_values; i++)
{
int mask_mode = BLIT_MASKED;
xpos, ypos);
// middle part of gadget
- for (i=0; i < num_steps; i++)
+ for (i = 0; i < num_steps; i++)
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x, gd->y + gi->border.ysize,
gi->width, design_body,
xpos, ypos);
// middle part of gadget
- for (i=0; i < num_steps; i++)
+ for (i = 0; i < num_steps; i++)
BlitBitmapOnBackground(gd->bitmap, drawto,
gd->x + gi->border.xsize, gd->y,
design_body, gi->height,
{
case GD_TYPE_NORMAL_BUTTON:
case GD_TYPE_CHECK_BUTTON:
+ case GD_TYPE_CHECK_BUTTON_2:
case GD_TYPE_RADIO_BUTTON:
SDL_SetTextureAlphaMod(gd->bitmap->texture_masked, alpha);
SDL_SetTextureBlendMode(gd->bitmap->texture_masked, SDL_BLENDMODE_BLEND);
{
int tag = first_tag;
- if (gi == NULL || gi->deactivated)
+ if (gi == NULL)
return;
while (tag != GDI_END)
{
- switch(tag)
+ switch (tag)
{
case GDI_IMAGE_ID:
gi->image_id = va_arg(ap, int);
break;
default:
- Error(ERR_EXIT, "HandleGadgetTags(): unknown tag %d", tag);
+ Fail("HandleGadgetTags(): unknown tag %d", tag);
}
tag = va_arg(ap, int); // read next tag
int border_xsize = gi->border.xsize;
int border_ysize = gi->border.ysize;
int button_size = gi->border.xsize_selectbutton;
- int bottom_screen_border = gfx.sy + gfx.sysize - font_height;
+ int bottom_screen_border = getGadgetScreenBorderBottom();
Bitmap *src_bitmap;
int src_x, src_y;
gi->height = 2 * border_ysize + font_height;
if (gi->selectbox.options == NULL)
- Error(ERR_EXIT, "selectbox gadget incomplete (missing options array)");
+ Fail("selectbox gadget incomplete (missing options array)");
gi->selectbox.num_values = 0;
while (gi->selectbox.options[gi->selectbox.num_values].text != NULL)
if (gi->width == 0 || gi->height == 0 ||
gs->items_max == 0 || gs->items_visible == 0)
- Error(ERR_EXIT, "scrollbar gadget incomplete (missing tags)");
+ Fail("scrollbar gadget incomplete (missing tags)");
// calculate internal scrollbar values
gs->size_min = (gi->type == GD_TYPE_SCROLLBAR_VERTICAL ?
int font_height = getFontHeight(font_nr);
int border_xsize = gi->border.xsize;
int border_ysize = gi->border.ysize;
+ int right_screen_border = getGadgetScreenBorderRight();
+ int bottom_screen_border = getGadgetScreenBorderBottom();
if (gi->width == 0 || gi->height == 0)
{
gi->textarea.xsize = (gi->width - 2 * border_xsize) / font_width;
gi->textarea.ysize = (gi->height - 2 * border_ysize) / font_height;
}
+
+ gi->textarea.full_x = gi->x;
+ gi->textarea.full_y = gi->y;
+ gi->textarea.crop_width = gi->width;
+ gi->textarea.crop_height = gi->height;
+ gi->textarea.crop_xsize = gi->textarea.xsize;
+ gi->textarea.crop_ysize = gi->textarea.ysize;
+
+ gi->textarea.cropped = FALSE;
+
+ if (gi->x + gi->width > right_screen_border)
+ {
+ gi->textarea.full_x = MAX(0, right_screen_border - gi->width);
+ gi->textarea.crop_width = right_screen_border - gi->x;
+ gi->textarea.crop_xsize =
+ (gi->textarea.crop_width - 2 * border_xsize) / font_width;
+ gi->textarea.crop_width =
+ 2 * border_xsize + gi->textarea.crop_xsize * font_width;
+
+ gi->textarea.cropped = TRUE;
+ }
+
+ if (gi->y + gi->height > bottom_screen_border)
+ {
+ gi->textarea.full_y = MAX(0, bottom_screen_border - gi->height);
+ gi->textarea.crop_height = bottom_screen_border - gi->y;
+ gi->textarea.crop_ysize =
+ (gi->textarea.crop_height - 2 * border_ysize) / font_height;
+ gi->textarea.crop_height =
+ 2 * border_ysize + gi->textarea.crop_ysize * font_height;
+
+ gi->textarea.cropped = TRUE;
+ }
+
+ // always start with unselected text area (which is potentially cropped)
+ gi->textarea.full_open = FALSE;
}
}
if (gi == NULL || gi->deactivated || gi->mapped)
return;
+ // do not map overlay touch buttons if touch screen is not used
+ if (gi->overlay_touch_button && !runtime.uses_touch_device)
+ return;
+
gi->mapped = TRUE;
if (redraw)
boolean HandleGadgets(int mx, int my, int button)
{
- static unsigned int pressed_delay = 0;
- static unsigned int pressed_delay_value = GADGET_FRAME_DELAY;
+ static DelayCounter pressed_delay = { GADGET_FRAME_DELAY };
static int last_button = 0;
static int last_mx = 0, last_my = 0;
static int pressed_mx = 0, pressed_my = 0;
struct GadgetInfo *gi = last_gi;
boolean gadget_changed = ((gi->event_mask & GD_EVENT_TEXT_LEAVING) != 0);
- // check if text gadget has changed its value
+ // check if text input gadget has changed its value
if (gi->type & GD_TYPE_TEXT_INPUT)
{
CheckRangeOfNumericInputGadget(gi);
gadget_changed = FALSE;
}
+ // check if text area gadget has changed its value
+ if (gi->type & GD_TYPE_TEXT_AREA)
+ {
+ if (!strEqual(gi->textarea.last_value, gi->textarea.value))
+ strcpy(gi->textarea.last_value, gi->textarea.value);
+ else
+ gadget_changed = FALSE;
+ }
+
// selectbox does not change its value when closed by clicking outside
if (gi->type & GD_TYPE_SELECTBOX)
gadget_changed = FALSE;
(button != 0 && last_gi != NULL && new_gi == last_gi);
gadget_pressed_delay_reached =
- DelayReached(&pressed_delay, pressed_delay_value);
+ DelayReached(&pressed_delay);
gadget_released = (release_event && last_gi != NULL);
gadget_released_inside = (gadget_released && new_gi == last_gi);
else if (gi->type & GD_TYPE_TEXT_AREA && button != 0 && !motion_status)
{
int old_cursor_position = gi->textarea.cursor_position;
- int x = (mx - gi->x - gi->border.xsize) / getFontWidth(gi->font);
- int y = (my - gi->y - gi->border.ysize) / getFontHeight(gi->font);
+ int gadget_x = mx - gi->textarea.full_x - gi->border.xsize;
+ int gadget_y = my - gi->textarea.full_y - gi->border.ysize;
+ int x = gadget_x / getFontWidth(gi->font);
+ int y = gadget_y / getFontHeight(gi->font);
x = (x < 0 ? 0 : x >= gi->textarea.xsize ? gi->textarea.xsize - 1 : x);
y = (y < 0 ? 0 : y >= gi->textarea.ysize ? gi->textarea.ysize - 1 : y);
if (gadget_pressed) // gadget pressed the first time
{
// initialize delay counter
- DelayReached(&pressed_delay, 0);
+ ResetDelayCounter(&pressed_delay);
// start gadget delay with longer delay after first click on gadget
- pressed_delay_value = GADGET_FRAME_DELAY_FIRST;
+ pressed_delay.value = GADGET_FRAME_DELAY_FIRST;
}
else // gadget hold pressed for some time
{
// after first repeated gadget click, continue with shorter delay value
- pressed_delay_value = GADGET_FRAME_DELAY;
+ pressed_delay.value = GADGET_FRAME_DELAY;
}
if (gi->type & GD_TYPE_SCROLLBAR && !gadget_dragging)
boolean deactivate_gadget = TRUE;
boolean gadget_changed = TRUE;
- if (gi->type & GD_TYPE_SELECTBOX)
+ if (gi->type == GD_TYPE_CHECK_BUTTON_2)
+ {
+ gi->checked = !gi->checked;
+ }
+ else if (gi->type & GD_TYPE_SELECTBOX)
{
if (keep_selectbox_open ||
mouse_released_where_pressed ||
{
if (anyTextGadgetActive())
{
+ boolean gadget_changed = ((gi->event_mask & GD_EVENT_TEXT_LEAVING) != 0);
+
// restore previous text (before activating text gadget)
if (gi->type & GD_TYPE_TEXT_INPUT)
+ {
strcpy(gi->textinput.value, gi->textinput.last_value);
+ CheckRangeOfNumericInputGadget(gi);
+ }
+
+ // store current text for text area gadgets when pressing "Escape" key
+ if (gi->type & GD_TYPE_TEXT_AREA)
+ {
+ if (!strEqual(gi->textarea.last_value, gi->textarea.value))
+ strcpy(gi->textarea.last_value, gi->textarea.value);
+ else
+ gadget_changed = FALSE;
+ }
+
DrawGadget(gi, DG_UNPRESSED, gi->direct_draw);
+ if (gi->type & GD_TYPE_TEXT_AREA)
+ {
+ gi->event.type = GD_EVENT_TEXT_LEAVING;
+
+ DoGadgetCallbackAction(gi, gadget_changed);
+ }
+
last_gi = NULL;
StopTextInput();
strcpy(text, gi->textinput.value);
strcpy(&gi->textinput.value[cursor_pos], &text[cursor_pos + 1]);
+ DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+ }
+ else if (key == KSYM_Home && cursor_pos > 0)
+ {
+ gi->textinput.cursor_position = 0;
+
+ DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+ }
+ else if (key == KSYM_End && cursor_pos < text_length)
+ {
+ gi->textinput.cursor_position = text_length;
+
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
}
}
int text_length = strlen(gi->textarea.value);
int area_ysize = gi->textarea.ysize;
int cursor_x_pref = gi->textarea.cursor_x_preferred;
+ int cursor_x = gi->textarea.cursor_x;
int cursor_y = gi->textarea.cursor_y;
int cursor_pos = gi->textarea.cursor_position;
char letter = getCharFromKey(key);
strcpy(text, gi->textarea.value);
strcpy(&gi->textarea.value[cursor_pos], &text[cursor_pos + 1]);
+ DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+ }
+ else if (key == KSYM_Home && cursor_x > 0)
+ {
+ setTextAreaCursorPosition(gi, gi->textarea.cursor_position - cursor_x);
+
+ DrawGadget(gi, DG_PRESSED, gi->direct_draw);
+ }
+ else if (key == KSYM_End && cursor_pos < text_length)
+ {
+ int last_cursor_pos = cursor_pos;
+
+ while (gi->textarea.value[cursor_pos] != '\0' &&
+ gi->textarea.value[cursor_pos] != '\n')
+ cursor_pos++;
+
+ setTextAreaCursorPosition(gi, gi->textarea.cursor_position + cursor_pos -
+ last_cursor_pos);
+
DrawGadget(gi, DG_PRESSED, gi->direct_draw);
}
}