diff --git a/panda/src/framework/pandaFramework.cxx b/panda/src/framework/pandaFramework.cxx index 0ab828c194..0aa369063a 100644 --- a/panda/src/framework/pandaFramework.cxx +++ b/panda/src/framework/pandaFramework.cxx @@ -99,6 +99,8 @@ open_framework(int &argc, char **&argv) { _is_open = true; 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("?", "", 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 -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for ESC or q key: close the current // window (and exit the application if that was the last // window). @@ -846,7 +846,7 @@ event_esc(CPT_Event event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_f -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for f key: report and reset frame // rate. //////////////////////////////////////////////////////////////////// @@ -859,7 +859,7 @@ event_f(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_w -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for w key: toggle wireframe. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -875,7 +875,7 @@ event_w(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_t -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for t key: toggle texture. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -891,7 +891,7 @@ event_t(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_b -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for b key: toggle backface (two-sided // rendering). //////////////////////////////////////////////////////////////////// @@ -908,7 +908,7 @@ event_b(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_i -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for i key: invert one-sided faces. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -924,7 +924,7 @@ event_i(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_l -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for l key: toggle lighting. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -940,7 +940,7 @@ event_l(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_c -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for c key: center the trackball over // 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 -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for shift-C key: toggle the showing // of collision solids. //////////////////////////////////////////////////////////////////// @@ -983,7 +983,7 @@ event_C(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_B -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for shift-B key: describe the // bounding volume of the currently selected object, or // the entire scene. @@ -1002,7 +1002,7 @@ event_B(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_L -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for shift-L key: list the contents of // the scene graph, or the highlighted node. //////////////////////////////////////////////////////////////////// @@ -1020,7 +1020,7 @@ event_L(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_h -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for h key: toggle highlight mode. In // this mode, you can walk the scene graph with the // arrow keys to highlight different nodes. @@ -1038,7 +1038,7 @@ event_h(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_arrow_up -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for up arrow key: in highlight mode, // move the highlight to the node's parent. //////////////////////////////////////////////////////////////////// @@ -1056,7 +1056,7 @@ event_arrow_up(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_arrow_down -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for up arrow key: in highlight mode, // 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 -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for up arrow key: in highlight mode, // move the highlight to the node's nearest sibling on // the left. @@ -1106,7 +1106,7 @@ event_arrow_left(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_arrow_right -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for up arrow key: in highlight mode, // move the highlight to the node's nearest sibling on // the right. @@ -1139,7 +1139,7 @@ event_arrow_right(CPT_Event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_S -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for shift-S key: activate stats. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -1154,7 +1154,7 @@ event_S(CPT_Event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_f9 -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for f9 key: take screenshot. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -1196,7 +1196,7 @@ event_f9(CPT_Event event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_comma -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for comma key: rotate background color. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -1222,7 +1222,7 @@ event_comma(CPT_Event event, void *) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_question -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for ? key: show the available keys. //////////////////////////////////////////////////////////////////// void PandaFramework:: @@ -1277,7 +1277,7 @@ event_question(CPT_Event event, void *data) { //////////////////////////////////////////////////////////////////// // Function: PandaFramework::event_window_event -// Access: Protected, Static +// Access: Public, Static // Description: Default handler for window events: window resized or // closed, etc. //////////////////////////////////////////////////////////////////// diff --git a/panda/src/framework/pandaFramework.h b/panda/src/framework/pandaFramework.h index d98a0d19df..c2eb04af4b 100644 --- a/panda/src/framework/pandaFramework.h +++ b/panda/src/framework/pandaFramework.h @@ -119,6 +119,7 @@ protected: virtual void do_enable_default_keys(); bool clear_text(); +public: static void event_esc(CPT_Event, void *data); static void event_f(CPT_Event, void *data); static void event_w(CPT_Event, void *data); diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index 7e022deff8..cbd1518b6f 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/panda/src/framework/windowFramework.cxx @@ -307,7 +307,15 @@ get_render_2d() { const NodePath &WindowFramework:: get_aspect_2d() { 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; if (this_aspect_ratio == 0.0f) { @@ -346,6 +354,9 @@ get_mouse() { // display region, if we have one. This means the node we return // from get_mouse() is actually a MouseWatcher, but since it // 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"); mw->set_display_region(_display_region_3d); _mouse = mouse.attach_new_node(mw); diff --git a/panda/src/pgui/Sources.pp b/panda/src/pgui/Sources.pp index 455f51deea..e8c367b597 100644 --- a/panda/src/pgui/Sources.pp +++ b/panda/src/pgui/Sources.pp @@ -55,3 +55,21 @@ #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 diff --git a/panda/src/pgui/config_pgui.cxx b/panda/src/pgui/config_pgui.cxx index 1ffe902ccf..33645f3586 100644 --- a/panda/src/pgui/config_pgui.cxx +++ b/panda/src/pgui/config_pgui.cxx @@ -42,10 +42,6 @@ ConfigureFn(config_pgui) { // powerful but somewhat slower. 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 diff --git a/panda/src/pgui/config_pgui.h b/panda/src/pgui/config_pgui.h index 77e7d994cb..c99a5109e2 100644 --- a/panda/src/pgui/config_pgui.h +++ b/panda/src/pgui/config_pgui.h @@ -26,7 +26,6 @@ NotifyCategoryDecl(pgui, EXPCL_PANDA, EXPTP_PANDA); // Configure variables for pgui package. extern const bool pgui_quick; -extern const bool use_keystrokes; extern EXPCL_PANDA void init_libpgui(); diff --git a/panda/src/pgui/pgEntry.cxx b/panda/src/pgui/pgEntry.cxx index d363088b20..4216150443 100644 --- a/panda/src/pgui/pgEntry.cxx +++ b/panda/src/pgui/pgEntry.cxx @@ -250,96 +250,7 @@ press(const MouseWatcherParameter ¶m, bool background) { _cursor_position = _wtext.length(); _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 ¶m, bool background) { if (get_active()) { if (param.has_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 (!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 (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; + } + + // 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(); + + 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); } - // 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; + 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); + } + + if (too_long) { + overflow(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); + } 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); } } } diff --git a/panda/src/pgui/test_pgentry.cxx b/panda/src/pgui/test_pgentry.cxx new file mode 100644 index 0000000000..78bcb32327 --- /dev/null +++ b/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 +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#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); +} diff --git a/panda/src/text/textAssembler.cxx b/panda/src/text/textAssembler.cxx index fad3af9bf6..9b7e29c648 100644 --- a/panda/src/text/textAssembler.cxx +++ b/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 // wordwrap width is narrower than a single character, or if we // 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++; + while (next_start < text.size() && + isbreakpoint(text[next_start]._character)) { + next_start++; + } } }