added cropping text area gadgets that do not fit to the current viewport
authorHolger Schemel <info@artsoft.org>
Fri, 17 Feb 2023 19:22:41 +0000 (20:22 +0100)
committerHolger Schemel <info@artsoft.org>
Fri, 17 Feb 2023 19:22:41 +0000 (20:22 +0100)
src/editor.c
src/libgame/gadgets.c
src/libgame/gadgets.h

index b202aaf890b047abf42b8dee34dd4e30ccb8cafc..23fb6db22508a537398438d1900225177307132b 100644 (file)
@@ -1759,7 +1759,7 @@ static struct
   int gadget_id;
   int xsize, ysize;
   char *value;
-  char *text_above, *infotext;
+  char *text_above, *text_above_cropped, *infotext;
 } textarea_info[ED_NUM_TEXTAREAS] =
 {
   {
@@ -1767,7 +1767,7 @@ static struct
     GADGET_ID_ENVELOPE_INFO,
     MAX_ENVELOPE_XSIZE, MAX_ENVELOPE_YSIZE,
     NULL,
-    "Envelope Content:", "Envelope Content"
+    "Envelope Content:", "Envelope Content (cropped):", "Envelope Content"
   }
 };
 
@@ -7509,9 +7509,13 @@ static void MapTextAreaGadget(int id)
   int yoffset_above = font_height + ED_GADGET_LINE_DISTANCE;
   int x_above = ED_SETTINGS_X(textarea_info[id].x);
   int y_above = ED_SETTINGS_Y(textarea_info[id].y) - yoffset_above;
+  char *text_above = textarea_info[id].text_above;
+
+  if (gi->textarea.cropped && textarea_info[id].text_above_cropped)
+    text_above = textarea_info[id].text_above_cropped;
 
-  if (textarea_info[id].text_above)
-    DrawTextS(x_above, y_above, font_nr, textarea_info[id].text_above);
+  if (text_above)
+    DrawTextS(x_above, y_above, font_nr, text_above);
 
   ModifyGadget(gi, GDI_TEXT_VALUE, textarea_info[id].value, GDI_END);
 
@@ -9649,7 +9653,9 @@ static void DrawEnvelopeTextArea(int envelope_nr)
   struct GadgetInfo *gi = level_editor_gadget[textarea_info[id].gadget_id];
 
   UnmapGadget(gi);
-  DrawBackground(gi->x, gi->y, gi->width, gi->height);
+
+  DrawBackground(gi->x, gi->y,
+                gi->textarea.crop_width, gi->textarea.crop_height);
 
   if (envelope_nr != -1)
     textarea_info[id].value = level.envelope[envelope_nr].text;
index 3c626c90fff3b3b8b33e23c31a6623a2d63eed9d..7c5dbacba410e0afac4f8d62625614577d345a44 100644 (file)
@@ -156,6 +156,16 @@ static struct GadgetInfo *getGadgetInfoFromMousePosition(int mx, int my,
       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)
   {
@@ -396,6 +406,44 @@ static void DrawGadget(struct GadgetInfo *gi, boolean pressed, boolean direct)
        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, x, y);
@@ -1392,6 +1440,8 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
     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)
     {
@@ -1403,6 +1453,42 @@ static void HandleGadgetTags(struct GadgetInfo *gi, int first_tag, va_list ap)
       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;
   }
 }
 
@@ -1893,8 +1979,10 @@ boolean HandleGadgets(int mx, int my, int button)
     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);
index ad9e939c5d7bb3ee37cd180e6072e539d4b5fb41..6a27f988f7c8f6b3101708ba0abdaf21d5a2475f 100644 (file)
@@ -188,6 +188,15 @@ struct GadgetTextArea
   int cursor_x_preferred;              // "preferred" x cursor position
   int size;                            // maximal size of input text
   int xsize, ysize;                    // size of text area (in chars)
+
+  // automatically determined values
+  boolean cropped;                     // text area cropped to fit viewport
+  int full_x, full_y;                  // text area position when not cropped
+  int crop_width, crop_height;         // size of text area when cropped
+  int crop_xsize, crop_ysize;          // size of text area when cropped
+
+  // runtime values
+  boolean full_open;                   // opening state of text area
 };
 
 struct GadgetSelectbox