PGEntry supports embedded mode changes

This commit is contained in:
David Rose 2007-07-12 00:19:18 +00:00
parent 504df9ac3c
commit 278fe85c1a
12 changed files with 1286 additions and 468 deletions

View File

@ -32,6 +32,23 @@ set_text(const string &text) {
set_wtext(text_node->decode_text(text)); set_wtext(text_node->decode_text(text));
} }
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_plain_text
// Access: Published
// Description: Returns the text currently displayed within the
// entry, without any embedded properties characters.
//
// This uses the Unicode encoding currently specified
// for the "focus" TextNode; therefore, the TextNode
// must exist before calling get_text().
////////////////////////////////////////////////////////////////////
INLINE string PGEntry::
get_plain_text() const {
TextNode *text_node = get_text_def(S_focus);
nassertr(text_node != (TextNode *)NULL, string());
return text_node->encode_wtext(get_plain_wtext());
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_text // Function: PGEntry::get_text
// Access: Published // Access: Published
@ -47,6 +64,61 @@ get_text() const {
return text_node->encode_wtext(get_wtext()); return text_node->encode_wtext(get_wtext());
} }
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_num_characters
// Access: Published
// Description: Returns the number of characters of text in the
// entry. This is the actual number of visible
// characters, not counting implicit newlines due to
// wordwrapping, or formatted characters for text
// properties changes. If there is an embedded
// TextGraphic object, it counts as one character.
//
// This is also the length of the string returned by
// get_plain_text().
////////////////////////////////////////////////////////////////////
INLINE int PGEntry::
get_num_characters() const {
return _text.get_num_characters();
}
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_character
// Access: Published
// Description: Returns the character at the indicated position in
// the entry. If the object at this position is a
// graphic object instead of a character, returns 0.
////////////////////////////////////////////////////////////////////
INLINE wchar_t PGEntry::
get_character(int n) const {
return _text.get_character(n);
}
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_graphic
// Access: Published
// Description: Returns the graphic object at the indicated position
// in the pre-wordwrapped string. If the object at this
// position is a character instead of a graphic object,
// returns NULL.
////////////////////////////////////////////////////////////////////
INLINE const TextGraphic *PGEntry::
get_graphic(int n) const {
return _text.get_graphic(n);
}
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_properties
// Access: Published
// Description: Returns the TextProperties in effect for the object
// at the indicated position in the pre-wordwrapped
// string.
////////////////////////////////////////////////////////////////////
INLINE const TextProperties &PGEntry::
get_properties(int n) const {
return _text.get_properties(n);
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PGEntry::set_cursor_position // Function: PGEntry::set_cursor_position
// Access: Published // Access: Published
@ -431,11 +503,25 @@ get_erase_event() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE void PGEntry:: INLINE void PGEntry::
set_wtext(const wstring &wtext) { set_wtext(const wstring &wtext) {
_wtext = wtext; _text.set_wtext(wtext);
if (_obscure_mode) {
_obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
}
_text_geom_stale = true; _text_geom_stale = true;
_cursor_stale = true; _cursor_stale = true;
_blink_start = ClockObject::get_global_clock()->get_frame_time(); _blink_start = ClockObject::get_global_clock()->get_frame_time();
set_cursor_position(_wtext.size()); set_cursor_position(_text.get_num_characters());
}
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_plain_wtext
// Access: Published
// Description: Returns the text currently displayed within the
// entry, without any embedded properties characters.
////////////////////////////////////////////////////////////////////
INLINE wstring PGEntry::
get_plain_wtext() const {
return _text.get_plain_wtext();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -444,9 +530,9 @@ set_wtext(const wstring &wtext) {
// Description: Returns the text currently displayed within the // Description: Returns the text currently displayed within the
// entry. // entry.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE const wstring &PGEntry:: INLINE wstring PGEntry::
get_wtext() const { get_wtext() const {
return _wtext; return _text.get_wtext();
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -41,7 +41,9 @@ TypeHandle PGEntry::_type_handle;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
PGEntry:: PGEntry::
PGEntry(const string &name) : PGEntry(const string &name) :
PGItem(name) PGItem(name),
_text(get_text_node()),
_obscure_text(get_text_node())
{ {
set_cull_callback(); set_cull_callback();
@ -53,6 +55,7 @@ PGEntry(const string &name) :
_max_chars = 0; _max_chars = 0;
_max_width = 0.0f; _max_width = 0.0f;
_num_lines = 1; _num_lines = 1;
_accept_enabled = true;
_last_text_def = (TextNode *)NULL; _last_text_def = (TextNode *)NULL;
_text_geom_stale = true; _text_geom_stale = true;
_blink_start = 0.0f; _blink_start = 0.0f;
@ -93,8 +96,8 @@ PGEntry::
PGEntry:: PGEntry::
PGEntry(const PGEntry &copy) : PGEntry(const PGEntry &copy) :
PGItem(copy), PGItem(copy),
_wtext(copy._wtext), _text(copy._text),
_obscured_wtext(copy._obscured_wtext), _obscure_text(copy._obscure_text),
_cursor_position(copy._cursor_position), _cursor_position(copy._cursor_position),
_max_chars(copy._max_chars), _max_chars(copy._max_chars),
_max_width(copy._max_width), _max_width(copy._max_width),
@ -203,7 +206,7 @@ press(const MouseWatcherParameter &param, bool background) {
_text_geom_stale = true; _text_geom_stale = true;
} }
_cursor_position = min(_cursor_position, (int)_wtext.length()); _cursor_position = min(_cursor_position, _text.get_num_characters());
_blink_start = ClockObject::get_global_clock()->get_frame_time(); _blink_start = ClockObject::get_global_clock()->get_frame_time();
if (button == KeyboardButton::enter()) { if (button == KeyboardButton::enter()) {
// Enter. Accept the entry. // Enter. Accept the entry.
@ -214,10 +217,7 @@ press(const MouseWatcherParameter &param, bool background) {
} else if (button == KeyboardButton::backspace()) { } else if (button == KeyboardButton::backspace()) {
// Backspace. Remove the character to the left of the cursor. // Backspace. Remove the character to the left of the cursor.
if (_cursor_position > 0) { if (_cursor_position > 0) {
if (_obscure_mode && _obscured_wtext.length() == _wtext.length()) { _text.set_wsubstr(wstring(), _cursor_position - 1, 1);
_obscured_wtext.erase(_obscured_wtext.begin() + _obscured_wtext.length() - 1);
}
_wtext.erase(_wtext.begin() + _cursor_position - 1);
_cursor_position--; _cursor_position--;
_cursor_stale = true; _cursor_stale = true;
_text_geom_stale = true; _text_geom_stale = true;
@ -226,11 +226,8 @@ press(const MouseWatcherParameter &param, bool background) {
} else if (button == KeyboardButton::del()) { } else if (button == KeyboardButton::del()) {
// Delete. Remove the character to the right of the cursor. // Delete. Remove the character to the right of the cursor.
if (_cursor_position < (int)_wtext.length()) { if (_cursor_position < _text.get_num_characters()) {
if (_obscure_mode && _obscured_wtext.length() == _wtext.length()) { _text.set_wsubstr(wstring(), _cursor_position, 1);
_obscured_wtext.erase(_obscured_wtext.begin() + _obscured_wtext.length() - 1);
}
_wtext.erase(_wtext.begin() + _cursor_position);
_text_geom_stale = true; _text_geom_stale = true;
erase(param); erase(param);
} }
@ -245,7 +242,7 @@ press(const MouseWatcherParameter &param, bool background) {
} else if (button == KeyboardButton::right()) { } else if (button == KeyboardButton::right()) {
if (_cursor_keys_active) { if (_cursor_keys_active) {
// Right arrow. Move the cursor position to the right. // Right arrow. Move the cursor position to the right.
_cursor_position = min(_cursor_position + 1, (int)_wtext.length()); _cursor_position = min(_cursor_position + 1, _text.get_num_characters());
_cursor_stale = true; _cursor_stale = true;
} }
@ -259,7 +256,7 @@ press(const MouseWatcherParameter &param, bool background) {
} else if (button == KeyboardButton::end()) { } else if (button == KeyboardButton::end()) {
if (_cursor_keys_active) { if (_cursor_keys_active) {
// End. Move the cursor position to the end. // End. Move the cursor position to the end.
_cursor_position = _wtext.length(); _cursor_position = _text.get_num_characters();
_cursor_stale = true; _cursor_stale = true;
} }
} }
@ -290,52 +287,27 @@ keystroke(const MouseWatcherParameter &param, bool background) {
} }
wstring new_char(1, (wchar_t)keycode); wstring new_char(1, (wchar_t)keycode);
if (get_max_chars() > 0 && (int)_wtext.length() >= get_max_chars()) { if (get_max_chars() > 0 && _text.get_num_characters() >= get_max_chars()) {
// In max_chars mode, we consider it an overflow after we // In max_chars mode, we consider it an overflow after we
// have exceeded a fixed number of characters, irrespective // have exceeded a fixed number of characters, irrespective
// of the formatted width of those characters. // of the formatted width of those characters.
overflow(param); overflow(param);
} else { } else {
_cursor_position = min(_cursor_position, (int)_wtext.length()); _cursor_position = min(_cursor_position, _text.get_num_characters());
wstring new_text = bool too_long = !_text.set_wsubstr(new_char, _cursor_position, 0);
_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) { if (_obscure_mode) {
measure_text = get_display_wtext() + (wchar_t)'*'; too_long = !_obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
} else { } else {
measure_text = new_text; if (!too_long && (_text.get_num_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();
if (!too_long && (text_node->get_num_rows() == _num_lines)) {
// If we've filled up all of the available lines, we // If we've filled up all of the available lines, we
// must also ensure that the last line is not too long // must also ensure that the last line is not too long
// (it might be, because of additional whitespace on // (it might be, because of additional whitespace on
// the end). // the end).
wstring ww_text = text_node->get_wordwrapped_wtext(); int r = _num_lines - 1;
size_t last_line_start = ww_text.rfind('\n'); int c = _text.get_num_cols(r);
if (last_line_start == string::npos) { float last_line_width =
last_line_start = 0; _text.get_xpos(r, c) - _text.get_xpos(r, 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); too_long = (last_line_width > _max_width);
} }
@ -345,59 +317,31 @@ keystroke(const MouseWatcherParameter &param, bool background) {
// end of the current line if it would make that line // end of the current line if it would make that line
// exceed the maximum width, just so we don't allow an // exceed the maximum width, just so we don't allow an
// infinite number of spaces to accumulate. // infinite number of spaces to accumulate.
int r, c;
// First, we need to figure out our current position _text.calc_r_c(r, c, _cursor_position);
// within the wordwrapped text, by skipping past the if (_text.get_num_cols(r) == c + 1) {
// 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++;
}
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 // The user is typing at the end of the line. But we
// must allow at least one space at the end of the // must allow at least one space at the end of the
// line, so we only make any of the following checks // line, so we only make any of the following checks
// if there are already multiple spaces at the end of // if there are already multiple spaces at the end of
// the line. // the line.
if (p - 2 >= 0 && ww_text[p - 2] == ' ') { if (c - 1 >= 0 && _text.get_character(r, c - 1) == ' ') {
// Ok, the user is putting multiple spaces on the // Ok, the user is putting multiple spaces on the
// end of a line; we need to make sure the line does // end of a line; we need to make sure the line does
// not grow too wide. Get the beginning of the line // not grow too wide. Measure the line's width.
// and measure its width. float current_line_width =
int q = current_pos; _text.get_xpos(r, c + 1) - _text.get_xpos(r, 0);
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) { if (current_line_width > _max_width) {
// We have to reject the space, but we don't treat // We have to reject the space, but we don't treat
// it as an overflow condition. // it as an overflow condition.
_text.set_wsubstr(wstring(), _cursor_position, 1);
// If the user is typing over existing space // If the user is typing over existing space
// characters, we act as if the right-arrow key // characters, we act as if the right-arrow key
// were pressed instead, and advance the cursor to // were pressed instead, and advance the cursor to
// the next position. Otherwise, we just quietly // the next position. Otherwise, we just quietly
// eat the space character. // eat the space character.
if (_cursor_position < (int)_wtext.length() && if (_cursor_position < _text.get_num_characters() &&
_wtext[_cursor_position] == ' ') { _text.get_character(_cursor_position) == ' ') {
_cursor_position++; _cursor_position++;
_cursor_stale = true; _cursor_stale = true;
} }
@ -409,14 +353,10 @@ keystroke(const MouseWatcherParameter &param, bool background) {
} }
if (too_long) { if (too_long) {
_text.set_wsubstr(wstring(), _cursor_position, 1);
overflow(param); overflow(param);
} else { } else {
_wtext = new_text;
if (_obscure_mode) {
_obscured_wtext = measure_text;
}
_cursor_position += new_char.length(); _cursor_position += new_char.length();
_cursor_stale = true; _cursor_stale = true;
_text_geom_stale = true; _text_geom_stale = true;
@ -445,8 +385,9 @@ candidate(const MouseWatcherParameter &param, bool background) {
_candidate_highlight_end = param.get_highlight_end(); _candidate_highlight_end = param.get_highlight_end();
_candidate_cursor_pos = param.get_cursor_pos(); _candidate_cursor_pos = param.get_cursor_pos();
_text_geom_stale = true; _text_geom_stale = true;
if (!_candidate_wtext.empty()) if (!_candidate_wtext.empty()) {
type(param); type(param);
}
} }
} }
PGItem::candidate(param, background); PGItem::candidate(param, background);
@ -678,9 +619,9 @@ set_focus(bool focus) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
bool PGEntry:: bool PGEntry::
is_wtext() const { is_wtext() const {
wstring::const_iterator ti; for (int i = 0; i < _text.get_num_characters(); ++i) {
for (ti = _wtext.begin(); ti != _wtext.end(); ++ti) { wchar_t ch = _text.get_character(i);
if (((*ti) & ~0x7f) != 0) { if ((ch & ~0x7f) != 0) {
return true; return true;
} }
} }
@ -688,34 +629,6 @@ is_wtext() const {
return false; return false;
} }
////////////////////////////////////////////////////////////////////
// Function: PGEntry::get_display_wtext
// Access: Private
// Description: Returns the string that should be displayed within
// the entry. This is normally _wtext, but it may be
// _obscured_wtext.
////////////////////////////////////////////////////////////////////
const wstring &PGEntry::
get_display_wtext() {
if (_obscure_mode) {
// If obscure mode is enabled, we should just display a bunch of
// asterisks.
if (_obscured_wtext.length() != _wtext.length()) {
_obscured_wtext = wstring();
wstring::const_iterator ti;
for (ti = _wtext.begin(); ti != _wtext.end(); ++ti) {
_obscured_wtext += (wchar_t)'*';
}
}
return _obscured_wtext;
} else {
// In normal, non-obscure mode, we display the actual text
return _wtext;
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: PGEntry::slot_text_def // Function: PGEntry::slot_text_def
// Access: Private // Access: Private
@ -741,89 +654,56 @@ update_text() {
nassertv(node != (TextNode *)NULL); nassertv(node != (TextNode *)NULL);
if (_text_geom_stale || node != _last_text_def) { if (_text_geom_stale || node != _last_text_def) {
wstring display_wtext; TextProperties props = *node;
props.set_wordwrap(_max_width);
props.set_preserve_trailing_whitespace(true);
_text.set_properties(props);
_text.set_max_rows(_num_lines);
if (_candidate_wtext.empty() || _obscure_mode) { if (node != _last_text_def) {
// Make sure the default properties are applied to all the
// characters in the text.
_text.set_wtext(_text.get_wtext());
_last_text_def = node;
}
PT(PandaNode) assembled;
if (_obscure_mode) {
_obscure_text.set_properties(props);
_obscure_text.set_max_rows(_num_lines);
_obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
assembled = _obscure_text.assemble_text();
} else if (_candidate_wtext.empty()) {
// If we're not trying to display a candidate string, it's easy: // If we're not trying to display a candidate string, it's easy:
// just display the current text contents. // just display the current text contents.
display_wtext = get_display_wtext(); assembled = _text.assemble_text();
} else { } else {
// Insert the complex sequence of characters required to show TextPropertiesManager *tp_mgr = TextPropertiesManager::get_global_ptr();
// the candidate string in a different color. This gets TextProperties inactive = tp_mgr->get_properties(_candidate_inactive);
// inserted at the current cursor position. TextProperties active = tp_mgr->get_properties(_candidate_active);
wstring source_wtext = get_display_wtext();
display_wtext = source_wtext.substr(0, _cursor_position); // Create a special TextAssembler to insert the candidate string
display_wtext += wstring(1, (wchar_t)text_push_properties_key); // in its own special colors.
display_wtext += node->decode_text(_candidate_inactive);
display_wtext += wstring(1, (wchar_t)text_push_properties_key);
display_wtext += _candidate_wtext.substr(0, _candidate_highlight_start);
display_wtext += wstring(1, (wchar_t)text_push_properties_key);
display_wtext += node->decode_text(_candidate_active);
display_wtext += wstring(1, (wchar_t)text_push_properties_key);
display_wtext += _candidate_wtext.substr(_candidate_highlight_start,
_candidate_highlight_end - _candidate_highlight_start);
display_wtext += wstring(1, (wchar_t)text_pop_properties_key);
display_wtext += _candidate_wtext.substr(_candidate_highlight_end);
display_wtext += wstring(1, (wchar_t)text_pop_properties_key);
display_wtext += source_wtext.substr(_cursor_position); TextAssembler ctext(_text);
} ctext.set_wsubstr(_candidate_wtext.substr(_candidate_highlight_end),
_cursor_position, 0, inactive);
// We need to regenerate. ctext.set_wsubstr(_candidate_wtext.substr(_candidate_highlight_start,
_last_text_def = node; _candidate_highlight_end - _candidate_highlight_start),
_last_text_def->set_wtext(display_wtext); _cursor_position, 0, active);
_last_text_def->set_wordwrap(_max_width); ctext.set_wsubstr(_candidate_wtext.substr(0, _candidate_highlight_start),
_last_text_def->set_preserve_trailing_whitespace(true); _cursor_position, 0, inactive);
_last_text_def->set_max_rows(_num_lines); assembled = ctext.assemble_text();
// Check for multiple lines.
wstring ww_text = _last_text_def->get_wordwrapped_wtext();
// And chop the lines up into pieces.
_ww_lines.clear();
size_t p = 0;
size_t q = ww_text.find((wchar_t)'\n');
while (q != string::npos) {
_ww_lines.push_back(WWLine());
WWLine &line = _ww_lines.back();
line._str = ww_text.substr(p, q - p);
// Get the left edge of the text at this line.
line._left = 0.0f;
if (_last_text_def->get_align() != TextNode::A_left) {
// Temporarily set this line's text in the TextNode, just so
// we can measure the left margin. (If align is A_left, we
// know that the left margin is 0.0, so we don't need to do
// this.)
_last_text_def->set_wtext(line._str);
line._left = _last_text_def->get_left();
}
p = q + 1;
q = ww_text.find('\n', p);
}
_ww_lines.push_back(WWLine());
WWLine &line = _ww_lines.back();
line._str = ww_text.substr(p);
// Get the left edge of the text at this line.
line._left = 0.0f;
if (_last_text_def->get_align() != TextNode::A_left) {
_last_text_def->set_wtext(line._str);
line._left = _last_text_def->get_left();
} }
if (!_current_text.is_empty()) { if (!_current_text.is_empty()) {
_current_text.remove_node(); _current_text.remove_node();
} }
// We need to reset the text, since we might have changed it
// temporarily in the above when align is not A_left.
_last_text_def->set_wtext(display_wtext);
_current_text = _current_text =
_text_render_root.attach_new_node(_last_text_def->generate()); _text_render_root.attach_new_node(assembled);
_text_geom_stale = false; _text_geom_stale = false;
_cursor_stale = true; _cursor_stale = true;
} }
@ -844,32 +724,23 @@ update_cursor() {
if (_cursor_stale || node != _last_text_def) { if (_cursor_stale || node != _last_text_def) {
update_text(); update_text();
_cursor_position = min(_cursor_position, (int)_wtext.length()); _cursor_position = min(_cursor_position, _text.get_num_characters());
_candidate_cursor_pos = min(_candidate_cursor_pos, _candidate_wtext.length()); _candidate_cursor_pos = min(_candidate_cursor_pos, _candidate_wtext.length());
// Determine the row and column of the cursor. // Determine the row and column of the cursor.
int row = 0; int row, column;
int column = _cursor_position + _candidate_cursor_pos; float xpos, ypos;
while (row + 1 < (int)_ww_lines.size() && if (_obscure_mode) {
column > (int)_ww_lines[row]._str.length()) { _obscure_text.calc_r_c(row, column, _cursor_position + _candidate_cursor_pos);
column -= _ww_lines[row]._str.length(); xpos = _obscure_text.get_xpos(row, column);
row++; ypos = _obscure_text.get_ypos(row, column);
} else {
_text.calc_r_c(row, column, _cursor_position + _candidate_cursor_pos);
xpos = _text.get_xpos(row, column);
ypos = _text.get_ypos(row, column);
} }
nassertv(row >= 0 && row < (int)_ww_lines.size()); _cursor_def.set_pos(xpos, 0.0f, ypos);
// It is possible for this to become untrue legitimately, if due
// to a candidate string we have wordwrapped down the last part of
// the line containing the cursor.
//nassertv(column >= 0 && column <= (int)_ww_lines[row]._str.length());
float width =
_last_text_def->calc_width(_ww_lines[row]._str.substr(0, column));
float line_height = _last_text_def->get_line_height();
LVecBase3f trans(_ww_lines[row]._left + width, 0.0f, -line_height * row);
_cursor_def.set_pos(trans);
_cursor_stale = false; _cursor_stale = false;
} }

View File

@ -27,6 +27,7 @@
#include "pointerTo.h" #include "pointerTo.h"
#include "pvector.h" #include "pvector.h"
#include "clockObject.h" #include "clockObject.h"
#include "textAssembler.h"
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Class : PGEntry // Class : PGEntry
@ -74,8 +75,14 @@ PUBLISHED:
void setup(float width, int num_lines); void setup(float width, int num_lines);
INLINE void set_text(const string &text); INLINE void set_text(const string &text);
INLINE string get_plain_text() const;
INLINE string get_text() const; INLINE string get_text() const;
INLINE int get_num_characters() const;
INLINE wchar_t get_character(int n) const;
INLINE const TextGraphic *get_graphic(int n) const;
INLINE const TextProperties &get_properties(int n) const;
INLINE void set_cursor_position(int position); INLINE void set_cursor_position(int position);
INLINE int get_cursor_position() const; INLINE int get_cursor_position() const;
@ -121,21 +128,21 @@ PUBLISHED:
INLINE string get_erase_event() const; INLINE string get_erase_event() const;
INLINE void set_wtext(const wstring &wtext); INLINE void set_wtext(const wstring &wtext);
INLINE const wstring &get_wtext() const; INLINE wstring get_plain_wtext() const;
INLINE wstring get_wtext() const;
INLINE void set_accept_enabled(bool enabled); INLINE void set_accept_enabled(bool enabled);
bool is_wtext() const; bool is_wtext() const;
private: private:
const wstring &get_display_wtext();
void slot_text_def(int state); void slot_text_def(int state);
void update_text(); void update_text();
void update_cursor(); void update_cursor();
void show_hide_cursor(bool visible); void show_hide_cursor(bool visible);
void update_state(); void update_state();
wstring _wtext; TextAssembler _text;
wstring _obscured_wtext; TextAssembler _obscure_text;
int _cursor_position; int _cursor_position;
bool _cursor_stale; bool _cursor_stale;
bool _cursor_visible; bool _cursor_visible;
@ -166,17 +173,6 @@ private:
TextNode *_last_text_def; TextNode *_last_text_def;
bool _text_geom_stale; bool _text_geom_stale;
// This is a list of each row of text in the entry, after it has
// been wordwrapped by update_text(). It's used by update_cursor()
// to compute the correct cursor position.
class WWLine {
public:
wstring _str;
float _left;
};
typedef pvector<WWLine> WWLines;
WWLines _ww_lines;
// This is the node that represents the cursor geometry. It is also // This is the node that represents the cursor geometry. It is also
// attached to the above node, and is transformed around and/or // attached to the above node, and is transformed around and/or
// hidden according to the cursor's position and blink state. // hidden according to the cursor's position and blink state.

View File

@ -43,9 +43,61 @@ get_usage_hint() const {
return _usage_hint; return _usage_hint;
} }
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::set_max_rows
// Access: Published
// Description: If max_rows is greater than zero, no more than
// max_rows will be accepted. Text beyond that will be
// truncated.
//
// The return value is true if all the text is accepted,
// or false if some was truncated.
////////////////////////////////////////////////////////////////////
INLINE bool TextAssembler::
set_max_rows(int max_rows) {
_max_rows = max_rows;
return wordwrap_text();
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_max_rows
// Access: Published
// Description: If max_rows is greater than zero, no more than
// max_rows will be accepted. Text beyond that will be
// truncated.
////////////////////////////////////////////////////////////////////
INLINE int TextAssembler::
get_max_rows() const {
return _max_rows;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::set_properties
// Access: Published
// Description: Specifies the default TextProperties that are applied
// to the text in the absence of any nested property
// change sequences.
////////////////////////////////////////////////////////////////////
INLINE void TextAssembler::
set_properties(const TextProperties &properties) {
_initial_cprops = new ComputedProperties(properties);
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_properties
// Access: Published
// Description: Returns the default TextProperties that are applied
// to the text in the absence of any nested property
// change sequences.
////////////////////////////////////////////////////////////////////
INLINE const TextProperties &TextAssembler::
get_properties() const {
return _initial_cprops->_properties;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_ul // Function: TextAssembler::get_ul
// Access: Public // Access: Published
// Description: Returns the upper-left corner of the assembled text, // Description: Returns the upper-left corner of the assembled text,
// in 2-d text coordinates. // in 2-d text coordinates.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -56,7 +108,7 @@ get_ul() const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_lr // Function: TextAssembler::get_lr
// Access: Public // Access: Published
// Description: Returns the lower-right corner of the assembled text, // Description: Returns the lower-right corner of the assembled text,
// in 2-d text coordinates. // in 2-d text coordinates.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -65,15 +117,212 @@ get_lr() const {
return _lr; return _lr;
} }
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::calc_r
// Access: Published
// Description: Computes the row index of the nth character or
// graphic object in the text and returns it.
//
// If the nth character is not a normal printable
// character with a position in the wordwrapped string,
// returns -1 (for instance, a soft-hyphen character, or
// a newline character, may not have a corresponding
// position).
////////////////////////////////////////////////////////////////////
int TextAssembler::
calc_r(int n) const {
int r, c;
if (calc_r_c(r, c, n)) {
return r;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::calc_c
// Access: Published
// Description: Computes the column index of the nth character or
// graphic object in the text and returns it.
//
// If the nth character is not a normal printable
// character with a position in the wordwrapped string,
// returns -1 (for instance, a soft-hyphen character, or
// a newline character, may not have a corresponding
// position).
////////////////////////////////////////////////////////////////////
int TextAssembler::
calc_c(int n) const {
int r, c;
if (calc_r_c(r, c, n)) {
return c;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_num_characters
// Access: Published
// Description: Returns the number of characters of text, before
// wordwrapping.
////////////////////////////////////////////////////////////////////
INLINE int TextAssembler::
get_num_characters() const {
return _text_string.size();
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_character
// Access: Published
// Description: Returns the character at the indicated position in
// the pre-wordwrapped string. If the object at this
// position is a graphic object instead of a character,
// returns 0.
////////////////////////////////////////////////////////////////////
INLINE wchar_t TextAssembler::
get_character(int n) const {
nassertr(n >= 0 && n < (int)_text_string.size(), 0);
return _text_string[n]._character;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_graphic
// Access: Published
// Description: Returns the graphic object at the indicated position
// in the pre-wordwrapped string. If the object at this
// position is a character instead of a graphic object,
// returns NULL.
////////////////////////////////////////////////////////////////////
INLINE const TextGraphic *TextAssembler::
get_graphic(int n) const {
nassertr(n >= 0 && n < (int)_text_string.size(), 0);
return _text_string[n]._graphic;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_properties
// Access: Published
// Description: Returns the TextProperties in effect for the object
// at the indicated position in the pre-wordwrapped
// string.
////////////////////////////////////////////////////////////////////
INLINE const TextProperties &TextAssembler::
get_properties(int n) const {
nassertr(n >= 0 && n < (int)_text_string.size(), *(new TextProperties()));
return _text_string[n]._cprops->_properties;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_width
// Access: Published
// Description: Returns the width of the character or object at the
// indicated position in the pre-wordwrapped string.
////////////////////////////////////////////////////////////////////
INLINE float TextAssembler::
get_width(int n) const {
nassertr(n >= 0 && n < (int)_text_string.size(), 0.0f);
return calc_width(_text_string[n]);
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_num_rows // Function: TextAssembler::get_num_rows
// Access: Public // Access: Published
// Description: Returns the number of rows of text after it has all // Description: Returns the number of rows of text after it has all
// been wordwrapped and assembled. // been wordwrapped and assembled.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE int TextAssembler:: INLINE int TextAssembler::
get_num_rows() const { get_num_rows() const {
return _num_rows; return _text_block.size();
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_num_cols
// Access: Published
// Description: Returns the number of characters and/or graphic
// objects in the nth row.
////////////////////////////////////////////////////////////////////
INLINE int TextAssembler::
get_num_cols(int r) const {
nassertr(r >= 0 && r <= (int)_text_block.size(), 0);
if (r == _text_block.size()) {
return 0;
}
return _text_block[r]._string.size();
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_character
// Access: Published
// Description: Returns the character at the indicated position in
// the indicated row. If the object at this position is
// a graphic object instead of a character, returns 0.
////////////////////////////////////////////////////////////////////
INLINE wchar_t TextAssembler::
get_character(int r, int c) const {
nassertr(r >= 0 && r < (int)_text_block.size(), 0);
nassertr(c >= 0 && c < (int)_text_block[r]._string.size(), 0);
return _text_block[r]._string[c]._character;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_graphic
// Access: Published
// Description: Returns the graphic object at the indicated position
// in the indicated row. If the object at this position
// is a character instead of a graphic object, returns
// NULL.
////////////////////////////////////////////////////////////////////
INLINE const TextGraphic *TextAssembler::
get_graphic(int r, int c) const {
nassertr(r >= 0 && r < (int)_text_block.size(), 0);
nassertr(c >= 0 && c < (int)_text_block[r]._string.size(), 0);
return _text_block[r]._string[c]._graphic;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_properties
// Access: Published
// Description: Returns the TextProperties in effect for the object
// at the indicated position in the indicated row.
////////////////////////////////////////////////////////////////////
INLINE const TextProperties &TextAssembler::
get_properties(int r, int c) const {
nassertr(r >= 0 && r < (int)_text_block.size(), *(new TextProperties()));
nassertr(c >= 0 && c < (int)_text_block[r]._string.size(), *(new TextProperties()));
return _text_block[r]._string[c]._cprops->_properties;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_width
// Access: Published
// Description: Returns the width of the character or object at the
// indicated position in the indicated row.
////////////////////////////////////////////////////////////////////
INLINE float TextAssembler::
get_width(int r, int c) const {
nassertr(r >= 0 && r < (int)_text_block.size(), 0.0f);
nassertr(c >= 0 && c < (int)_text_block[r]._string.size(), 0.0f);
return calc_width(_text_block[r]._string[c]);
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::get_ypos
// Access: Published
// Description: Returns the y position of the origin of all of the
// characters or graphic objects in the indicated row.
//
// It is legal for r to exceed the index number of the
// last row by 1. The value of c is presently ignored.
////////////////////////////////////////////////////////////////////
INLINE float TextAssembler::
get_ypos(int r, int) const {
nassertr(r >= 0 && r <= (int)_text_block.size(), 0.0f);
if (r == _text_block.size()) {
return _next_row_ypos;
} else {
return _text_block[r]._ypos;
}
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -85,9 +334,9 @@ get_num_rows() const {
INLINE float TextAssembler:: INLINE float TextAssembler::
calc_width(const TextCharacter &tch) { calc_width(const TextCharacter &tch) {
if (tch._graphic != (TextGraphic *)NULL) { if (tch._graphic != (TextGraphic *)NULL) {
return calc_width(tch._graphic, *tch._properties); return calc_width(tch._graphic, tch._cprops->_properties);
} else { } else {
return calc_width(tch._character, *tch._properties); return calc_width(tch._character, tch._cprops->_properties);
} }
} }
@ -97,10 +346,11 @@ calc_width(const TextCharacter &tch) {
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE TextAssembler::TextCharacter:: INLINE TextAssembler::TextCharacter::
TextCharacter(wchar_t character, const TextProperties *properties) : TextCharacter(wchar_t character,
TextAssembler::ComputedProperties *cprops) :
_character(character), _character(character),
_graphic(NULL), _graphic(NULL),
_properties(properties) _cprops(cprops)
{ {
} }
@ -110,13 +360,126 @@ TextCharacter(wchar_t character, const TextProperties *properties) :
// Description: // Description:
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE TextAssembler::TextCharacter:: INLINE TextAssembler::TextCharacter::
TextCharacter(const TextGraphic *graphic, const TextProperties *properties) : TextCharacter(const TextGraphic *graphic, const wstring &graphic_wname,
TextAssembler::ComputedProperties *cprops) :
_character(0), _character(0),
_graphic(graphic), _graphic(graphic),
_properties(properties) _graphic_wname(graphic_wname),
_cprops(cprops)
{ {
} }
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::TextCharacter::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE TextAssembler::TextCharacter::
TextCharacter(const TextAssembler::TextCharacter &copy) :
_character(copy._character),
_graphic(copy._graphic),
_graphic_wname(copy._graphic_wname),
_cprops(copy._cprops)
{
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::TextCharacter::Copy Assignment
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void TextAssembler::TextCharacter::
operator = (const TextAssembler::TextCharacter &copy) {
_character = copy._character;
_graphic = copy._graphic;
_graphic_wname = copy._graphic_wname;
_cprops = copy._cprops;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::TextRow::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE TextAssembler::TextRow::
TextRow(int row_start) :
_row_start(row_start),
_got_soft_hyphens(false)
{
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::TextRow::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE TextAssembler::TextRow::
TextRow(const TextAssembler::TextRow &copy) :
_string(copy._string),
_row_start(copy._row_start),
_got_soft_hyphens(copy._got_soft_hyphens),
_xpos(copy._xpos),
_ypos(copy._ypos)
{
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::TextRow::Copy Assignment
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void TextAssembler::TextRow::
operator = (const TextAssembler::TextRow &copy) {
_string = copy._string;
_row_start = copy._row_start;
_got_soft_hyphens = copy._got_soft_hyphens;
_xpos = copy._xpos;
_ypos = copy._ypos;
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::ComputedProperties::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE TextAssembler::ComputedProperties::
ComputedProperties(const TextProperties &orig_properties) :
_based_on(NULL),
_depth(0),
_properties(orig_properties)
{
}
////////////////////////////////////////////////////////////////////
// Function: TextAssembler::ComputedProperties::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE TextAssembler::ComputedProperties::
ComputedProperties(ComputedProperties *based_on, const wstring &wname,
TextEncoder *encoder) :
_based_on(based_on),
_depth(_based_on->_depth + 1),
_wname(wname),
_properties(based_on->_properties)
{
TextPropertiesManager *manager =
TextPropertiesManager::get_global_ptr();
// Now we have to encode the wstring into a string, for lookup
// in the TextPropertiesManager.
string name = encoder->encode_wtext(wname);
const TextProperties *named_props = manager->get_properties_ptr(name);
if (named_props != (TextProperties *)NULL) {
_properties.add_properties(*named_props);
} else {
text_cat.warning()
<< "Unknown TextProperties: " << name << "\n";
}
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextAssembler::GlyphPlacement::add_piece // Function: TextAssembler::GlyphPlacement::add_piece
// Access: Public // Access: Public

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,6 @@
#ifndef TEXTASSEMBLER_H #ifndef TEXTASSEMBLER_H
#define TEXTASSEMBLER_H #define TEXTASSEMBLER_H
#ifndef CPPPARSER // interrogate has a bit of trouble with wstring iterators.
#include "pandabase.h" #include "pandabase.h"
#include "textProperties.h" #include "textProperties.h"
@ -29,23 +27,27 @@
#include "geomNode.h" #include "geomNode.h"
#include "pointerTo.h" #include "pointerTo.h"
#include "geom.h" #include "geom.h"
#include "textPropertiesManager.h"
#include "textEncoder.h"
class TextEncoder; class TextEncoder;
class TextGraphic; class TextGraphic;
class TextAssembler;
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Class : TextAssembler // Class : TextAssembler
// Description : This class is not intended to be used directly by // Description : This class is not normally used directly by user
// user code, but is used by the TextNode to lay out a // code, but is used by the TextNode to lay out a block
// block of text and convert it into rows of Geoms // of text and convert it into rows of Geoms according
// according to the TextProperties. // to the TextProperties. However, user code may take
// // advantage of it, if desired, for very low-level text
// It is not exported from the DLL since it is not // operations.
// intended to be used outside of this module.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
class TextAssembler { class EXPCL_PANDA TextAssembler {
public: PUBLISHED:
TextAssembler(TextEncoder *encoder); TextAssembler(TextEncoder *encoder);
TextAssembler(const TextAssembler &copy);
void operator = (const TextAssembler &copy);
~TextAssembler(); ~TextAssembler();
void clear(); void clear();
@ -53,11 +55,41 @@ public:
INLINE void set_usage_hint(Geom::UsageHint usage_hint); INLINE void set_usage_hint(Geom::UsageHint usage_hint);
INLINE Geom::UsageHint get_usage_hint() const; INLINE Geom::UsageHint get_usage_hint() const;
bool set_wtext(const wstring &wtext, const TextProperties &properties, INLINE bool set_max_rows(int max_rows);
int max_rows = 0); INLINE int get_max_rows() const;
INLINE int get_num_rows() const;
INLINE void set_properties(const TextProperties &properties);
INLINE const TextProperties &get_properties() const;
bool set_wtext(const wstring &wtext);
bool set_wsubstr(const wstring &wtext, int start, int count,
const TextProperties &properties = TextProperties());
wstring get_plain_wtext() const;
wstring get_wordwrapped_plain_wtext() const;
wstring get_wtext() const;
wstring get_wordwrapped_wtext() const; wstring get_wordwrapped_wtext() const;
bool calc_r_c(int &r, int &c, int n) const;
INLINE int calc_r(int n) const;
INLINE int calc_c(int n) const;
int calc_index(int r, int c) const;
INLINE int get_num_characters() const;
INLINE wchar_t get_character(int n) const;
INLINE const TextGraphic *get_graphic(int n) const;
INLINE const TextProperties &get_properties(int n) const;
INLINE float get_width(int n) const;
INLINE int get_num_rows() const;
INLINE int get_num_cols(int r) const;
INLINE wchar_t get_character(int r, int c) const;
INLINE const TextGraphic *get_graphic(int r, int c) const;
INLINE const TextProperties &get_properties(int r, int c) const;
INLINE float get_width(int r, int c) const;
float get_xpos(int r, int c) const;
INLINE float get_ypos(int r, int c) const;
PT(PandaNode) assemble_text(); PT(PandaNode) assemble_text();
INLINE const LVector2f &get_ul() const; INLINE const LVector2f &get_ul() const;
@ -67,33 +99,69 @@ public:
static float calc_width(const TextGraphic *graphic, const TextProperties &properties); static float calc_width(const TextGraphic *graphic, const TextProperties &properties);
private: private:
class ComputedProperties : public ReferenceCount {
public:
INLINE ComputedProperties(const TextProperties &orig_properties);
INLINE ComputedProperties(ComputedProperties *based_on,
const wstring &wname, TextEncoder *encoder);
void append_delta(wstring &wtext, ComputedProperties *other);
PT(ComputedProperties) _based_on;
int _depth;
wstring _wname;
TextProperties _properties;
};
// These structures are built up and operated on by scan_wtext() and // These structures are built up and operated on by scan_wtext() and
// wordwrap_text(). It represents the unrolling of the embedded \1 // wordwrap_text(). It represents the unrolling of the embedded \1
// .. \2 sequences embedded in the string into a TextProperties // .. \2 sequences embedded in the string into a TextProperties
// pointer associated with each character. // pointer associated with each character.
typedef pvector<TextProperties *> PropertiesList;
class TextCharacter { class TextCharacter {
public: public:
INLINE TextCharacter(wchar_t character, const TextProperties *properties); INLINE TextCharacter(wchar_t character, ComputedProperties *cprops);
INLINE TextCharacter(const TextGraphic *graphic, const TextProperties *properties); INLINE TextCharacter(const TextGraphic *graphic,
const wstring &graphic_wname,
ComputedProperties *cprops);
INLINE TextCharacter(const TextCharacter &copy);
INLINE void operator = (const TextCharacter &copy);
wchar_t _character; wchar_t _character;
const TextGraphic *_graphic; const TextGraphic *_graphic;
const TextProperties *_properties; wstring _graphic_wname;
PT(ComputedProperties) _cprops;
}; };
typedef pvector<TextCharacter> TextString; typedef pvector<TextCharacter> TextString;
PropertiesList _properties_list; class TextRow {
TextString _wordwrapped_string; public:
int _num_rows; INLINE TextRow(int row_start);
INLINE TextRow(const TextRow &copy);
INLINE void operator = (const TextRow &copy);
void scan_wtext(wstring::const_iterator &si, TextString _string;
int _row_start;
bool _got_soft_hyphens;
float _xpos;
float _ypos;
};
typedef pvector<TextRow> TextBlock;
PT(ComputedProperties) _initial_cprops;
// This is the string, unwordwrapped.
TextString _text_string;
// And here it is, wordwrapped.
TextBlock _text_block;
#ifndef CPPPARSER // interrogate has a bit of trouble with wstring iterators.
void scan_wtext(TextString &output_string,
wstring::const_iterator &si,
const wstring::const_iterator &send, const wstring::const_iterator &send,
const TextProperties *current_properties, ComputedProperties *current_cprops);
TextString &text_string); #endif // CPPPARSER
bool wordwrap_text(const TextAssembler::TextString &text,
TextAssembler::TextString &output_text, bool wordwrap_text();
int max_rows);
INLINE static float calc_width(const TextCharacter &tch); INLINE static float calc_width(const TextCharacter &tch);
static float calc_hyphen_width(const TextCharacter &tch); static float calc_hyphen_width(const TextCharacter &tch);
@ -127,11 +195,8 @@ private:
}; };
typedef pvector<GlyphPlacement *> PlacedGlyphs; typedef pvector<GlyphPlacement *> PlacedGlyphs;
void assemble_paragraph(TextString::const_iterator si, void assemble_paragraph(PlacedGlyphs &placed_glyphs);
const TextString::const_iterator &send, void assemble_row(TextRow &row,
PlacedGlyphs &placed_glyphs);
void assemble_row(TextString::const_iterator &si,
const TextString::const_iterator &send,
PlacedGlyphs &row_placed_glyphs, PlacedGlyphs &row_placed_glyphs,
float &row_width, float &line_height, float &row_width, float &line_height,
TextProperties::Alignment &align); TextProperties::Alignment &align);
@ -188,14 +253,14 @@ private:
// These are filled in by assemble_paragraph(). // These are filled in by assemble_paragraph().
LVector2f _ul; LVector2f _ul;
LVector2f _lr; LVector2f _lr;
float _next_row_ypos;
TextEncoder *_encoder; TextEncoder *_encoder;
Geom::UsageHint _usage_hint; Geom::UsageHint _usage_hint;
int _max_rows;
}; };
#include "textAssembler.I" #include "textAssembler.I"
#endif // CPPPARSER
#endif #endif

View File

@ -47,6 +47,7 @@ get_line_height() const {
INLINE void TextNode:: INLINE void TextNode::
set_max_rows(int max_rows) { set_max_rows(int max_rows) {
_max_rows = max_rows; _max_rows = max_rows;
_assembler.set_max_rows(_max_rows);
invalidate_with_measure(); invalidate_with_measure();
} }
@ -59,6 +60,7 @@ set_max_rows(int max_rows) {
INLINE void TextNode:: INLINE void TextNode::
clear_max_rows() { clear_max_rows() {
_max_rows = 0; _max_rows = 0;
_assembler.set_max_rows(_max_rows);
invalidate_with_measure(); invalidate_with_measure();
} }
@ -1183,8 +1185,10 @@ append_unicode_char(int character) {
// Access: Public // Access: Public
// Description: Returns a string that represents the contents of the // Description: Returns a string that represents the contents of the
// text, as it has been formatted by wordwrap rules. // text, as it has been formatted by wordwrap rules.
// This will not contain any embedded special characters //
// like \1 or \3. // In earlier versions, this did not contain any
// embedded special characters like \1 or \3; now it
// does.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE string TextNode:: INLINE string TextNode::
get_wordwrapped_text() const { get_wordwrapped_text() const {
@ -1234,8 +1238,10 @@ append_wtext(const wstring &wtext) {
// Access: Published // Access: Published
// Description: Returns a wstring that represents the contents of the // Description: Returns a wstring that represents the contents of the
// text, as it has been formatted by wordwrap rules. // text, as it has been formatted by wordwrap rules.
// This will not contain any embedded special characters //
// like \1 or \3. // In earlier versions, this did not contain any
// embedded special characters like \1 or \3; now it
// does.
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
INLINE wstring TextNode:: INLINE wstring TextNode::
get_wordwrapped_wtext() const { get_wordwrapped_wtext() const {

View File

@ -302,7 +302,8 @@ generate() {
wstring wtext = get_wtext(); wstring wtext = get_wtext();
// Assemble the text. // Assemble the text.
bool all_set = _assembler.set_wtext(wtext, *this, _max_rows); _assembler.set_properties(*this);
bool all_set = _assembler.set_wtext(wtext);
if (all_set) { if (all_set) {
// No overflow. // No overflow.
_flags &= ~F_has_overflow; _flags &= ~F_has_overflow;

View File

@ -296,9 +296,7 @@ private:
LPoint3f _ul3d, _lr3d; LPoint3f _ul3d, _lr3d;
#ifndef CPPPARSER
TextAssembler _assembler; TextAssembler _assembler;
#endif
public: public:
static TypeHandle get_class_type() { static TypeHandle get_class_type() {

View File

@ -17,6 +17,16 @@
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: TextProperties::operator !=
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool TextProperties::
operator != (const TextProperties &other) const {
return !operator == (other);
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextProperties::is_any_specified // Function: TextProperties::is_any_specified
// Access: Published // Access: Published

View File

@ -91,6 +91,71 @@ operator = (const TextProperties &copy) {
_glyph_shift = copy._glyph_shift; _glyph_shift = copy._glyph_shift;
} }
////////////////////////////////////////////////////////////////////
// Function: TextProperties::operator ==
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
bool TextProperties::
operator == (const TextProperties &other) const {
if (_specified != other._specified) {
return false;
}
if ((_specified & F_has_font) && _font != other._font) {
return false;
}
if ((_specified & F_has_small_caps) && _small_caps != other._small_caps) {
return false;
}
if ((_specified & F_has_small_caps_scale) && _small_caps_scale != other._small_caps_scale) {
return false;
}
if ((_specified & F_has_slant) && _slant != other._slant) {
return false;
}
if ((_specified & F_has_align) && _align != other._align) {
return false;
}
if ((_specified & F_has_indent) && _indent_width != other._indent_width) {
return false;
}
if ((_specified & F_has_wordwrap) && _wordwrap_width != other._wordwrap_width) {
return false;
}
if ((_specified & F_has_preserve_trailing_whitespace) && _preserve_trailing_whitespace != other._preserve_trailing_whitespace) {
return false;
}
if ((_specified & F_has_text_color) && _text_color != other._text_color) {
return false;
}
if ((_specified & F_has_text_color) && _text_color != other._text_color) {
return false;
}
if ((_specified & F_has_shadow_color) && _shadow_color != other._shadow_color) {
return false;
}
if ((_specified & F_has_shadow) && _shadow_offset != other._shadow_offset) {
return false;
}
if ((_specified & F_has_bin) && _bin != other._bin) {
return false;
}
if ((_specified & F_has_draw_order) && _draw_order != other._draw_order) {
return false;
}
if ((_specified & F_has_tab_width) && _tab_width != other._tab_width) {
return false;
}
if ((_specified & F_has_glyph_scale) && _glyph_scale != other._glyph_scale) {
return false;
}
if ((_specified & F_has_glyph_shift) && _glyph_shift != other._glyph_shift) {
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: TextProperties::clear // Function: TextProperties::clear
// Access: Published // Access: Published

View File

@ -59,6 +59,9 @@ PUBLISHED:
TextProperties(const TextProperties &copy); TextProperties(const TextProperties &copy);
void operator = (const TextProperties &copy); void operator = (const TextProperties &copy);
bool operator == (const TextProperties &other) const;
INLINE bool operator != (const TextProperties &other) const;
void clear(); void clear();
INLINE bool is_any_specified() const; INLINE bool is_any_specified() const;