Sfoglia il codice sorgente

fix stopped-chat wordwrap problem

David Rose 21 anni fa
parent
commit
382f0527eb

+ 23 - 23
panda/src/framework/pandaFramework.cxx

@@ -99,6 +99,8 @@ open_framework(int &argc, char **&argv) {
   _is_open = true;
   _is_open = true;
 
 
   reset_frame_rate();
   reset_frame_rate();
+
+  _event_handler.add_hook("window-event", event_window_event, this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -781,8 +783,6 @@ do_enable_default_keys() {
   define_key(",", "change background color", event_comma, this);
   define_key(",", "change background color", event_comma, this);
   define_key("?", "", event_question, this);
   define_key("?", "", event_question, this);
   define_key("shift-/", "", event_question, this);
   define_key("shift-/", "", event_question, this);
-
-  _event_handler.add_hook("window-event", event_window_event, this);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -810,7 +810,7 @@ clear_text() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_esc
 //     Function: PandaFramework::event_esc
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for ESC or q key: close the current
 //  Description: Default handler for ESC or q key: close the current
 //               window (and exit the application if that was the last
 //               window (and exit the application if that was the last
 //               window).
 //               window).
@@ -846,7 +846,7 @@ event_esc(CPT_Event event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_f
 //     Function: PandaFramework::event_f
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for f key: report and reset frame
 //  Description: Default handler for f key: report and reset frame
 //               rate.
 //               rate.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -859,7 +859,7 @@ event_f(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_w
 //     Function: PandaFramework::event_w
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for w key: toggle wireframe.
 //  Description: Default handler for w key: toggle wireframe.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -875,7 +875,7 @@ event_w(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_t
 //     Function: PandaFramework::event_t
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for t key: toggle texture.
 //  Description: Default handler for t key: toggle texture.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -891,7 +891,7 @@ event_t(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_b
 //     Function: PandaFramework::event_b
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for b key: toggle backface (two-sided
 //  Description: Default handler for b key: toggle backface (two-sided
 //               rendering).
 //               rendering).
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -908,7 +908,7 @@ event_b(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_i
 //     Function: PandaFramework::event_i
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for i key: invert one-sided faces.
 //  Description: Default handler for i key: invert one-sided faces.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -924,7 +924,7 @@ event_i(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_l
 //     Function: PandaFramework::event_l
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for l key: toggle lighting.
 //  Description: Default handler for l key: toggle lighting.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -940,7 +940,7 @@ event_l(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_c
 //     Function: PandaFramework::event_c
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for c key: center the trackball over
 //  Description: Default handler for c key: center the trackball over
 //               the scene, or over the highlighted part of the scene.
 //               the scene, or over the highlighted part of the scene.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -963,7 +963,7 @@ event_c(CPT_Event event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_C
 //     Function: PandaFramework::event_C
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for shift-C key: toggle the showing
 //  Description: Default handler for shift-C key: toggle the showing
 //               of collision solids.
 //               of collision solids.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -983,7 +983,7 @@ event_C(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_B
 //     Function: PandaFramework::event_B
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for shift-B key: describe the
 //  Description: Default handler for shift-B key: describe the
 //               bounding volume of the currently selected object, or
 //               bounding volume of the currently selected object, or
 //               the entire scene.
 //               the entire scene.
@@ -1002,7 +1002,7 @@ event_B(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_L
 //     Function: PandaFramework::event_L
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for shift-L key: list the contents of
 //  Description: Default handler for shift-L key: list the contents of
 //               the scene graph, or the highlighted node.
 //               the scene graph, or the highlighted node.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1020,7 +1020,7 @@ event_L(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_h
 //     Function: PandaFramework::event_h
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for h key: toggle highlight mode.  In
 //  Description: Default handler for h key: toggle highlight mode.  In
 //               this mode, you can walk the scene graph with the
 //               this mode, you can walk the scene graph with the
 //               arrow keys to highlight different nodes.
 //               arrow keys to highlight different nodes.
@@ -1038,7 +1038,7 @@ event_h(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_arrow_up
 //     Function: PandaFramework::event_arrow_up
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for up arrow key: in highlight mode,
 //  Description: Default handler for up arrow key: in highlight mode,
 //               move the highlight to the node's parent.
 //               move the highlight to the node's parent.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1056,7 +1056,7 @@ event_arrow_up(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_arrow_down
 //     Function: PandaFramework::event_arrow_down
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for up arrow key: in highlight mode,
 //  Description: Default handler for up arrow key: in highlight mode,
 //               move the highlight to the node's first child.
 //               move the highlight to the node's first child.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1074,7 +1074,7 @@ event_arrow_down(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_arrow_left
 //     Function: PandaFramework::event_arrow_left
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for up arrow key: in highlight mode,
 //  Description: Default handler for up arrow key: in highlight mode,
 //               move the highlight to the node's nearest sibling on
 //               move the highlight to the node's nearest sibling on
 //               the left.
 //               the left.
@@ -1106,7 +1106,7 @@ event_arrow_left(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_arrow_right
 //     Function: PandaFramework::event_arrow_right
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for up arrow key: in highlight mode,
 //  Description: Default handler for up arrow key: in highlight mode,
 //               move the highlight to the node's nearest sibling on
 //               move the highlight to the node's nearest sibling on
 //               the right.
 //               the right.
@@ -1139,7 +1139,7 @@ event_arrow_right(CPT_Event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_S
 //     Function: PandaFramework::event_S
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for shift-S key: activate stats.
 //  Description: Default handler for shift-S key: activate stats.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -1154,7 +1154,7 @@ event_S(CPT_Event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_f9
 //     Function: PandaFramework::event_f9
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for f9 key: take screenshot.
 //  Description: Default handler for f9 key: take screenshot.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -1196,7 +1196,7 @@ event_f9(CPT_Event event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_comma
 //     Function: PandaFramework::event_comma
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for comma key: rotate background color.
 //  Description: Default handler for comma key: rotate background color.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -1222,7 +1222,7 @@ event_comma(CPT_Event event, void *) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_question
 //     Function: PandaFramework::event_question
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for ? key: show the available keys.
 //  Description: Default handler for ? key: show the available keys.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PandaFramework::
 void PandaFramework::
@@ -1277,7 +1277,7 @@ event_question(CPT_Event event, void *data) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaFramework::event_window_event
 //     Function: PandaFramework::event_window_event
-//       Access: Protected, Static
+//       Access: Public, Static
 //  Description: Default handler for window events: window resized or
 //  Description: Default handler for window events: window resized or
 //               closed, etc.
 //               closed, etc.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/framework/pandaFramework.h

@@ -119,6 +119,7 @@ protected:
   virtual void do_enable_default_keys();
   virtual void do_enable_default_keys();
   bool clear_text();
   bool clear_text();
 
 
+public:
   static void event_esc(CPT_Event, void *data);
   static void event_esc(CPT_Event, void *data);
   static void event_f(CPT_Event, void *data);
   static void event_f(CPT_Event, void *data);
   static void event_w(CPT_Event, void *data);
   static void event_w(CPT_Event, void *data);

+ 12 - 1
panda/src/framework/windowFramework.cxx

@@ -307,7 +307,15 @@ get_render_2d() {
 const NodePath &WindowFramework::
 const NodePath &WindowFramework::
 get_aspect_2d() {
 get_aspect_2d() {
   if (_aspect_2d.is_empty()) {
   if (_aspect_2d.is_empty()) {
-    _aspect_2d = get_render_2d().attach_new_node(new PGTop("aspect_2d"));
+    PGTop *top = new PGTop("aspect_2d");
+    _aspect_2d = get_render_2d().attach_new_node(top);
+
+    // Tell the PGTop about our MouseWatcher object, so the PGui
+    // system can operate.
+    PandaNode *mouse_node = get_mouse().node();
+    if (mouse_node->is_of_type(MouseWatcher::get_class_type())) {
+      top->set_mouse_watcher(DCAST(MouseWatcher, mouse_node));
+    }
 
 
     float this_aspect_ratio = aspect_ratio;
     float this_aspect_ratio = aspect_ratio;
     if (this_aspect_ratio == 0.0f) {
     if (this_aspect_ratio == 0.0f) {
@@ -346,6 +354,9 @@ get_mouse() {
     // display region, if we have one.  This means the node we return
     // display region, if we have one.  This means the node we return
     // from get_mouse() is actually a MouseWatcher, but since it
     // from get_mouse() is actually a MouseWatcher, but since it
     // presents the same interface as a Mouse, no one should mind.
     // presents the same interface as a Mouse, no one should mind.
+
+    // Another advantage to using a MouseWatcher is that it the PGTop
+    // of aspect2d likes it better.
     PT(MouseWatcher) mw = new MouseWatcher("watcher");
     PT(MouseWatcher) mw = new MouseWatcher("watcher");
     mw->set_display_region(_display_region_3d);
     mw->set_display_region(_display_region_3d);
     _mouse = mouse.attach_new_node(mw);
     _mouse = mouse.attach_new_node(mw);

+ 18 - 0
panda/src/pgui/Sources.pp

@@ -55,3 +55,21 @@
 
 
 #end lib_target
 #end lib_target
 
 
+
+#begin test_bin_target
+  #define TARGET test_pgentry
+
+  #define OTHER_LIBS $[OTHER_LIBS] pystub
+
+  #define LOCAL_LIBS \
+    framework putil collide pgraph chan text \
+    pnmimage pnmimagetypes event effects gobj display \
+    mathutil putil express dgraph device tform \
+    linmath pstatclient panda
+
+  #define UNIX_SYS_LIBS m
+
+  #define SOURCES \
+    test_pgentry.cxx
+
+#end test_bin_target

+ 0 - 4
panda/src/pgui/config_pgui.cxx

@@ -42,10 +42,6 @@ ConfigureFn(config_pgui) {
 // powerful but somewhat slower.
 // powerful but somewhat slower.
 const bool pgui_quick = config_pgui.GetBool("pgui-quick", true);
 const bool pgui_quick = config_pgui.GetBool("pgui-quick", true);
 
 
-// Temporary variable to support old-style button press/release for
-// pgentries, before keystrokes were implemented.
-const bool use_keystrokes = config_pgui.GetBool("use-keystrokes", true);
-
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libpgui
 //     Function: init_libpgui

+ 0 - 1
panda/src/pgui/config_pgui.h

@@ -26,7 +26,6 @@ NotifyCategoryDecl(pgui, EXPCL_PANDA, EXPTP_PANDA);
 
 
 // Configure variables for pgui package.
 // Configure variables for pgui package.
 extern const bool pgui_quick;
 extern const bool pgui_quick;
-extern const bool use_keystrokes;
 
 
 extern EXPCL_PANDA void init_libpgui();
 extern EXPCL_PANDA void init_libpgui();
 
 

+ 129 - 167
panda/src/pgui/pgEntry.cxx

@@ -250,96 +250,7 @@ press(const MouseWatcherParameter &param, bool background) {
             _cursor_position = _wtext.length();
             _cursor_position = _wtext.length();
             _cursor_stale = true;
             _cursor_stale = true;
           }
           }
-          
-        } else if (!use_keystrokes && button.has_ascii_equivalent()) {
-          // This part of the code is deprecated and will be removed
-          // soon.  It only supports the old button up/down method of
-          // sending keystrokes, instead of the new keystroke method.
-          wchar_t key = button.get_ascii_equivalent();
-          if (isprint(key)) {
-            // A normal visible character.  Add a new character to the
-            // text entry, if there's room.
-            
-            if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) {
-              overflow(param);
-            } else {
-              wstring new_text = 
-                _wtext.substr(0, _cursor_position) + key +
-                _wtext.substr(_cursor_position);
-
-              // Get a string to measure its length.  In normal mode,
-              // we measure the text itself.  In obscure mode, we
-              // measure a string of n asterisks.
-              wstring measure_text;
-              if (_obscure_mode) {
-                measure_text = get_display_wtext() + (wchar_t)'*';
-              } else {
-                measure_text = new_text;
-              }
-              
-              // Check the length.
-              bool too_long = false;
-              if (_max_width > 0.0f) {
-                TextNode *text_node = get_text_def(S_focus);
-                text_node->set_wtext(measure_text);
-                text_node->set_wordwrap(_max_width);
-                text_node->set_preserve_trailing_whitespace(true);
-                text_node->set_max_rows(_num_lines);
-
-                too_long = text_node->has_overflow();
-
-                if (!too_long) {
-                  // We must also ensure that the last line is not too
-                  // long (it might be, because of additional
-                  // whitespace on the end).
-                  wstring ww_text = text_node->get_wordwrapped_wtext();
-                  size_t last_line_start = ww_text.rfind('\n');
-                  if (last_line_start == string::npos) {
-                    last_line_start = 0;
-                  }
-                  wstring last_line = ww_text.substr(last_line_start);
-                  float last_line_width = text_node->calc_width(last_line);
-                  if (text_node->get_num_rows() == _num_lines) {
-                    // Mainly we only care about this if we're on
-                    // the very last line.
-                    too_long = (last_line_width > _max_width);
-                    
-                  } else {
-                    // If we're not on the very last line, the width
-                    // is still important, just so we don't allow an
-                    // infinite number of spaces to accumulate.
-                    // However, we must allow at least *one* space
-                    // on the end of a line.
-                    if (_wtext.length() >= 1 && 
-                        _wtext[_wtext.length() - 1] == ' ') {
-                      if (last_line_width > _max_width) {
-                        // In this case, however, it's not exactly
-                        // an overflow; we just want to reject the
-                        // space.
-                        return;
-                      }
-                    }
-                  }
-                }
-              }
-              
-              if (too_long) {
-                overflow(param);
-                
-              } else {
-                _wtext = new_text;
-                if (_obscure_mode) {
-                  _obscured_wtext = measure_text;
-                }
-                
-                _cursor_position++;
-                _cursor_stale = true;
-                _text_geom_stale = true;
-                type(param);
-              }
-            }
-          }
-        }
+        }          
       }
       }
     }
     }
   }
   }
@@ -357,96 +268,147 @@ keystroke(const MouseWatcherParameter &param, bool background) {
   if (get_active()) {
   if (get_active()) {
     if (param.has_keycode()) {
     if (param.has_keycode()) {
       int keycode = param.get_keycode();
       int keycode = param.get_keycode();
-          
-      if (use_keystrokes) {
-
-        if (!isascii(keycode) || isprint(keycode)) {
-          // A normal visible character.  Add a new character to the
-          // text entry, if there's room.
-          if (!_candidate_wtext.empty()) {
-            _candidate_wtext.clear();
-            _text_geom_stale = true;
-          }
-          wstring new_char(1, (wchar_t)keycode);
 
 
-          if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) {
-            overflow(param);
-          } else {
-            _cursor_position = min(_cursor_position, (int)_wtext.length());
-            wstring new_text = 
-              _wtext.substr(0, _cursor_position) + new_char +
-              _wtext.substr(_cursor_position);
+      if (!isascii(keycode) || isprint(keycode)) {
+        // A normal visible character.  Add a new character to the
+        // text entry, if there's room.
+        if (!_candidate_wtext.empty()) {
+          _candidate_wtext.clear();
+          _text_geom_stale = true;
+        }
+        wstring new_char(1, (wchar_t)keycode);
+
+        if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) {
+          // In max_chars mode, we consider it an overflow after we
+          // have exceeded a fixed number of characters, irrespective
+          // of the formatted width of those characters.
+          overflow(param);
+
+        } else {
+          _cursor_position = min(_cursor_position, (int)_wtext.length());
+          wstring new_text = 
+            _wtext.substr(0, _cursor_position) + new_char +
+            _wtext.substr(_cursor_position);
             
             
-            // Get a string to measure its length.  In normal mode,
-            // we measure the text itself.  In obscure mode, we
-            // measure a string of n asterisks.
-            wstring measure_text;
-            if (_obscure_mode) {
-              measure_text = get_display_wtext() + (wchar_t)'*';
-            } else {
-              measure_text = new_text;
-            }
+          // Get the new string to measure its length.  In normal
+          // mode, we measure the text itself.  In obscure mode, we
+          // measure a string of n asterisks.
+          wstring measure_text;
+          if (_obscure_mode) {
+            measure_text = get_display_wtext() + (wchar_t)'*';
+          } else {
+            measure_text = new_text;
+          }
 
 
-            // Check the length.
-            bool too_long = false;
-            if (_max_width > 0.0f) {
-              TextNode *text_node = get_text_def(S_focus);
-              text_node->set_wtext(measure_text);
-              text_node->set_wordwrap(_max_width);
-              text_node->set_preserve_trailing_whitespace(true);
-              text_node->set_max_rows(_num_lines);
+          // Now check the length.
+          bool too_long = false;
+          if (_max_width > 0.0f) {
+            TextNode *text_node = get_text_def(S_focus);
+            text_node->set_wtext(measure_text);
+            text_node->set_wordwrap(_max_width);
+            text_node->set_preserve_trailing_whitespace(true);
+            text_node->set_max_rows(_num_lines);
               
               
-              too_long = text_node->has_overflow();
+            too_long = text_node->has_overflow();
               
               
-              if (!too_long) {
-                // We must also ensure that the last line is not too
-                // long (it might be, because of additional
-                // whitespace on the end).
-                wstring ww_text = text_node->get_wordwrapped_wtext();
-                size_t last_line_start = ww_text.rfind('\n');
-                if (last_line_start == string::npos) {
-                  last_line_start = 0;
+            if (!too_long && (text_node->get_num_rows() == _num_lines)) {
+              // If we've filled up all of the available lines, we
+              // must also ensure that the last line is not too long
+              // (it might be, because of additional whitespace on
+              // the end).
+              wstring ww_text = text_node->get_wordwrapped_wtext();
+              size_t last_line_start = ww_text.rfind('\n');
+              if (last_line_start == string::npos) {
+                last_line_start = 0;
+              }
+              wstring last_line = ww_text.substr(last_line_start);
+              float last_line_width = text_node->calc_width(last_line);
+              
+              too_long = (last_line_width > _max_width);
+            }
+
+            if (!too_long && keycode == ' ') {
+              // Even if we haven't filled up all of the available
+              // lines, we should reject a space that's typed at the
+              // end of the current line if it would make that line
+              // exceed the maximum width, just so we don't allow an
+              // infinite number of spaces to accumulate.
+
+              // First, we need to figure out our current position
+              // within the wordwrapped text, by skipping past the
+              // newlines.
+              wstring ww_text = text_node->get_wordwrapped_wtext();
+              int i = 0;
+              int current_pos = 0;
+              while (i < _cursor_position) {
+                nassertv(current_pos < (int)ww_text.length());
+                if (ww_text[current_pos] != '\n') {
+                  i++;
                 }
                 }
-                wstring last_line = ww_text.substr(last_line_start);
-                float last_line_width = text_node->calc_width(last_line);
-                if (text_node->get_num_rows() == _num_lines) {
-                  // Mainly we only care about this if we're on
-                  // the very last line.
-                  too_long = (last_line_width > _max_width);
-                  
-                } else {
-                  // If we're not on the very last line, the width
-                  // is still important, just so we don't allow an
-                  // infinite number of spaces to accumulate.
-                  // However, we must allow at least *one* space
-                  // on the end of a line.
-                  if (_wtext.length() >= 1 && 
-                      _wtext[_wtext.length() - 1] == ' ') {
-                    if (last_line_width > _max_width) {
-                      // In this case, however, it's not exactly
-                      // an overflow; we just want to reject the
-                      // space.
-                      return;
+                current_pos++;
+              }
+
+              // Is the user typing at the end of the line?  Scan for
+              // the next character that's not a space following the
+              // current position.
+              int p = current_pos + 1;
+              while (p < (int)ww_text.length() && ww_text[p] == ' ') {
+                p++;
+              }
+
+              if (p >= (int)ww_text.length() || ww_text[p] == '\n') {
+                // The user is typing at the end of the line.  But we
+                // must allow at least one space at the end of the
+                // line, so we only make any of the following checks
+                // if there are already multiple spaces at the end of
+                // the line.
+                if (p - 2 >= 0 && ww_text[p - 2] == ' ') {
+                  // Ok, the user is putting multiple spaces on the
+                  // end of a line; we need to make sure the line does
+                  // not grow too wide.  Get the beginning of the line
+                  // and measure its width.
+                  int q = current_pos;
+                  while (q >= 0 && ww_text[q] != '\n') {
+                    q--;
+                  }
+
+                  wstring current_line = ww_text.substr(q + 1, p - (q + 1));
+                  float current_line_width = text_node->calc_width(current_line);
+
+                  if (current_line_width > _max_width) {
+                    // We have to reject the space, but we don't treat
+                    // it as an overflow condition.  
+
+                    // If the user is typing over existing space
+                    // characters, we act as if the right-arrow key
+                    // were pressed instead, and advance the cursor to
+                    // the next position.  Otherwise, we just quietly
+                    // eat the space character.
+                    if (_cursor_position < (int)_wtext.length() && 
+                        _wtext[_cursor_position] == ' ') {
+                      _cursor_position++;
+                      _cursor_stale = true;
                     }
                     }
+                    return;
                   }
                   }
                 }
                 }
               }
               }
             }
             }
-
-            if (too_long) {
-              overflow(param);
-              
-            } else {
-              _wtext = new_text;
-              if (_obscure_mode) {
-                _obscured_wtext = measure_text;
-              }
+          }
+          
+          if (too_long) {
+            overflow(param);
               
               
-              _cursor_position += new_char.length();
-              _cursor_stale = true;
-              _text_geom_stale = true;
-              type(param);
+          } else {
+            _wtext = new_text;
+            if (_obscure_mode) {
+              _obscured_wtext = measure_text;
             }
             }
+              
+            _cursor_position += new_char.length();
+            _cursor_stale = true;
+            _text_geom_stale = true;
+            type(param);
           }
           }
         }
         }
       }
       }

+ 48 - 0
panda/src/pgui/test_pgentry.cxx

@@ -0,0 +1,48 @@
+// Filename: test_pgentry.cxx
+// Created by:  drose (30Apr04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandaFramework.h"
+#include "pgEntry.h"
+
+PandaFramework framework;
+
+int
+main(int argc, char *argv[]) {
+  framework.open_framework(argc, argv);
+  framework.set_window_title("Panda Viewer");
+
+  WindowFramework *window = framework.open_window();
+  if (window != (WindowFramework *)NULL) {
+    window->enable_keyboard();
+
+    NodePath aspect2d = window->get_aspect_2d();
+    PGEntry *entry = new PGEntry("entry");
+    NodePath entry_np = aspect2d.attach_new_node(entry);
+    entry_np.set_scale(0.1);
+    entry_np.set_pos(-0.5, 0, 0.2);
+
+    entry->setup(10, 4);
+
+    framework.define_key("escape", "close window", 
+                         PandaFramework::event_esc, &framework);
+
+    framework.main_loop();
+  }
+
+  return (0);
+}

+ 10 - 4
panda/src/text/textAssembler.cxx

@@ -565,11 +565,17 @@ wordwrap_text(const TextAssembler::TextString &text,
       // No characters got in at all.  This could only happen if the
       // No characters got in at all.  This could only happen if the
       // wordwrap width is narrower than a single character, or if we
       // wordwrap width is narrower than a single character, or if we
       // have a substantial number of leading spaces in a line.
       // have a substantial number of leading spaces in a line.
-      q++;
-      next_start++;
-      while (next_start < text.size() && 
-             isbreakpoint(text[next_start]._character)) {
+
+      if (initial_width == 0.0f) {
+        // There was no leading whitespace on the line, so the
+        // character itself didn't fit within the margins.  Let it in
+        // anyway; what else can we do?
+        q++;
         next_start++;
         next_start++;
+        while (next_start < text.size() && 
+               isbreakpoint(text[next_start]._character)) {
+          next_start++;
+        }
       }
       }
     }
     }