Panda3D
pgEntry.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pgEntry.cxx
10  * @author drose
11  * @date 2002-03-13
12  */
13 
14 #include "pgEntry.h"
16 
17 #include "cullTraverser.h"
18 #include "cullTraverserData.h"
19 #include "throw_event.h"
20 #include "transformState.h"
21 #include "mouseWatcherParameter.h"
22 #include "keyboardButton.h"
23 #include "mouseButton.h"
24 #include "lineSegs.h"
25 #include "textEncoder.h"
26 #include "config_text.h"
27 
28 #include <math.h>
29 
30 using std::max;
31 using std::min;
32 using std::string;
33 using std::wstring;
34 
35 TypeHandle PGEntry::_type_handle;
36 
37 /**
38  *
39  */
40 PGEntry::
41 PGEntry(const string &name) :
42  PGItem(name),
43  _text(get_text_node()),
44  _obscure_text(get_text_node())
45 {
46  set_cull_callback();
47 
48  _cursor_position = 0;
49  _cursor_stale = true;
50  _candidate_highlight_start = 0;
51  _candidate_highlight_end = 0;
52  _candidate_cursor_pos = 0;
53  _max_chars = 0;
54  _max_width = 0.0f;
55  _num_lines = 1;
56  _accept_enabled = true;
57  _last_text_def = nullptr;
58  _text_geom_stale = true;
59  _text_geom_flattened = true;
60  _blink_start = 0.0f;
61  _blink_rate = 1.0f;
62 
63  _text_render_root = NodePath("text_root");
64 
65  CPT(TransformState) transform = TransformState::make_mat(LMatrix4::convert_mat(CS_default, CS_zup_right));
66  _text_render_root.set_transform(transform);
67 
68  _cursor_scale = _text_render_root.attach_new_node("cursor_scale");
69  _cursor_def = _cursor_scale.attach_new_node("cursor");
70  _cursor_visible = true;
71 
72  // These strings are used to specify the TextProperties to apply to
73  // candidate strings generated from the IME (for entering text in an east
74  // Asian language).
75  _candidate_active = "candidate_active";
76  _candidate_inactive = "candidate_inactive";
77 
78  _cursor_keys_active = true;
79  _obscure_mode = false;
80  _overflow_mode = false;
81 
82  _current_padding = 0.0f;
83 
84  set_active(true);
85  update_state();
86 
87  // Some default parameters so it doesn't crash hard if no one calls setup().
88  setup_minimal(10, 1);
89 }
90 
91 /**
92  *
93  */
94 PGEntry::
95 ~PGEntry() {
96 }
97 
98 /**
99  *
100  */
101 PGEntry::
102 PGEntry(const PGEntry &copy) :
103  PGItem(copy),
104  _text(copy._text),
105  _obscure_text(copy._obscure_text),
106  _cursor_position(copy._cursor_position),
107  _cursor_visible(copy._cursor_visible),
108  _candidate_highlight_start(copy._candidate_highlight_start),
109  _candidate_highlight_end(copy._candidate_highlight_end),
110  _candidate_cursor_pos(copy._candidate_cursor_pos),
111  _max_chars(copy._max_chars),
112  _max_width(copy._max_width),
113  _num_lines(copy._num_lines),
114  _accept_enabled(copy._accept_enabled),
115  _candidate_active(copy._candidate_active),
116  _candidate_inactive(copy._candidate_inactive),
117  _text_defs(copy._text_defs),
118  _blink_start(copy._blink_start),
119  _blink_rate(copy._blink_rate),
120  _cursor_keys_active(copy._cursor_keys_active),
121  _obscure_mode(copy._obscure_mode),
122  _overflow_mode(copy._overflow_mode)
123 {
124  _cursor_stale = true;
125  _last_text_def = nullptr;
126  _text_geom_stale = true;
127  _text_geom_flattened = true;
128 
129  _text_render_root = NodePath("text_root");
130  _cursor_scale = _text_render_root.attach_new_node("cursor_scale");
131  _cursor_scale.set_transform(copy._cursor_scale.get_transform());
132  _cursor_def = copy._cursor_def.copy_to(_cursor_scale);
133 }
134 
135 /**
136  * Returns a newly-allocated Node that is a shallow copy of this one. It will
137  * be a different Node pointer, but its internal data may or may not be shared
138  * with that of the original Node.
139  */
141 make_copy() const {
142  LightReMutexHolder holder(_lock);
143  return new PGEntry(*this);
144 }
145 
146 /**
147  * Transforms the contents of this node by the indicated matrix, if it means
148  * anything to do so. For most kinds of nodes, this does nothing.
149  */
150 void PGEntry::
151 xform(const LMatrix4 &mat) {
152  LightReMutexHolder holder(_lock);
153  PGItem::xform(mat);
154  _text_render_root.set_mat(_text_render_root.get_mat() * mat);
155 }
156 
157 /**
158  * This function will be called during the cull traversal to perform any
159  * additional operations that should be performed at cull time. This may
160  * include additional manipulation of render state or additional
161  * visible/invisible decisions, or any other arbitrary operation.
162  *
163  * Note that this function will *not* be called unless set_cull_callback() is
164  * called in the constructor of the derived class. It is necessary to call
165  * set_cull_callback() to indicated that we require cull_callback() to be
166  * called.
167  *
168  * By the time this function is called, the node has already passed the
169  * bounding-volume test for the viewing frustum, and the node's transform and
170  * state have already been applied to the indicated CullTraverserData object.
171  *
172  * The return value is true if this node should be visible, or false if it
173  * should be culled.
174  */
175 bool PGEntry::
177  LightReMutexHolder holder(_lock);
178  PGItem::cull_callback(trav, data);
179  update_text();
180  update_cursor();
181 
182  // Now render the text.
183  CullTraverserData next_data(data, _text_render_root.node());
184  trav->traverse(next_data);
185 
186  // Now continue to render everything else below this node.
187  return true;
188 }
189 
190 /**
191  * This is a callback hook function, called whenever a mouse or keyboard entry
192  * is depressed while the mouse is within the region.
193  */
194 void PGEntry::
195 press(const MouseWatcherParameter &param, bool background) {
196  LightReMutexHolder holder(_lock);
197  if (get_active()) {
198  if (param.has_button()) {
199  // Make sure _text is initialized properly.
200  update_text();
201 
202  bool overflow_mode = get_overflow_mode() && _num_lines == 1;
203 
204  ButtonHandle button = param.get_button();
205 
206  if (button == MouseButton::one() ||
207  button == MouseButton::two() ||
208  button == MouseButton::three() ||
209  button == MouseButton::four() ||
210  button == MouseButton::five()) {
211  // Mouse button; set focus.
212  set_focus(true);
213 
214  } else if ((!background && get_focus()) ||
215  (background && get_background_focus())) {
216  // Keyboard button.
217  if (!_candidate_wtext.empty()) {
218  _candidate_wtext = wstring();
219  _text_geom_stale = true;
220  }
221 
222  _cursor_position = min(_cursor_position, _text.get_num_characters());
223  _blink_start = ClockObject::get_global_clock()->get_frame_time();
224  if (button == KeyboardButton::enter()) {
225  // Enter. Accept the entry.
226  if (_accept_enabled) {
227  accept(param);
228  }
229  else {
230  accept_failed(param);
231  }
232 
233  } else if (button == KeyboardButton::backspace()) {
234  // Backspace. Remove the character to the left of the cursor.
235  if (_cursor_position > 0) {
236  _text.set_wsubstr(wstring(), _cursor_position - 1, 1);
237  _cursor_position--;
238  _cursor_stale = true;
239  _text_geom_stale = true;
240  erase(param);
241  }
242 
243  } else if (button == KeyboardButton::del()) {
244  // Delete. Remove the character to the right of the cursor.
245  if (_cursor_position < _text.get_num_characters()) {
246  _text.set_wsubstr(wstring(), _cursor_position, 1);
247  _text_geom_stale = true;
248  erase(param);
249  }
250 
251  } else if (button == KeyboardButton::left()) {
252  if (_cursor_keys_active) {
253  // Left arrow. Move the cursor position to the left.
254  --_cursor_position;
255  if (_cursor_position < 0) {
256  _cursor_position = 0;
257  overflow(param);
258  } else {
259  type(param);
260  }
261  _cursor_stale = true;
262  if (overflow_mode){
263  _text_geom_stale = true;
264  }
265  }
266 
267  } else if (button == KeyboardButton::right()) {
268  if (_cursor_keys_active) {
269  // Right arrow. Move the cursor position to the right.
270  ++_cursor_position;
271  if (_cursor_position > _text.get_num_characters()) {
272  _cursor_position = _text.get_num_characters();
273  overflow(param);
274  } else {
275  type(param);
276  }
277  _cursor_stale = true;
278  if (overflow_mode){
279  _text_geom_stale = true;
280  }
281  }
282 
283  } else if (button == KeyboardButton::home()) {
284  if (_cursor_keys_active) {
285  // Home. Move the cursor position to the beginning.
286  _cursor_position = 0;
287  _cursor_stale = true;
288  if (overflow_mode){
289  _text_geom_stale = true;
290  }
291  type(param);
292  }
293 
294  } else if (button == KeyboardButton::end()) {
295  if (_cursor_keys_active) {
296  // End. Move the cursor position to the end.
297  _cursor_position = _text.get_num_characters();
298  _cursor_stale = true;
299  if (overflow_mode){
300  _text_geom_stale = true;
301  }
302  type(param);
303  }
304  }
305  }
306  }
307  }
308  PGItem::press(param, background);
309 }
310 
311 /**
312  * This is a callback hook function, called whenever the user types a key.
313  */
314 void PGEntry::
315 keystroke(const MouseWatcherParameter &param, bool background) {
316  LightReMutexHolder holder(_lock);
317  if (get_active()) {
318  if (param.has_keycode()) {
319  // Make sure _text is initialized properly.
320  update_text();
321 
322  int keycode = param.get_keycode();
323 
324  if (!isascii(keycode) || isprint(keycode)) {
325  // A normal visible character. Add a new character to the text entry,
326  // if there's room.
327  if (!_candidate_wtext.empty()) {
328  _candidate_wtext = wstring();
329  _text_geom_stale = true;
330  }
331  wstring new_char(1, (wchar_t)keycode);
332 
333  if (get_max_chars() > 0 && _text.get_num_characters() >= get_max_chars()) {
334  // In max_chars mode, we consider it an overflow after we have
335  // exceeded a fixed number of characters, irrespective of the
336  // formatted width of those characters.
337  overflow(param);
338 
339  } else {
340  _cursor_position = min(_cursor_position, _text.get_num_characters());
341  bool too_long = !_text.set_wsubstr(new_char, _cursor_position, 0);
342  bool overflow_mode = get_overflow_mode() && _num_lines == 1;
343  if(overflow_mode){
344  too_long = false;
345  }
346  if (_obscure_mode) {
347  too_long = !_obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
348  } else {
349  if (!too_long && (_text.get_num_rows() == _num_lines) && !overflow_mode) {
350  // If we've filled up all of the available lines, we must also
351  // ensure that the last line is not too long (it might be,
352  // because of additional whitespace on the end).
353  int r = _num_lines - 1;
354  int c = _text.get_num_cols(r);
355  PN_stdfloat last_line_width =
356  _text.get_xpos(r, c) - _text.get_xpos(r, 0);
357  too_long = (last_line_width > _max_width);
358  }
359 
360  if (!too_long && keycode == ' ' && !overflow_mode) {
361  // Even if we haven't filled up all of the available lines, we
362  // should reject a space that's typed at the end of the current
363  // line if it would make that line exceed the maximum width,
364  // just so we don't allow an infinite number of spaces to
365  // accumulate.
366  int r, c;
367  _text.calc_r_c(r, c, _cursor_position);
368  if (_text.get_num_cols(r) == c + 1) {
369  // The user is typing at the end of the line. But we must
370  // allow at least one space at the end of the line, so we only
371  // make any of the following checks if there are already
372  // multiple spaces at the end of the line.
373  if (c - 1 >= 0 && _text.get_character(r, c - 1) == ' ') {
374  // Ok, the user is putting multiple spaces on the end of a
375  // line; we need to make sure the line does not grow too
376  // wide. Measure the line's width.
377  PN_stdfloat current_line_width =
378  _text.get_xpos(r, c + 1) - _text.get_xpos(r, 0);
379  if (current_line_width > _max_width) {
380  // We have to reject the space, but we don't treat it as
381  // an overflow condition.
382  _text.set_wsubstr(wstring(), _cursor_position, 1);
383  // If the user is typing over existing space characters,
384  // we act as if the right-arrow key were pressed instead,
385  // and advance the cursor to the next position.
386  // Otherwise, we just quietly eat the space character.
387  if (_cursor_position < _text.get_num_characters() &&
388  _text.get_character(_cursor_position) == ' ') {
389  _cursor_position++;
390  _cursor_stale = true;
391  }
392  return;
393  }
394  }
395  }
396  }
397  }
398 
399  if (too_long) {
400  _text.set_wsubstr(wstring(), _cursor_position, 1);
401  overflow(param);
402 
403  } else {
404  _cursor_position += new_char.length();
405  _cursor_stale = true;
406  _text_geom_stale = true;
407  type(param);
408  }
409  }
410  }
411  }
412  }
413  PGItem::keystroke(param, background);
414 }
415 
416 /**
417  * This is a callback hook function, called whenever the user selects an item
418  * from the IME menu.
419  */
420 void PGEntry::
421 candidate(const MouseWatcherParameter &param, bool background) {
422  LightReMutexHolder holder(_lock);
423  if (get_active()) {
424  if (param.has_candidate()) {
425  // Save the candidate string so it can be displayed.
426  _candidate_wtext = param.get_candidate_string();
427  _candidate_highlight_start = param.get_highlight_start();
428  _candidate_highlight_end = param.get_highlight_end();
429  _candidate_cursor_pos = param.get_cursor_pos();
430  _text_geom_stale = true;
431  if (!_candidate_wtext.empty()) {
432  type(param);
433  }
434  }
435  }
436  PGItem::candidate(param, background);
437 }
438 
439 /**
440  * This is a callback hook function, called whenever the entry is accepted by
441  * the user pressing Enter normally.
442  */
443 void PGEntry::
445  LightReMutexHolder holder(_lock);
447  string event = get_accept_event(param.get_button());
448  play_sound(event);
449  throw_event(event, EventParameter(ep));
450  set_focus(false);
451 }
452 
453 /**
454  * This is a callback hook function, called whenever the user presses Enter
455  * but we can't accept the input.
456  */
457 void PGEntry::
459  LightReMutexHolder holder(_lock);
461  string event = get_accept_failed_event(param.get_button());
462  play_sound(event);
463  throw_event(event, EventParameter(ep));
464  // set_focus(false);
465 }
466 
467 /**
468  * This is a callback hook function, called whenever the entry is overflowed
469  * because the user attempts to type too many characters, exceeding either
470  * set_max_chars() or set_max_width().
471  */
472 void PGEntry::
474  LightReMutexHolder holder(_lock);
476  string event = get_overflow_event();
477  play_sound(event);
478  throw_event(event, EventParameter(ep));
479 }
480 
481 /**
482  * This is a callback hook function, called whenever the user extends the text
483  * by typing.
484  */
485 void PGEntry::
487  LightReMutexHolder holder(_lock);
489  string event = get_type_event();
490  play_sound(event);
491  throw_event(event, EventParameter(ep));
492 }
493 
494 /**
495  * This is a callback hook function, called whenever the user erase characters
496  * in the text.
497  */
498 void PGEntry::
500  LightReMutexHolder holder(_lock);
502  string event = get_erase_event();
503  play_sound(event);
504  throw_event(event, EventParameter(ep));
505 }
506 
507 /**
508  * This is a callback hook function, called whenever the cursor moves.
509  */
510 void PGEntry::
512  LightReMutexHolder holder(_lock);
513  string event = get_cursormove_event();
514  throw_event(event, EventParameter(_cursor_def.get_x()), EventParameter(_cursor_def.get_y()));
515 }
516 
517 /**
518  * Sets up the entry for normal use. The width is the maximum width of
519  * characters that will be typed, and num_lines is the integer number of lines
520  * of text of the entry. Both of these together determine the size of the
521  * entry, based on the TextNode in effect.
522  */
523 void PGEntry::
524 setup(PN_stdfloat width, int num_lines) {
525  LightReMutexHolder holder(_lock);
526  setup_minimal(width, num_lines);
527 
528  TextNode *text_node = get_text_def(S_focus);
529  PN_stdfloat line_height = text_node->get_line_height();
530 
531  // Determine the four corners of the frame.
532  float bottom = -0.3f * line_height - (line_height * (num_lines - 1));
533  // Transform each corner by the TextNode's transform.
534  LMatrix4 mat = text_node->get_transform();
535  LPoint3 ll = LPoint3::rfu(0.0f, 0.0f, bottom) * mat;
536  LPoint3 ur = LPoint3::rfu(width, 0.0f, line_height) * mat;
537  LPoint3 lr = LPoint3::rfu(width, 0.0f, bottom) * mat;
538  LPoint3 ul = LPoint3::rfu(0.0f, 0.0f, line_height) * mat;
539 
540  LVector3 up = LVector3::up();
541  int up_axis;
542  if (up[1]) {
543  up_axis = 1;
544  }
545  else if (up[2]) {
546  up_axis = 2;
547  }
548  else {
549  up_axis = 0;
550  }
551  LVector3 right = LVector3::right();
552  int right_axis;
553  if (right[0]) {
554  right_axis = 0;
555  }
556  else if (right[2]) {
557  right_axis = 2;
558  }
559  else {
560  right_axis = 1;
561  }
562 
563  // And get the new minmax to define the frame. We do all this work instead
564  // of just using the lower-left and upper-right corners, just in case the
565  // text was rotated.
566  LVecBase4 frame;
567  frame[0] = min(min(ll[right_axis], ur[right_axis]), min(lr[right_axis], ul[right_axis]));
568  frame[1] = max(max(ll[right_axis], ur[right_axis]), max(lr[right_axis], ul[right_axis]));
569  frame[2] = min(min(ll[up_axis], ur[up_axis]), min(lr[up_axis], ul[up_axis]));
570  frame[3] = max(max(ll[up_axis], ur[up_axis]), max(lr[up_axis], ul[up_axis]));
571 
572  switch (text_node->get_align()) {
573  case TextNode::A_left:
574  case TextNode::A_boxed_left:
575  // The default case.
576  break;
577 
578  case TextNode::A_center:
579  case TextNode::A_boxed_center:
580  frame[0] = -width / 2.0;
581  frame[1] = width / 2.0;
582  break;
583 
584  case TextNode::A_right:
585  case TextNode::A_boxed_right:
586  frame[0] = -width;
587  frame[1] = 0.0f;
588  break;
589  }
590 
591  set_frame(frame[0] - 0.15f, frame[1] + 0.15f, frame[2], frame[3]);
592 
593  PGFrameStyle style;
594  style.set_width(0.1f, 0.1f);
595  style.set_type(PGFrameStyle::T_bevel_in);
596  style.set_color(0.8f, 0.8f, 0.8f, 1.0f);
597 
598  set_frame_style(S_no_focus, style);
599 
600  style.set_color(0.9f, 0.9f, 0.9f, 1.0f);
601  set_frame_style(S_focus, style);
602 
603  style.set_color(0.6f, 0.6f, 0.6f, 1.0f);
604  set_frame_style(S_inactive, style);
605 }
606 
607 /**
608  * Sets up the entry without creating any frame or other decoration.
609  */
610 void PGEntry::
611 setup_minimal(PN_stdfloat width, int num_lines) {
612  LightReMutexHolder holder(_lock);
613  set_text(string());
614  _cursor_position = 0;
615  set_max_chars(0);
616  set_max_width(width);
617  set_num_lines(num_lines);
618  update_text();
619 
620  _accept_enabled = true;
621 
622  TextNode *text_node = get_text_def(S_focus);
623  PN_stdfloat line_height = text_node->get_line_height();
624 
625  // Set up a default cursor: a vertical bar.
627 
628  LineSegs ls;
629  ls.set_color(text_node->get_text_color());
630  ls.move_to(0.0f, 0.0f, -0.15f * line_height);
631  ls.draw_to(0.0f, 0.0f, 0.70f * line_height);
633 
634  /*
635  // An underscore cursor would work too.
636  text_node->set_text("_");
637  get_cursor_def().attach_new_node(text_node->generate());
638  */
639 }
640 
641 /**
642  * Changes the TextNode that will be used to render the text within the entry
643  * when the entry is in the indicated state. The default if nothing is
644  * specified is the same TextNode returned by PGItem::get_text_node().
645  */
646 void PGEntry::
647 set_text_def(int state, TextNode *node) {
648  LightReMutexHolder holder(_lock);
649  nassertv(state >= 0 && state < 1000); // Sanity check.
650  if (node == nullptr && state >= (int)_text_defs.size()) {
651  // If we're setting it to NULL, we don't need to slot a new one.
652  return;
653  }
654  slot_text_def(state);
655 
656  _text_defs[state] = node;
657 }
658 
659 /**
660  * Returns the TextNode that will be used to render the text within the entry
661  * when the entry is in the indicated state. See set_text_def().
662  */
664 get_text_def(int state) const {
665  LightReMutexHolder holder(_lock);
666  if (state < 0 || state >= (int)_text_defs.size()) {
667  // If we don't have a definition, use the global one.
668  return get_text_node();
669  }
670  if (_text_defs[state] == nullptr) {
671  return get_text_node();
672  }
673  return _text_defs[state];
674 }
675 
676 /**
677  * Toggles the active/inactive state of the entry. In the case of a PGEntry,
678  * this also changes its visual appearance.
679  */
680 void PGEntry::
681 set_active(bool active) {
682  LightReMutexHolder holder(_lock);
683  PGItem::set_active(active);
684  update_state();
685 }
686 
687 /**
688  * Toggles the focus state of the entry. In the case of a PGEntry, this also
689  * changes its visual appearance.
690  */
691 void PGEntry::
692 set_focus(bool focus) {
693  LightReMutexHolder holder(_lock);
694  PGItem::set_focus(focus);
695  _blink_start = ClockObject::get_global_clock()->get_frame_time();
696  update_state();
697 }
698 
699 /**
700  * Returns true if any of the characters in the string returned by get_wtext()
701  * are out of the range of an ASCII character (and, therefore, get_wtext()
702  * should be called in preference to get_text()).
703  */
704 bool PGEntry::
705 is_wtext() const {
706  LightReMutexHolder holder(_lock);
707  for (int i = 0; i < _text.get_num_characters(); ++i) {
708  wchar_t ch = _text.get_character(i);
709  if ((ch & ~0x7f) != 0) {
710  return true;
711  }
712  }
713 
714  return false;
715 }
716 
717 /**
718  * Ensures there is a slot in the array for the given text definition.
719  */
720 void PGEntry::
721 slot_text_def(int state) {
722  while (state >= (int)_text_defs.size()) {
723  _text_defs.push_back(nullptr);
724  }
725 }
726 
727 /**
728  * Causes the PGEntry to recompute its text, if necessary.
729  */
730 void PGEntry::
731 update_text() {
732  TextNode *node = get_text_def(get_state());
733  nassertv(node != nullptr);
734 
735  if (_text_geom_stale || node != _last_text_def) {
736  TextProperties props = *node;
737  props.set_wordwrap(_max_width);
739  _text.set_properties(props);
740  _text.set_max_rows(_num_lines);
741 
742  if (node != _last_text_def) {
743  // Make sure the default properties are applied to all the characters in
744  // the text.
745  _text.set_wtext(_text.get_wtext());
746  _last_text_def = node;
747  }
748 
749  _text.set_multiline_mode (!(get_overflow_mode() && _num_lines == 1));
750 
751  PT(PandaNode) assembled;
752  if (_obscure_mode) {
753  _obscure_text.set_properties(props);
754  _obscure_text.set_max_rows(_num_lines);
755  _obscure_text.set_wtext(wstring(_text.get_num_characters(), '*'));
756  assembled = _obscure_text.assemble_text();
757 
758  } else if (_candidate_wtext.empty()) {
759  // If we're not trying to display a candidate string, it's easy: just
760  // display the current text contents.
761  assembled = _text.assemble_text();
762 
763  } else {
765  TextProperties inactive = tp_mgr->get_properties(_candidate_inactive);
766  TextProperties active = tp_mgr->get_properties(_candidate_active);
767 
768  // Insert the complex sequence of characters required to show the
769  // candidate string in a different color. This gets inserted at the
770  // current cursor position.
771  wstring cseq;
772  cseq += wstring(1, (wchar_t)text_push_properties_key);
773  cseq += node->decode_text(_candidate_inactive);
774  cseq += wstring(1, (wchar_t)text_push_properties_key);
775  cseq += _candidate_wtext.substr(0, _candidate_highlight_start);
776  cseq += wstring(1, (wchar_t)text_push_properties_key);
777  cseq += node->decode_text(_candidate_active);
778  cseq += wstring(1, (wchar_t)text_push_properties_key);
779  cseq += _candidate_wtext.substr(_candidate_highlight_start,
780  _candidate_highlight_end - _candidate_highlight_start);
781  cseq += wstring(1, (wchar_t)text_pop_properties_key);
782  cseq += _candidate_wtext.substr(_candidate_highlight_end);
783  cseq += wstring(1, (wchar_t)text_pop_properties_key);
784 
785  // Create a special TextAssembler to insert the candidate string.
786  TextAssembler ctext(_text);
787  ctext.set_wsubstr(cseq, _cursor_position, 0);
788  assembled = ctext.assemble_text();
789  }
790 
791  if (!_current_text.is_empty()) {
792  _current_text.remove_node();
793  }
794 
795  _current_text =
796  _text_render_root.attach_new_node(assembled);
797 
798  _current_text.set_mat(node->get_transform());
799 
800  if (get_overflow_mode() && _num_lines == 1){
801  // We determine the minimum required padding:
802  PN_stdfloat cursor_graphic_pos = _text.get_xpos(0, _cursor_position);
803  PN_stdfloat min_padding = (cursor_graphic_pos - _max_width);
804 
805 /*
806  * If the current padding would produce a caret outside the text entry, we
807  * relocate it. Here we also have to make a jump towards the center when the
808  * caret is going outside the visual area and there's enough text ahead for
809  * increased usability. The amount that the caret is moved for hinting
810  * depends on the OS, and the specific behavior under certain circunstances in
811  * different Operating Systems is very complicated (the implementation would
812  * need to "remember" the original typing starting point). For the moment we
813  * are gonna use an unconditional 50% jump, this behavior is found in some Mac
814  * dialogs, and it's the easiest to implement by far, while providing proven
815  * usability. PROS: Reduces the amount of scrolling while both writing and
816  * navigating with arrow keys, which is desirable. CONS: The user needs to
817  * remember that heshe has exceeded the boundaries, but this happens with all
818  * implementations to some degree.
819  */
820 
821  if (_current_padding < min_padding || _current_padding > cursor_graphic_pos){
822  _current_padding = min_padding + (cursor_graphic_pos - min_padding) * 0.5;
823  }
824 
825  if (_current_padding < 0){ // Caret virtual position doesn't exceed boundaries
826  _current_padding = 0;
827  }
828 
829  _current_text.set_x(_current_text.get_x() - _current_padding);
830  _current_text.set_scissor(NodePath(this),
831  LPoint3::rfu(0, 0, -0.5), LPoint3::rfu(_max_width, 0, -0.5),
832  LPoint3::rfu(_max_width, 0, 1.5), LPoint3::rfu(0, 0, 1.5));
833  }
834 
835  _text_geom_stale = false;
836  _text_geom_flattened = false;
837  _cursor_stale = true;
838  }
839 
840  // We'll flatten the text geometry only if we don't have focus. Otherwise,
841  // we assume the user may be changing it frequently.
842  if (!get_focus() && !_text_geom_flattened) {
843  _current_text.flatten_strong();
844  _text_geom_flattened = true;
845  }
846 }
847 
848 /**
849  * Moves the cursor to its correct position.
850  */
851 void PGEntry::
852 update_cursor() {
853  TextNode *node = get_text_def(get_state());
854  nassertv(node != nullptr);
855  _cursor_scale.set_mat(node->get_transform());
856  _cursor_scale.set_color(node->get_text_color());
857 
858  if (_cursor_stale || node != _last_text_def) {
859  update_text();
860 
861  _cursor_position = min(_cursor_position, _text.get_num_characters());
862 
863  // Determine the row and column of the cursor.
864  int row, column;
865  PN_stdfloat xpos, ypos;
866  if (_obscure_mode) {
867  _obscure_text.calc_r_c(row, column, _cursor_position);
868  xpos = _obscure_text.get_xpos(row, column);
869  ypos = _obscure_text.get_ypos(row, column);
870  } else {
871  _text.calc_r_c(row, column, _cursor_position);
872  if (_cursor_position > 0 && _text.get_character(_cursor_position - 1) == '\n') {
873  row += 1;
874  column = 0;
875  }
876  xpos = _text.get_xpos(row, column);
877  ypos = _text.get_ypos(row, column);
878  }
879 
880  _cursor_def.set_pos(xpos - _current_padding, 0.0f, ypos);
881  _cursor_stale = false;
882  cursormove();
883 
884  }
885 
886  // Should the cursor be visible?
887  if (!get_focus() || !_candidate_wtext.empty()) {
888  show_hide_cursor(false);
889  } else {
890  double elapsed_time =
891  ClockObject::get_global_clock()->get_frame_time() - _blink_start;
892  int cycle = (int)(elapsed_time * _blink_rate * 2.0f);
893  bool visible = ((cycle & 1) == 0);
894  show_hide_cursor(visible);
895  }
896 }
897 
898 /**
899  * Makes the cursor visible or invisible, e.g. during a blink cycle.
900  */
901 void PGEntry::
902 show_hide_cursor(bool visible) {
903  if (visible != _cursor_visible) {
904  if (visible) {
905  _cursor_scale.show();
906  } else {
907  _cursor_scale.hide();
908  }
909  _cursor_visible = visible;
910  }
911 }
912 
913 /**
914  * Determines what the correct state for the PGEntry should be.
915  */
916 void PGEntry::
917 update_state() {
918  if (get_active()) {
919  if (get_focus()) {
920  set_state(S_focus);
921  } else {
922  set_state(S_no_focus);
923  }
924  } else {
925  set_state(S_inactive);
926  }
927 }
MouseButton::one
static ButtonHandle one()
Returns the ButtonHandle associated with the first mouse button.
Definition: mouseButton.cxx:43
PGEntry::get_text_def
TextNode * get_text_def(int state) const
Returns the TextNode that will be used to render the text within the entry when the entry is in the i...
Definition: pgEntry.cxx:664
MouseWatcherParameter::get_highlight_end
size_t get_highlight_end() const
Returns one more than the last highlighted character in the candidate string.
Definition: mouseWatcherParameter.I:227
PGItem::candidate
virtual void candidate(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user highlights an option in the IME window.
Definition: pgItem.cxx:733
PGItem::get_text_node
static TextNode * get_text_node()
Returns the TextNode object that will be used by all PGItems to generate default labels given a strin...
Definition: pgItem.cxx:1055
PGEntry::accept
virtual void accept(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the entry is accepted by the user pressing Enter no...
Definition: pgEntry.cxx:444
PGItem::keystroke
virtual void keystroke(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user presses a key.
Definition: pgItem.cxx:709
PGItem::set_frame
void set_frame(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets the bounding rectangle of the item, in local coordinates.
Definition: pgItem.I:81
LineSegs
Encapsulates creation of a series of connected or disconnected line segments or points,...
Definition: lineSegs.h:33
cullTraverser.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextPropertiesManager::get_global_ptr
static TextPropertiesManager * get_global_ptr()
Returns the pointer to the global TextPropertiesManager object.
Definition: textPropertiesManager.cxx:197
LineSegs::draw_to
void draw_to(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Draws a line segment from the pen's last position (the last call to move_to or draw_to) to the indica...
Definition: lineSegs.I:94
throw_event.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextAssembler
This class is not normally used directly by user code, but is used by the TextNode to lay out a block...
Definition: textAssembler.h:43
MouseWatcherParameter::get_cursor_pos
size_t get_cursor_pos() const
Returns the position of the user's edit cursor within the candidate string.
Definition: mouseWatcherParameter.I:235
PGEntry::get_type_event
std::string get_type_event() const
Returns the event name that will be thrown whenever the user extends the text by typing.
Definition: pgEntry.I:486
MouseWatcherParameter::get_keycode
int get_keycode() const
Returns the keycode associated with this event.
Definition: mouseWatcherParameter.I:174
PGItem::press
virtual void press(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever a mouse or keyboard button is depressed while the m...
Definition: pgItem.cxx:657
MouseWatcherParameter
This is sent along as a parameter to most events generated for a region to indicate the mouse and but...
Definition: mouseWatcherParameter.h:28
PGItem::get_focus
bool get_focus() const
Returns whether the PGItem currently has focus for keyboard events.
Definition: pgItem.I:170
PGItem
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:53
PGEntry::type
virtual void type(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the user extends the text by typing.
Definition: pgEntry.cxx:486
PGEntry::accept_failed
virtual void accept_failed(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the user presses Enter but we can't accept the inpu...
Definition: pgEntry.cxx:458
PGEntry::set_focus
virtual void set_focus(bool focus)
Toggles the focus state of the entry.
Definition: pgEntry.cxx:692
TextAssembler::set_max_rows
set_max_rows
If max_rows is greater than zero, no more than max_rows will be accepted.
Definition: textAssembler.h:109
TextAssembler::get_xpos
PN_stdfloat get_xpos(int r, int c) const
Returns the x position of the origin of the character or graphic object at the indicated position in ...
Definition: textAssembler.cxx:486
PGEntry::set_active
virtual void set_active(bool active)
Toggles the active/inactive state of the entry.
Definition: pgEntry.cxx:681
LightReMutexHolder
Similar to MutexHolder, but for a light reentrant mutex.
Definition: lightReMutexHolder.h:25
MouseWatcherParameter::has_candidate
bool has_candidate() const
Returns true if this parameter has an associated candidate string, false otherwise.
Definition: mouseWatcherParameter.I:183
mouseButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseWatcherParameter::get_candidate_string
const std::wstring & get_candidate_string() const
Returns the candidate string associated with this event.
Definition: mouseWatcherParameter.I:192
PGEntry::press
virtual void press(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever a mouse or keyboard entry is depressed while the mo...
Definition: pgEntry.cxx:195
PGEntry::get_cursor_def
NodePath get_cursor_def()
Returns the Node that will be rendered to represent the cursor.
Definition: pgEntry.I:251
pgMouseWatcherParameter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ButtonHandle
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
PGEntry::get_accept_failed_event
std::string get_accept_failed_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the entry cannot accept an input.
Definition: pgEntry.I:467
ClockObject::get_global_clock
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
PGEntry::clear_cursor_def
void clear_cursor_def()
Removes all the children from the cursor_def node, in preparation for adding a new definition.
Definition: pgEntry.I:261
PGEntry::get_accept_event
std::string get_accept_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the entry is accepted normally.
Definition: pgEntry.I:458
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
PGEntry::cursormove
virtual void cursormove()
This is a callback hook function, called whenever the cursor moves.
Definition: pgEntry.cxx:511
PGEntry
This is a particular kind of PGItem that handles simple one-line or short multi-line text entries,...
Definition: pgEntry.h:37
MouseWatcherParameter::get_highlight_start
size_t get_highlight_start() const
Returns the first highlighted character in the candidate string.
Definition: mouseWatcherParameter.I:218
PandaNode::set_state
set_state
Sets the complete RenderState that will be applied to all nodes at this level and below.
Definition: pandaNode.h:173
NodePath::get_transform
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:758
PGItem::set_focus
virtual void set_focus(bool focus)
Sets whether the PGItem currently has keyboard focus.
Definition: pgItem.cxx:851
TextNode
The primary interface to this module.
Definition: textNode.h:48
PGEntry::erase
virtual void erase(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the user erase characters in the text.
Definition: pgEntry.cxx:499
keyboardButton.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextProperties
This defines the set of visual properties that may be assigned to the individual characters of the te...
Definition: textProperties.h:41
PGEntry::set_max_width
void set_max_width(PN_stdfloat max_width)
Sets the maximum width of all characters that may be typed into the entry.
Definition: pgEntry.I:184
PGMouseWatcherParameter
This specialization on MouseWatcherParameter allows us to tag on additional elements to events for th...
Definition: pgMouseWatcherParameter.h:27
TextAssembler::set_wtext
bool set_wtext(const std::wstring &wtext)
Accepts a new text string and associated properties structure, and precomputes the wordwrapping layou...
Definition: textAssembler.cxx:170
TextNode::get_line_height
PN_stdfloat get_line_height() const
Returns the number of units high each line of text is.
Definition: textNode.I:20
PGEntry::cull_callback
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
Definition: pgEntry.cxx:176
PGEntry::candidate
virtual void candidate(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user selects an item from the IME menu.
Definition: pgEntry.cxx:421
pgEntry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseWatcherParameter::has_button
bool has_button() const
Returns true if this parameter has an associated mouse or keyboard button, false otherwise.
Definition: mouseWatcherParameter.I:139
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
NodePath::show
void show()
Undoes the effect of a previous hide() on this node: makes the referenced node (and the entire subgra...
Definition: nodePath.I:1788
NodePath::set_scissor
void set_scissor(PN_stdfloat left, PN_stdfloat right, PN_stdfloat bottom, PN_stdfloat top)
Sets up a scissor region on the nodes rendered at this level and below.
Definition: nodePath.cxx:2709
cullTraverserData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PGEntry::set_text_def
void set_text_def(int state, TextNode *node)
Changes the TextNode that will be used to render the text within the entry when the entry is in the i...
Definition: pgEntry.cxx:647
PGEntry::get_max_chars
int get_max_chars() const
Returns the current maximum number of characters that may be typed into the entry,...
Definition: pgEntry.I:167
MouseButton::five
static ButtonHandle five()
Returns the ButtonHandle associated with the fifth mouse button.
Definition: mouseButton.cxx:75
transformState.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MouseWatcherParameter::has_keycode
bool has_keycode() const
Returns true if this parameter has an associated keycode, false otherwise.
Definition: mouseWatcherParameter.I:165
PGFrameStyle::set_width
void set_width(PN_stdfloat x, PN_stdfloat y)
Sets the width parameter, which has meaning only for certain frame types.
Definition: pgFrameStyle.I:139
PGFrameStyle
Definition: pgFrameStyle.h:29
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
MouseButton::four
static ButtonHandle four()
Returns the ButtonHandle associated with the fourth mouse button.
Definition: mouseButton.cxx:67
TextAssembler::get_wtext
std::wstring get_wtext() const
Returns a wstring that represents the contents of the text.
Definition: textAssembler.cxx:295
LineSegs::move_to
void move_to(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Moves the pen to the given point without drawing a line.
Definition: lineSegs.I:84
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
LineSegs::set_color
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a=1.0f)
Establishes the color that will be assigned to all vertices created by future calls to move_to() and ...
Definition: lineSegs.I:56
PGEntry::get_cursormove_event
std::string get_cursormove_event() const
Returns the event name that will be thrown whenever the cursor moves.
Definition: pgEntry.I:503
NodePath::set_mat
void set_mat(const LMatrix4 &mat)
Directly sets an arbitrary 4x4 transform matrix.
Definition: nodePath.cxx:1292
PGItem::get_active
bool get_active() const
Returns whether the PGItem is currently active for mouse events.
Definition: pgItem.I:160
TextPropertiesManager::get_properties
TextProperties get_properties(const std::string &name)
Returns the TextProperties associated with the indicated name.
Definition: textPropertiesManager.cxx:62
NodePath::flatten_strong
int flatten_strong()
The strongest possible flattening.
Definition: nodePath.cxx:5558
config_text.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PGEntry::overflow
virtual void overflow(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the entry is overflowed because the user attempts t...
Definition: pgEntry.cxx:473
PGEntry::set_max_chars
void set_max_chars(int max_chars)
Sets the maximum number of characters that may be typed into the entry.
Definition: pgEntry.I:157
PGFrameStyle::set_color
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a)
Sets the dominant color of the frame.
Definition: pgFrameStyle.I:80
PGEntry::set_num_lines
void set_num_lines(int num_lines)
Sets the number of lines of text the PGEntry will use.
Definition: pgEntry.I:206
PGEntry::xform
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this node by the indicated matrix, if it means anything to do so.
Definition: pgEntry.cxx:151
TextProperties::set_preserve_trailing_whitespace
set_preserve_trailing_whitespace
Sets the preserve_trailing_whitespace flag.
Definition: textProperties.h:193
PGItem::get_state
int get_state() const
Returns the "state" of this particular PGItem.
Definition: pgItem.I:150
PGEntry::get_overflow_event
std::string get_overflow_event() const
Returns the event name that will be thrown when too much text is attempted to be entered into the PGE...
Definition: pgEntry.I:477
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
PGItem::set_frame_style
void set_frame_style(int state, const PGFrameStyle &style)
Changes the kind of frame that will be drawn behind the item when it is in the indicated state.
Definition: pgItem.cxx:992
PGEntry::get_erase_event
std::string get_erase_event() const
Returns the event name that will be thrown whenever the user erases characters in the text.
Definition: pgEntry.I:495
TextAssembler::get_ypos
PN_stdfloat get_ypos(int r, int c) const
Returns the y position of the origin of all of the characters or graphic objects in the indicated row...
Definition: textAssembler.I:285
EventParameter
An optional parameter associated with an event.
Definition: eventParameter.h:35
PGEntry::setup_minimal
void setup_minimal(PN_stdfloat width, int num_lines)
Sets up the entry without creating any frame or other decoration.
Definition: pgEntry.cxx:611
TextAssembler::set_multiline_mode
set_multiline_mode
Sets the multiline mode flag.
Definition: textAssembler.h:111
TextAssembler::get_num_cols
int get_num_cols(int r) const
Returns the number of characters and/or graphic objects in the nth row.
Definition: textAssembler.I:222
PGEntry::is_wtext
bool is_wtext() const
Returns true if any of the characters in the string returned by get_wtext() are out of the range of a...
Definition: pgEntry.cxx:705
ClockObject::get_frame_time
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
PGEntry::get_overflow_mode
bool get_overflow_mode() const
Specifies whether overflow mode is enabled.
Definition: pgEntry.I:335
PGItem::set_active
virtual void set_active(bool active)
Sets whether the PGItem is active for mouse watching.
Definition: pgItem.cxx:828
NodePath::get_mat
const LMatrix4 & get_mat() const
Returns the transform matrix that has been applied to the referenced node, or the identity matrix if ...
Definition: nodePath.I:776
PGFrameStyle::set_type
void set_type(Type type)
Sets the basic type of frame.
Definition: pgFrameStyle.I:64
lineSegs.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextAssembler::get_character
wchar_t get_character(int n) const
Returns the character at the indicated position in the pre-wordwrapped string.
Definition: textAssembler.I:172
NodePath::copy_to
NodePath copy_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Functions like instance_to(), except a deep copy is made of the referenced node and all of its descen...
Definition: nodePath.cxx:535
MouseWatcherParameter::get_button
ButtonHandle get_button() const
Returns the mouse or keyboard button associated with this event.
Definition: mouseWatcherParameter.I:148
NodePath::attach_new_node
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:563
TextAssembler::set_wsubstr
bool set_wsubstr(const std::wstring &wtext, int start, int count)
Replaces the 'count' characters from 'start' of the current text with the indicated replacement text.
Definition: textAssembler.cxx:205
TextAssembler::get_num_characters
int get_num_characters() const
Returns the number of characters of text, before wordwrapping.
Definition: textAssembler.I:162
PGEntry::set_text
bool set_text(const std::string &text)
Changes the text currently displayed within the entry.
Definition: pgEntry.I:23
NodePath::hide
void hide()
Makes the referenced node (and the entire subgraph below this node) invisible to all cameras.
Definition: nodePath.I:1843
MouseButton::two
static ButtonHandle two()
Returns the ButtonHandle associated with the second mouse button.
Definition: mouseButton.cxx:51
MouseButton::three
static ButtonHandle three()
Returns the ButtonHandle associated with the third mouse button.
Definition: mouseButton.cxx:59
PGEntry::setup
void setup(PN_stdfloat width, int num_lines)
Sets up the entry for normal use.
Definition: pgEntry.cxx:524
NodePath::set_color
void set_color(PN_stdfloat r, PN_stdfloat g, PN_stdfloat b, PN_stdfloat a=1.0, int priority=0)
Applies a scene-graph color to the referenced node.
Definition: nodePath.cxx:1952
CullTraverser::traverse
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
Definition: cullTraverser.cxx:106
PGItem::get_background_focus
bool get_background_focus() const
Returns whether background_focus is currently enabled.
Definition: pgItem.I:180
PandaNode
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
LineSegs::create
GeomNode * create(bool dynamic=false)
Creates a new GeomNode that will render the series of line segments and points described via calls to...
Definition: lineSegs.I:108
NodePath::node
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
TextProperties::set_wordwrap
set_wordwrap
Sets the text up to automatically wordwrap when it exceeds the indicated width.
Definition: textProperties.h:190
mouseWatcherParameter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextEncoder::decode_text
std::wstring decode_text(const std::string &text) const
Returns the given wstring decoded to a single-byte string, via the current encoding system.
Definition: textEncoder.I:490
TextPropertiesManager
This defines all of the TextProperties structures that might be referenced by name from an embedded t...
Definition: textPropertiesManager.h:44
textEncoder.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath::remove_node
void remove_node(Thread *current_thread=Thread::get_current_thread())
Disconnects the referenced node from the scene graph.
Definition: nodePath.cxx:591
PGEntry::keystroke
virtual void keystroke(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever the user types a key.
Definition: pgEntry.cxx:315
TextAssembler::get_num_rows
int get_num_rows() const
Returns the number of rows of text after it has all been wordwrapped and assembled.
Definition: textAssembler.I:214
TextAssembler::set_properties
set_properties
Specifies the default TextProperties that are applied to the text in the absence of any nested proper...
Definition: textAssembler.h:112
NodePath::is_empty
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
TextAssembler::calc_r_c
bool calc_r_c(int &r, int &c, int n) const
Computes the row and column index of the nth character or graphic object in the text.
Definition: textAssembler.cxx:374
PGEntry::make_copy
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: pgEntry.cxx:141