shithub: choc

Download patch

ref: 379d372c65dee97d13b9a490c7c7a1a466478b7f
parent: 1ec21c92ed4c3f792e642524c5cd5bc30b6d7457
parent: cc87c97e4bae07238e9e1af8668944fd7805c133
author: Simon Howard <[email protected]>
date: Sun Oct 23 16:07:40 EDT 2011

Merge from trunk.

Subversion-branch: /branches/v2-branch
Subversion-revision: 2464

--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,13 @@
      * Fix teleport behavior when emulating the alternate Final Doom
        executable (-gameversion final2) (thanks xttl).
 
+    libtextscreen:
+     * Input boxes stop editing and save when they lose their focus,
+       correcting a previous counterintuitive behavior (thanks
+       Twelve).
+     * The numeric keypad now works properly when entering text values
+       (thanks Twelve).
+
 1.6.0 (2011-05-17):
 
      * The instructions in the INSTALL file are now customized for
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -129,7 +129,7 @@
     sprintf(buf, "BUTTON #%i", button + 1);
 }
 
-static void TXT_JoystickInputDrawer(TXT_UNCAST_ARG(joystick_input), int selected)
+static void TXT_JoystickInputDrawer(TXT_UNCAST_ARG(joystick_input))
 {
     TXT_CAST_ARG(txt_joystick_input_t, joystick_input);
     char buf[20];
@@ -144,7 +144,7 @@
         GetJoystickButtonDescription(*joystick_input->variable, buf);
     }
 
-    TXT_SetWidgetBG(joystick_input, selected);
+    TXT_SetWidgetBG(joystick_input);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -102,7 +102,7 @@
 }
 
 
-static void TXT_KeyInputDrawer(TXT_UNCAST_ARG(key_input), int selected)
+static void TXT_KeyInputDrawer(TXT_UNCAST_ARG(key_input))
 {
     TXT_CAST_ARG(txt_key_input_t, key_input);
     char buf[20];
@@ -117,7 +117,7 @@
         TXT_GetKeyDescription(*key_input->variable, buf);
     }
 
-    TXT_SetWidgetBG(key_input, selected);
+    TXT_SetWidgetBG(key_input);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -94,7 +94,7 @@
     }
 }
 
-static void TXT_MouseInputDrawer(TXT_UNCAST_ARG(mouse_input), int selected)
+static void TXT_MouseInputDrawer(TXT_UNCAST_ARG(mouse_input))
 {
     TXT_CAST_ARG(txt_mouse_input_t, mouse_input);
     char buf[20];
@@ -109,7 +109,7 @@
         GetMouseButtonDescription(*mouse_input->variable, buf);
     }
 
-    TXT_SetWidgetBG(mouse_input, selected);
+    TXT_SetWidgetBG(mouse_input);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
     
     TXT_DrawString(buf);
--- a/textscreen/txt_button.c
+++ b/textscreen/txt_button.c
@@ -38,7 +38,7 @@
     button->widget.h = 1;
 }
 
-static void TXT_ButtonDrawer(TXT_UNCAST_ARG(button), int selected)
+static void TXT_ButtonDrawer(TXT_UNCAST_ARG(button))
 {
     TXT_CAST_ARG(txt_button_t, button);
     int i;
@@ -48,7 +48,7 @@
 
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
-    TXT_SetWidgetBG(button, selected);
+    TXT_SetWidgetBG(button);
 
     TXT_DrawString(button->label);
 
--- a/textscreen/txt_checkbox.c
+++ b/textscreen/txt_checkbox.c
@@ -40,7 +40,7 @@
     checkbox->widget.h = 1;
 }
 
-static void TXT_CheckBoxDrawer(TXT_UNCAST_ARG(checkbox), int selected)
+static void TXT_CheckBoxDrawer(TXT_UNCAST_ARG(checkbox))
 {
     TXT_CAST_ARG(txt_checkbox_t, checkbox);
     int i;
@@ -67,7 +67,7 @@
 
     TXT_DrawString(") ");
 
-    TXT_SetWidgetBG(checkbox, selected);
+    TXT_SetWidgetBG(checkbox);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     TXT_DrawString(checkbox->label);
--- a/textscreen/txt_desktop.c
+++ b/textscreen/txt_desktop.c
@@ -45,8 +45,19 @@
 
 void TXT_AddDesktopWindow(txt_window_t *win)
 {
+    // Previously-top window loses focus:
+
+    if (num_windows > 0)
+    {
+        TXT_SetWindowFocus(all_windows[num_windows - 1], 0);
+    }
+
     all_windows[num_windows] = win;
     ++num_windows;
+
+    // New window gains focus:
+
+    TXT_SetWindowFocus(win, 1);
 }
 
 void TXT_RemoveDesktopWindow(txt_window_t *win)
@@ -53,6 +64,10 @@
 {
     int from, to;
 
+    // Window must lose focus if it's being removed:
+
+    TXT_SetWindowFocus(win, 0);
+
     for (from=0, to=0; from<num_windows; ++from)
     {
         if (all_windows[from] != win)
@@ -61,8 +76,15 @@
             ++to;
         }
     }
-    
+
     num_windows = to;
+
+    // Top window gains focus:
+
+    if (num_windows > 0)
+    {
+        TXT_SetWindowFocus(all_windows[num_windows - 1], 1);
+    }
 }
 
 txt_window_t *TXT_GetActiveWindow(void)
@@ -144,7 +166,7 @@
 
     for (i=0; i<num_windows; ++i)
     {
-        TXT_DrawWindow(all_windows[i], i == num_windows - 1);
+        TXT_DrawWindow(all_windows[i]);
     }
 
     TXT_UpdateScreen();
--- a/textscreen/txt_desktop.h
+++ b/textscreen/txt_desktop.h
@@ -36,7 +36,8 @@
 void TXT_RemoveDesktopWindow(txt_window_t *win);
 void TXT_DrawDesktop(void);
 void TXT_DispatchEvents(void);
-void TXT_DrawWindow(txt_window_t *window, int selected);
+void TXT_DrawWindow(txt_window_t *window);
+void TXT_SetWindowFocus(txt_window_t *window, int focused);
 void TXT_WindowKeyPress(txt_window_t *window, int c);
 
 /**
--- a/textscreen/txt_dropdown.c
+++ b/textscreen/txt_dropdown.c
@@ -221,7 +221,7 @@
     list->widget.h = 1;
 }
 
-static void TXT_DropdownListDrawer(TXT_UNCAST_ARG(list), int selected)
+static void TXT_DropdownListDrawer(TXT_UNCAST_ARG(list))
 {
     TXT_CAST_ARG(txt_dropdown_list_t, list);
     unsigned int i;
@@ -229,7 +229,7 @@
 
     // Set bg/fg text colors.
 
-    TXT_SetWidgetBG(list, selected);
+    TXT_SetWidgetBG(list);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     // Select a string to draw from the list, if the current value is
--- a/textscreen/txt_inputbox.c
+++ b/textscreen/txt_inputbox.c
@@ -32,8 +32,72 @@
 #include "txt_main.h"
 #include "txt_window.h"
 
-static void SetBufferFromValue(txt_inputbox_t *inputbox);
+extern txt_widget_class_t txt_inputbox_class;
+extern txt_widget_class_t txt_int_inputbox_class;
 
+static void SetBufferFromValue(txt_inputbox_t *inputbox)
+{
+    if (inputbox->widget.widget_class == &txt_inputbox_class)
+    {
+        char **value = (char **) inputbox->value;
+
+        if (*value != NULL)
+        {
+            strncpy(inputbox->buffer, *value, inputbox->size);
+            inputbox->buffer[inputbox->size] = '\0';
+        }
+        else
+        {
+            strcpy(inputbox->buffer, "");
+        }
+    }
+    else if (inputbox->widget.widget_class == &txt_int_inputbox_class)
+    {
+        int *value = (int *) inputbox->value;
+        sprintf(inputbox->buffer, "%i", *value);
+    }
+}
+
+static void StartEditing(txt_inputbox_t *inputbox)
+{
+    // Integer input boxes start from an empty buffer:
+
+    if (inputbox->widget.widget_class == &txt_int_inputbox_class)
+    {
+        strcpy(inputbox->buffer, "");
+    }
+    else
+    {
+        SetBufferFromValue(inputbox);
+    }
+
+    inputbox->editing = 1;
+}
+
+static void FinishEditing(txt_inputbox_t *inputbox)
+{
+    if (!inputbox->editing)
+    {
+        return;
+    }
+
+    // Save the new value back to the variable.
+
+    if (inputbox->widget.widget_class == &txt_inputbox_class)
+    {
+        free(*((char **)inputbox->value));
+        *((char **) inputbox->value) = strdup(inputbox->buffer);
+    }
+    else if (inputbox->widget.widget_class == &txt_int_inputbox_class)
+    {
+        *((int *) inputbox->value) = atoi(inputbox->buffer);
+    }
+
+    TXT_EmitSignal(&inputbox->widget, "changed");
+
+    inputbox->editing = 0;
+}
+
 static void TXT_InputBoxSizeCalc(TXT_UNCAST_ARG(inputbox))
 {
     TXT_CAST_ARG(txt_inputbox_t, inputbox);
@@ -44,25 +108,27 @@
     inputbox->widget.h = 1;
 }
 
-static void TXT_InputBoxDrawer(TXT_UNCAST_ARG(inputbox), int selected)
+static void TXT_InputBoxDrawer(TXT_UNCAST_ARG(inputbox))
 {
     TXT_CAST_ARG(txt_inputbox_t, inputbox);
+    int focused;
     int i;
     int chars;
     int w;
 
+    focused = inputbox->widget.focused;
     w = inputbox->widget.w;
 
     // Select the background color based on whether we are currently
-    // editing, and if not, whether the widget is selected.
+    // editing, and if not, whether the widget is focused.
 
-    if (inputbox->editing && selected)
+    if (inputbox->editing && focused)
     {
         TXT_BGColor(TXT_COLOR_BLACK, 0);
     }
     else
     {
-        TXT_SetWidgetBG(inputbox, selected);
+        TXT_SetWidgetBG(inputbox);
     }
 
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
@@ -70,21 +136,21 @@
     if (!inputbox->editing)
     {
         // If not editing, use the current value from inputbox->value.
- 
+
         SetBufferFromValue(inputbox);
     }
-    
+
     TXT_DrawString(inputbox->buffer);
 
     chars = strlen(inputbox->buffer);
 
-    if (chars < w && inputbox->editing && selected)
+    if (chars < w && inputbox->editing && focused)
     {
         TXT_BGColor(TXT_COLOR_BLACK, 1);
         TXT_DrawString("_");
         ++chars;
     }
-    
+
     for (i=chars; i < w; ++i)
     {
         TXT_DrawString(" ");
@@ -125,8 +191,7 @@
     {
         if (key == KEY_ENTER)
         {
-            SetBufferFromValue(inputbox);
-            inputbox->editing = 1;
+            StartEditing(inputbox);
             return 1;
         }
 
@@ -135,12 +200,7 @@
 
     if (key == KEY_ENTER)
     {
-        free(*((char **)inputbox->value));
-        *((char **) inputbox->value) = strdup(inputbox->buffer);
-
-        TXT_EmitSignal(&inputbox->widget, "changed");
-
-        inputbox->editing = 0;
+        FinishEditing(inputbox);
     }
 
     if (key == KEY_ESCAPE)
@@ -159,50 +219,7 @@
     {
         Backspace(inputbox);
     }
-    
-    return 1;
-}
 
-static int TXT_IntInputBoxKeyPress(TXT_UNCAST_ARG(inputbox), int key)
-{
-    TXT_CAST_ARG(txt_inputbox_t, inputbox);
-
-    if (!inputbox->editing)
-    {
-        if (key == KEY_ENTER)
-        {
-            strcpy(inputbox->buffer, "");
-            inputbox->editing = 1;
-            return 1;
-        }
-
-        return 0;
-    }
-
-    if (key == KEY_ENTER)
-    {
-        *((int *) inputbox->value) = atoi(inputbox->buffer);
-
-        inputbox->editing = 0;
-    }
-
-    if (key == KEY_ESCAPE)
-    {
-        inputbox->editing = 0;
-    }
-
-    if (isdigit(key))
-    {
-        // Add character to the buffer
-
-        AddCharacter(inputbox, key);
-    }
-
-    if (key == KEY_BACKSPACE)
-    {
-        Backspace(inputbox);
-    }
-    
     return 1;
 }
 
@@ -224,6 +241,18 @@
     }
 }
 
+static void TXT_InputBoxFocused(TXT_UNCAST_ARG(inputbox), int focused)
+{
+    TXT_CAST_ARG(txt_inputbox_t, inputbox);
+
+    // Stop editing when we lose focus.
+
+    if (inputbox->editing && !focused)
+    {
+        FinishEditing(inputbox);
+    }
+}
+
 txt_widget_class_t txt_inputbox_class =
 {
     TXT_AlwaysSelectable,
@@ -233,6 +262,7 @@
     TXT_InputBoxDestructor,
     TXT_InputBoxMousePress,
     NULL,
+    TXT_InputBoxFocused,
 };
 
 txt_widget_class_t txt_int_inputbox_class =
@@ -240,34 +270,12 @@
     TXT_AlwaysSelectable,
     TXT_InputBoxSizeCalc,
     TXT_InputBoxDrawer,
-    TXT_IntInputBoxKeyPress,
+    TXT_InputBoxKeyPress,
     TXT_InputBoxDestructor,
     TXT_InputBoxMousePress,
     NULL,
+    TXT_InputBoxFocused,
 };
-
-static void SetBufferFromValue(txt_inputbox_t *inputbox)
-{
-    if (inputbox->widget.widget_class == &txt_inputbox_class)
-    {
-        char **value = (char **) inputbox->value;
-
-        if (*value != NULL)
-        {
-            strncpy(inputbox->buffer, *value, inputbox->size);
-            inputbox->buffer[inputbox->size] = '\0';
-        }
-        else
-        {
-            strcpy(inputbox->buffer, "");
-        }
-    }
-    else if (inputbox->widget.widget_class == &txt_int_inputbox_class)
-    {
-        int *value = (int *) inputbox->value;
-        sprintf(inputbox->buffer, "%i", *value);
-    }
-}
 
 txt_inputbox_t *TXT_NewInputBox(char **value, int size)
 {
--- a/textscreen/txt_label.c
+++ b/textscreen/txt_label.c
@@ -36,7 +36,7 @@
     label->widget.h = label->h;
 }
 
-static void TXT_LabelDrawer(TXT_UNCAST_ARG(label), int selected)
+static void TXT_LabelDrawer(TXT_UNCAST_ARG(label))
 {
     TXT_CAST_ARG(txt_label_t, label);
     unsigned int x, y;
--- a/textscreen/txt_radiobutton.c
+++ b/textscreen/txt_radiobutton.c
@@ -40,7 +40,7 @@
     radiobutton->widget.h = 1;
 }
 
-static void TXT_RadioButtonDrawer(TXT_UNCAST_ARG(radiobutton), int selected)
+static void TXT_RadioButtonDrawer(TXT_UNCAST_ARG(radiobutton))
 {
     TXT_CAST_ARG(txt_radiobutton_t, radiobutton);
     int i;
@@ -67,7 +67,7 @@
 
     TXT_DrawString(") ");
 
-    TXT_SetWidgetBG(radiobutton, selected);
+    TXT_SetWidgetBG(radiobutton);
     TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
 
     TXT_DrawString(radiobutton->label);
--- a/textscreen/txt_scrollpane.c
+++ b/textscreen/txt_scrollpane.c
@@ -172,7 +172,7 @@
     }
 }
 
-static void TXT_ScrollPaneDrawer(TXT_UNCAST_ARG(scrollpane), int selected)
+static void TXT_ScrollPaneDrawer(TXT_UNCAST_ARG(scrollpane))
 {
     TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
     int x1, y1, x2, y2;
@@ -211,7 +211,7 @@
 
     if (scrollpane->child != NULL)
     {
-        TXT_DrawWidget(scrollpane->child, selected);
+        TXT_DrawWidget(scrollpane->child);
     }
 
     // Restore old clipping area.
@@ -229,6 +229,19 @@
     }
 }
 
+static void TXT_ScrollPaneFocused(TXT_UNCAST_ARG(scrollpane), int focused)
+{
+    TXT_CAST_ARG(txt_scrollpane_t, scrollpane);
+
+    // Whether the child is focused depends only on whether the scroll pane
+    // itself is focused. Pass through focus to the child.
+
+    if (scrollpane->child != NULL)
+    {
+        TXT_SetWidgetFocus(scrollpane->child, focused);
+    }
+}
+
 // Hack for tables - when browsing a table inside a scroll pane,
 // automatically scroll the window to show the newly-selected
 // item.
@@ -552,6 +565,7 @@
     TXT_ScrollPaneDestructor,
     TXT_ScrollPaneMousePress,
     TXT_ScrollPaneLayout,
+    TXT_ScrollPaneFocused,
 };
 
 txt_scrollpane_t *TXT_NewScrollPane(int w, int h, TXT_UNCAST_ARG(target))
--- a/textscreen/txt_sdl.c
+++ b/textscreen/txt_sdl.c
@@ -410,25 +410,6 @@
         case SDLK_CAPSLOCK:    return KEY_CAPSLOCK;
         case SDLK_SCROLLOCK:   return KEY_SCRLCK;
 
-        case SDLK_KP0:         return KEYP_0;
-        case SDLK_KP1:         return KEYP_1;
-        case SDLK_KP2:         return KEYP_2;
-        case SDLK_KP3:         return KEYP_3;
-        case SDLK_KP4:         return KEYP_4;
-        case SDLK_KP5:         return KEYP_5;
-        case SDLK_KP6:         return KEYP_6;
-        case SDLK_KP7:         return KEYP_7;
-        case SDLK_KP8:         return KEYP_8;
-        case SDLK_KP9:         return KEYP_9;
-
-        case SDLK_KP_PERIOD:   return KEYP_PERIOD;
-        case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
-        case SDLK_KP_PLUS:     return KEYP_PLUS;
-        case SDLK_KP_MINUS:    return KEYP_MINUS;
-        case SDLK_KP_DIVIDE:   return KEYP_DIVIDE;
-        case SDLK_KP_EQUALS:   return KEYP_EQUALS;
-        case SDLK_KP_ENTER:    return KEYP_ENTER;
-
         case SDLK_HOME:        return KEY_HOME;
         case SDLK_INSERT:      return KEY_INS;
         case SDLK_END:         return KEY_END;
@@ -459,7 +440,34 @@
     }
     else
     {
-        return tolower(sym->sym);
+        // Keypad mapping is only done when we want a raw value:
+        // most of the time, the keypad should behave as it normally
+        // does.
+
+        switch (sym->sym)
+        {
+            case SDLK_KP0:         return KEYP_0;
+            case SDLK_KP1:         return KEYP_1;
+            case SDLK_KP2:         return KEYP_2;
+            case SDLK_KP3:         return KEYP_3;
+            case SDLK_KP4:         return KEYP_4;
+            case SDLK_KP5:         return KEYP_5;
+            case SDLK_KP6:         return KEYP_6;
+            case SDLK_KP7:         return KEYP_7;
+            case SDLK_KP8:         return KEYP_8;
+            case SDLK_KP9:         return KEYP_9;
+
+            case SDLK_KP_PERIOD:   return KEYP_PERIOD;
+            case SDLK_KP_MULTIPLY: return KEYP_MULTIPLY;
+            case SDLK_KP_PLUS:     return KEYP_PLUS;
+            case SDLK_KP_MINUS:    return KEYP_MINUS;
+            case SDLK_KP_DIVIDE:   return KEYP_DIVIDE;
+            case SDLK_KP_EQUALS:   return KEYP_EQUALS;
+            case SDLK_KP_ENTER:    return KEYP_ENTER;
+
+            default:
+                return tolower(sym->sym);
+        }
     }
 }
 
--- a/textscreen/txt_separator.c
+++ b/textscreen/txt_separator.c
@@ -46,7 +46,7 @@
     separator->widget.h = 1;
 }
 
-static void TXT_SeparatorDrawer(TXT_UNCAST_ARG(separator), int selected)
+static void TXT_SeparatorDrawer(TXT_UNCAST_ARG(separator))
 {
     TXT_CAST_ARG(txt_separator_t, separator);
     int x, y;
--- a/textscreen/txt_spinctrl.c
+++ b/textscreen/txt_spinctrl.c
@@ -142,12 +142,15 @@
     }
 }
 
-static void TXT_SpinControlDrawer(TXT_UNCAST_ARG(spincontrol), int selected)
+static void TXT_SpinControlDrawer(TXT_UNCAST_ARG(spincontrol))
 {
     TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
     unsigned int i;
     unsigned int padding;
+    int focused;
 
+    focused = spincontrol->widget.focused;
+
     TXT_FGColor(TXT_COLOR_BRIGHT_CYAN);
     TXT_BGColor(TXT_WINDOW_BACKGROUND, 0);
 
@@ -157,13 +160,13 @@
 
     // Choose background color
 
-    if (selected && spincontrol->editing)
+    if (focused && spincontrol->editing)
     {
         TXT_BGColor(TXT_COLOR_BLACK, 0);
     }
     else
     {
-        TXT_SetWidgetBG(spincontrol, selected);
+        TXT_SetWidgetBG(spincontrol);
     }
 
     if (!spincontrol->editing)
@@ -239,6 +242,23 @@
     }
 }
 
+static void FinishEditing(txt_spincontrol_t *spincontrol)
+{
+    switch (spincontrol->type)
+    {
+        case TXT_SPINCONTROL_INT:
+            spincontrol->value->i = atoi(spincontrol->buffer);
+            break;
+
+        case TXT_SPINCONTROL_FLOAT:
+            spincontrol->value->f = (float) atof(spincontrol->buffer);
+            break;
+    }
+
+    spincontrol->editing = 0;
+    EnforceLimits(spincontrol);
+}
+
 static int TXT_SpinControlKeyPress(TXT_UNCAST_ARG(spincontrol), int key)
 {
     TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
@@ -249,19 +269,7 @@
     {
         if (key == KEY_ENTER)
         {
-            switch (spincontrol->type)
-            {
-                case TXT_SPINCONTROL_INT:
-                    spincontrol->value->i = atoi(spincontrol->buffer);
-                    break;
-
-                case TXT_SPINCONTROL_FLOAT:
-                    spincontrol->value->f = (float) atof(spincontrol->buffer);
-                    break;
-            }
-
-            spincontrol->editing = 0;
-            EnforceLimits(spincontrol);
+            FinishEditing(spincontrol);
             return 1;
         }
 
@@ -352,6 +360,13 @@
     }
 }
 
+static void TXT_SpinControlFocused(TXT_UNCAST_ARG(spincontrol), int focused)
+{
+    TXT_CAST_ARG(txt_spincontrol_t, spincontrol);
+
+    FinishEditing(spincontrol);
+}
+
 txt_widget_class_t txt_spincontrol_class =
 {
     TXT_AlwaysSelectable,
@@ -361,6 +376,7 @@
     TXT_SpinControlDestructor,
     TXT_SpinControlMousePress,
     NULL,
+    TXT_SpinControlFocused,
 };
 
 static txt_spincontrol_t *TXT_BaseSpinControl(void)
--- a/textscreen/txt_strut.c
+++ b/textscreen/txt_strut.c
@@ -39,7 +39,7 @@
     strut->widget.h = strut->height;
 }
 
-static void TXT_StrutDrawer(TXT_UNCAST_ARG(strut), int selected)
+static void TXT_StrutDrawer(TXT_UNCAST_ARG(strut))
 {
     // Nothing is drawn for a strut.
 }
--- a/textscreen/txt_table.c
+++ b/textscreen/txt_table.c
@@ -264,6 +264,48 @@
     return -1;
 }
 
+// Change the selected widget.
+
+static void ChangeSelection(txt_table_t *table, int x, int y)
+{
+    txt_widget_t *cur_widget;
+    txt_widget_t *new_widget;
+    int i;
+
+    // No change?
+
+    if (x == table->selected_x && y == table->selected_y)
+    {
+        return;
+    }
+
+    // Unfocus current widget:
+
+    i = table->selected_y * table->columns + table->selected_x;
+
+    if (i < table->num_widgets)
+    {
+        cur_widget = table->widgets[i];
+
+        if (table->widget.focused && cur_widget != NULL)
+        {
+            TXT_SetWidgetFocus(cur_widget, 0);
+        }
+    }
+
+    // Focus new widget.
+
+    new_widget = table->widgets[y * table->columns + x];
+
+    table->selected_x = x;
+    table->selected_y = y;
+
+    if (table->widget.focused && new_widget != NULL)
+    {
+        TXT_SetWidgetFocus(new_widget, 1);
+    }
+}
+
 static int TXT_TableKeyPress(TXT_UNCAST_ARG(table), int key)
 {
     TXT_CAST_ARG(txt_table_t, table);
@@ -300,8 +342,7 @@
             {
                 // Found a selectable widget in this column!
 
-                table->selected_x = new_x;
-                table->selected_y = new_y;
+                ChangeSelection(table, new_x, new_y);
 
                 return 1;
             }
@@ -317,13 +358,12 @@
         for (new_y = table->selected_y - 1; new_y >= 0; --new_y)
         {
             new_x = FindSelectableColumn(table, new_y, table->selected_x);
-                            
+
             if (new_x >= 0)
             {
                 // Found a selectable widget in this column!
 
-                table->selected_x = new_x;
-                table->selected_y = new_y;
+                ChangeSelection(table, new_x, new_y);
 
                 return 1;
             }
@@ -342,7 +382,7 @@
             {
                 // Found a selectable widget!
 
-                table->selected_x = new_x;
+                ChangeSelection(table, new_x, table->selected_y);
 
                 return 1;
             }
@@ -361,7 +401,7 @@
             {
                 // Found a selectable widget!
 
-                table->selected_x = new_x;
+                ChangeSelection(table, new_x, table->selected_y);
 
                 return 1;
             }
@@ -388,8 +428,7 @@
         {
             // Found a selectable column.
 
-            table->selected_x = new_x;
-            table->selected_y = new_y;
+            ChangeSelection(table, new_x, new_y);
 
             break;
         }
@@ -502,25 +541,20 @@
     free(row_heights);
     free(column_widths);
 }
-                
-static void TXT_TableDrawer(TXT_UNCAST_ARG(table), int selected)
+
+static void TXT_TableDrawer(TXT_UNCAST_ARG(table))
 {
     TXT_CAST_ARG(txt_table_t, table);
     txt_widget_t *widget;
-    int selected_cell;
     int i;
-    
+
     // Check the table's current selection points at something valid before
     // drawing.
 
     CheckValidSelection(table);
 
-    // Find the index of the currently-selected widget.
-
-    selected_cell = table->selected_y * table->columns + table->selected_x;
-    
     // Draw all cells
-    
+
     for (i=0; i<table->num_widgets; ++i)
     {
         widget = table->widgets[i];
@@ -528,7 +562,7 @@
         if (widget != NULL)
         {
             TXT_GotoXY(widget->x, widget->y);
-            TXT_DrawWidget(widget, selected && i == selected_cell);
+            TXT_DrawWidget(widget);
         }
     }
 }
@@ -558,8 +592,8 @@
 
                 if (TXT_SelectableWidget(widget))
                 {
-                    table->selected_x = i % table->columns;
-                    table->selected_y = i / table->columns;
+                    ChangeSelection(table, i % table->columns,
+                                           i / table->columns);
                 }
 
                 // Propagate click
@@ -593,8 +627,7 @@
         if (table->widgets[i] != NULL
          && TXT_SelectableWidget(table->widgets[i]))
         {
-            table->selected_x = i % table->columns;
-            table->selected_y = i / table->columns;
+            ChangeSelection(table, i % table->columns, i / table->columns);
             return 1;
         }
     }
@@ -604,6 +637,24 @@
     return 0;
 }
 
+// Need to pass through focus changes to the selected child widget.
+
+static void TXT_TableFocused(TXT_UNCAST_ARG(table), int focused)
+{
+    TXT_CAST_ARG(txt_table_t, table);
+    int i;
+
+    i = table->selected_y * table->columns + table->selected_x;
+
+    if (i < table->num_widgets)
+    {
+        if (table->widgets[i] != NULL)
+        {
+            TXT_SetWidgetFocus(table->widgets[i], focused);
+        }
+    }
+}
+
 txt_widget_class_t txt_table_class =
 {
     TXT_TableSelectable,
@@ -613,6 +664,7 @@
     TXT_TableDestructor,
     TXT_TableMousePress,
     TXT_TableLayout,
+    TXT_TableFocused,
 };
 
 void TXT_InitTable(txt_table_t *table, int columns)
@@ -757,17 +809,16 @@
         {
             continue;
         }
-        
+
         if (table->widgets[i] == widget)
         {
             // Found the item!  Select it and return.
-            
-            table->selected_x = i % table->columns;
-            table->selected_y = i / table->columns;
 
+            ChangeSelection(table, i % table->columns, i / table->columns);
+
             return 1;
         }
-        
+
         if (table->widgets[i]->widget_class == &txt_table_class)
         {
             // This item is a subtable.  Recursively search this table.
@@ -776,8 +827,7 @@
             {
                 // Found it in the subtable.  Select this subtable and return.
 
-                table->selected_x = i % table->columns;
-                table->selected_y = i / table->columns;
+                ChangeSelection(table, i % table->columns, i / table->columns);
 
                 return 1;
             }
@@ -874,8 +924,7 @@
                 // Found a selectable widget in this column!
                 // Select it anyway in case we don't find something better.
 
-                table->selected_x = new_x;
-                table->selected_y = new_y;
+                ChangeSelection(table, new_x, new_y);
                 changed = 1;
 
                 // ...but is it far enough away?
--- a/textscreen/txt_widget.c
+++ b/textscreen/txt_widget.c
@@ -86,6 +86,10 @@
     widget->callback_table = TXT_NewCallbackTable();
     widget->parent = NULL;
 
+    // Not focused until we hear otherwise.
+
+    widget->focused = 0;
+
     // Visible by default.
 
     widget->visible = 1;
@@ -155,7 +159,7 @@
     widget->widget_class->size_calc(widget);
 }
 
-void TXT_DrawWidget(TXT_UNCAST_ARG(widget), int selected)
+void TXT_DrawWidget(TXT_UNCAST_ARG(widget))
 {
     TXT_CAST_ARG(txt_widget_t, widget);
 
@@ -165,7 +169,7 @@
 
     // Call drawer method
  
-    widget->widget_class->drawer(widget, selected);
+    widget->widget_class->drawer(widget);
 }
 
 void TXT_DestroyWidget(TXT_UNCAST_ARG(widget))
@@ -189,6 +193,26 @@
     return 0;
 }
 
+void TXT_SetWidgetFocus(TXT_UNCAST_ARG(widget), int focused)
+{
+    TXT_CAST_ARG(txt_widget_t, widget);
+
+    if (widget == NULL)
+    {
+        return;
+    }
+
+    if (widget->focused != focused)
+    {
+        widget->focused = focused;
+
+        if (widget->widget_class->focus_change != NULL)
+        {
+            widget->widget_class->focus_change(widget, focused);
+        }
+    }
+}
+
 void TXT_SetWidgetAlign(TXT_UNCAST_ARG(widget), txt_horiz_align_t horiz_align)
 {
     TXT_CAST_ARG(txt_widget_t, widget);
@@ -281,11 +305,11 @@
          && y >= widget->y && y < widget->y + widget->h);
 }
 
-void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget), int selected)
+void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget))
 {
     TXT_CAST_ARG(txt_widget_t, widget);
 
-    if (selected)
+    if (widget->focused)
     {
         TXT_BGColor(TXT_COLOR_GREY, 0);
     }
--- a/textscreen/txt_widget.h
+++ b/textscreen/txt_widget.h
@@ -71,7 +71,7 @@
 typedef struct txt_callback_table_s txt_callback_table_t;
 
 typedef void (*TxtWidgetSizeCalc)(TXT_UNCAST_ARG(widget));
-typedef void (*TxtWidgetDrawer)(TXT_UNCAST_ARG(widget), int selected);
+typedef void (*TxtWidgetDrawer)(TXT_UNCAST_ARG(widget));
 typedef void (*TxtWidgetDestroy)(TXT_UNCAST_ARG(widget));
 typedef int (*TxtWidgetKeyPress)(TXT_UNCAST_ARG(widget), int key);
 typedef void (*TxtWidgetSignalFunc)(TXT_UNCAST_ARG(widget), void *user_data);
@@ -78,6 +78,7 @@
 typedef void (*TxtMousePressFunc)(TXT_UNCAST_ARG(widget), int x, int y, int b);
 typedef void (*TxtWidgetLayoutFunc)(TXT_UNCAST_ARG(widget));
 typedef int (*TxtWidgetSelectableFunc)(TXT_UNCAST_ARG(widget));
+typedef void (*TxtWidgetFocusFunc)(TXT_UNCAST_ARG(widget), int focused);
 
 struct txt_widget_class_s
 {
@@ -88,6 +89,7 @@
     TxtWidgetDestroy destructor;
     TxtMousePressFunc mouse_press;
     TxtWidgetLayoutFunc layout;
+    TxtWidgetFocusFunc focus_change;
 };
 
 struct txt_widget_s
@@ -96,6 +98,7 @@
     txt_callback_table_t *callback_table;
     int visible;
     txt_horiz_align_t align;
+    int focused;
 
     // These are set automatically when the window is drawn and should
     // not be set manually.
@@ -110,7 +113,7 @@
 
 void TXT_InitWidget(TXT_UNCAST_ARG(widget), txt_widget_class_t *widget_class);
 void TXT_CalcWidgetSize(TXT_UNCAST_ARG(widget));
-void TXT_DrawWidget(TXT_UNCAST_ARG(widget), int selected);
+void TXT_DrawWidget(TXT_UNCAST_ARG(widget));
 void TXT_EmitSignal(TXT_UNCAST_ARG(widget), const char *signal_name);
 int TXT_WidgetKeyPress(TXT_UNCAST_ARG(widget), int key);
 void TXT_WidgetMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b);
@@ -118,6 +121,7 @@
 void TXT_LayoutWidget(TXT_UNCAST_ARG(widget));
 int TXT_AlwaysSelectable(TXT_UNCAST_ARG(widget));
 int TXT_NeverSelectable(TXT_UNCAST_ARG(widget));
+void TXT_SetWidgetFocus(TXT_UNCAST_ARG(widget), int focused);
 
 /**
  * Set a callback function to be invoked when a signal occurs.
@@ -167,7 +171,7 @@
  * @param selected     Whether the widget is selected.
  */
 
-void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget), int selected);
+void TXT_SetWidgetBG(TXT_UNCAST_ARG(widget));
 
 /**
  * Query whether the specified widget is contained within another
--- a/textscreen/txt_window.c
+++ b/textscreen/txt_window.c
@@ -97,6 +97,7 @@
     int i;
 
     TXT_EmitSignal(window, "closed");
+    TXT_RemoveDesktopWindow(window);
 
     free(window->title);
 
@@ -113,8 +114,6 @@
     // Destroy table and window
 
     TXT_DestroyWidget(window);
-    
-    TXT_RemoveDesktopWindow(window);
 }
 
 static void CalcWindowPosition(txt_window_t *window)
@@ -218,7 +217,7 @@
     {
         if (window->actions[i] != NULL)
         {
-            TXT_DrawWidget(window->actions[i], 0);
+            TXT_DrawWidget(window->actions[i]);
         }
     }
 }
@@ -317,7 +316,7 @@
     TXT_LayoutWidget(widgets);
 }
 
-void TXT_DrawWindow(txt_window_t *window, int selected)
+void TXT_DrawWindow(txt_window_t *window)
 {
     txt_widget_t *widgets;
 
@@ -331,7 +330,7 @@
 
     // Draw all widgets
 
-    TXT_DrawWidget(window, selected);
+    TXT_DrawWidget(window);
 
     // Draw an action area, if we have one
 
@@ -410,7 +409,21 @@
          && x >= widget->x && x < (signed) (widget->x + widget->w)
          && y >= widget->y && y < (signed) (widget->y + widget->h))
         {
+            int was_focused;
+
+            // Main table temporarily loses focus when action area button
+            // is clicked. This way, any active input boxes that depend
+            // on having focus will save their values before the
+            // action is performed.
+
+            was_focused = window->table.widget.focused;
+            TXT_SetWidgetFocus(window, 0);
+            TXT_SetWidgetFocus(window, was_focused);
+
+            // Pass through mouse press.
+
             TXT_WidgetMousePress(widget, x, y, b);
+
             break;
         }
     }
@@ -472,6 +485,11 @@
 {
     window->mouse_listener = mouse_listener;
     window->mouse_listener_data = user_data;
+}
+
+void TXT_SetWindowFocus(txt_window_t *window, int focused)
+{
+    TXT_SetWidgetFocus(window, focused);
 }
 
 txt_window_t *TXT_MessageBox(char *title, char *message, ...)
--- a/textscreen/txt_window_action.c
+++ b/textscreen/txt_window_action.c
@@ -44,7 +44,7 @@
     action->widget.h = 1;
 }
 
-static void TXT_WindowActionDrawer(TXT_UNCAST_ARG(action), int selected)
+static void TXT_WindowActionDrawer(TXT_UNCAST_ARG(action))
 {
     TXT_CAST_ARG(txt_window_action_t, action);
     char buf[10];