fix stopped-chat wordwrap problem

This commit is contained in:
David Rose 2004-04-30 20:58:21 +00:00
parent 7786bc0915
commit 382f0527eb
9 changed files with 241 additions and 200 deletions

View File

@ -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.
////////////////////////////////////////////////////////////////////

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -250,95 +250,6 @@ press(const MouseWatcherParameter &param, 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);
}
}
}
}
}
}
@ -358,95 +269,146 @@ keystroke(const MouseWatcherParameter &param, bool background) {
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()) {
// 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);
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);
// 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 {
_cursor_position = min(_cursor_position, (int)_wtext.length());
wstring new_text =
_wtext.substr(0, _cursor_position) + new_char +
_wtext.substr(_cursor_position);
measure_text = new_text;
}
// 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;
// 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);
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.
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;
// 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);
current_pos++;
}
} 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;
// 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);
}
}
}

View File

@ -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);
}

View File

@ -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++;
}
}
}