Panda3D
pgItem.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 pgItem.cxx
10  * @author drose
11  * @date 2002-03-13
12  */
13 
14 #include "pgItem.h"
16 #include "pgCullTraverser.h"
17 #include "config_pgui.h"
18 #include "boundingVolume.h"
19 #include "pandaNode.h"
20 #include "sceneGraphReducer.h"
21 #include "throw_event.h"
22 #include "string_utils.h"
23 #include "nodePath.h"
24 #include "cullTraverser.h"
25 #include "cullTraverserData.h"
26 #include "cullBinManager.h"
27 #include "clipPlaneAttrib.h"
28 #include "scissorAttrib.h"
29 #include "dcast.h"
30 #include "boundingSphere.h"
31 #include "boundingBox.h"
32 #include "config_mathutil.h"
33 
34 #ifdef HAVE_AUDIO
35 #include "audioSound.h"
36 #endif
37 
38 using std::string;
39 
40 TypeHandle PGItem::_type_handle;
41 PT(TextNode) PGItem::_text_node;
42 PGItem *PGItem::_focus_item = nullptr;
43 PGItem::BackgroundFocus PGItem::_background_focus;
44 
45 
46 /**
47  * Returns true if the 2-d v1 is to the right of v2.
48  */
49 INLINE bool
50 is_right(const LVector2 &v1, const LVector2 &v2) {
51  return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
52 }
53 
54 /**
55  *
56  */
57 PGItem::
58 PGItem(const string &name) :
59  PandaNode(name),
60  _lock(name),
61  _notify(nullptr),
62  _has_frame(false),
63  _frame(0, 0, 0, 0),
64  _region(new PGMouseWatcherRegion(this)),
65  _state(0),
66  _flags(0)
67 {
68  set_cull_callback();
69 }
70 
71 /**
72  *
73  */
74 PGItem::
75 ~PGItem() {
76  if (_notify != nullptr) {
77  _notify->remove_item(this);
78  _notify = nullptr;
79  }
80 
81  nassertv(_region->_item == this);
82  _region->_item = nullptr;
83 
84  set_background_focus(false);
85  if (_focus_item == this) {
86  _focus_item = nullptr;
87  }
88 }
89 
90 /**
91  *
92  */
93 PGItem::
94 PGItem(const PGItem &copy) :
95  PandaNode(copy),
96  _notify(nullptr),
97  _has_frame(copy._has_frame),
98  _frame(copy._frame),
99  _state(copy._state),
100  _flags(copy._flags),
101  _region(new PGMouseWatcherRegion(this))
102 #ifdef HAVE_AUDIO
103  , _sounds(copy._sounds)
104 #endif
105 {
106  // We give our region the same name as the region for the PGItem we're
107  // copying--so that this PGItem will generate the same event names when the
108  // user interacts with it.
109  _region->set_name(copy._region->get_name());
110 
111  // Make a deep copy of all of the original PGItem's StateDefs.
112  size_t num_state_defs = copy._state_defs.size();
113  _state_defs.reserve(num_state_defs);
114  for (size_t i = 0; i < num_state_defs; ++i) {
115  // We cheat and cast away the const, because the frame is just a cache.
116  // But we have to get the frame out of the source before we can safely
117  // copy it.
118  StateDef &old_sd = (StateDef &)(copy._state_defs[i]);
119  old_sd._frame.remove_node();
120  old_sd._frame_stale = true;
121 
122  StateDef new_sd;
123  new_sd._root = old_sd._root.copy_to(NodePath());
124  new_sd._frame_style = old_sd._frame_style;
125 
126  _state_defs.push_back(new_sd);
127  }
128 }
129 
130 /**
131  * Returns a newly-allocated Node that is a shallow copy of this one. It will
132  * be a different Node pointer, but its internal data may or may not be shared
133  * with that of the original Node.
134  */
135 PandaNode *PGItem::
136 make_copy() const {
137  LightReMutexHolder holder(_lock);
138  return new PGItem(*this);
139 }
140 
141 /**
142  * Called after the node's transform has been changed for any reason, this
143  * just provides a hook so derived classes can do something special in this
144  * case.
145  */
146 void PGItem::
147 transform_changed() {
148  LightReMutexHolder holder(_lock);
149  PandaNode::transform_changed();
150  if (_notify != nullptr) {
151  _notify->item_transform_changed(this);
152  }
153 }
154 
155 /**
156  * Called after the node's draw_mask has been changed for any reason, this
157  * just provides a hook so derived classes can do something special in this
158  * case.
159  */
160 void PGItem::
161 draw_mask_changed() {
162  LightReMutexHolder holder(_lock);
163  PandaNode::draw_mask_changed();
164  if (_notify != nullptr) {
165  _notify->item_draw_mask_changed(this);
166  }
167 }
168 
169 /**
170  * This function will be called during the cull traversal to perform any
171  * additional operations that should be performed at cull time. This may
172  * include additional manipulation of render state or additional
173  * visible/invisible decisions, or any other arbitrary operation.
174  *
175  * Note that this function will *not* be called unless set_cull_callback() is
176  * called in the constructor of the derived class. It is necessary to call
177  * set_cull_callback() to indicated that we require cull_callback() to be
178  * called.
179  *
180  * By the time this function is called, the node has already passed the
181  * bounding-volume test for the viewing frustum, and the node's transform and
182  * state have already been applied to the indicated CullTraverserData object.
183  *
184  * The return value is true if this node should be visible, or false if it
185  * should be culled.
186  */
187 bool PGItem::
188 cull_callback(CullTraverser *trav, CullTraverserData &data) {
189  // We try not to hold the lock for longer than necessary.
190  PT(PandaNode) state_def_root;
191  bool has_frame;
192  PGMouseWatcherRegion *region;
193  {
194  LightReMutexHolder holder(_lock);
195  has_frame = _has_frame && ((_flags & F_active) != 0);
196  region = _region;
197 
198  int state = _state;
199  if (state >= 0 && (size_t)state < _state_defs.size()) {
200  StateDef &state_def = _state_defs[state];
201  if (!state_def._root.is_empty()) {
202  if (state_def._frame_stale) {
203  update_frame(state);
204  }
205 
206  state_def_root = state_def._root.node();
207  }
208  }
209  }
210 
211  if (has_frame && !data.is_this_node_hidden(trav->get_camera_mask())) {
212  // The item has a frame, so we want to generate a region for it and update
213  // the MouseWatcher.
214 
215  // We can only do this if our traverser is a PGCullTraverser (which will
216  // be the case if this node was parented somewhere under a PGTop node).
217  if (trav->is_exact_type(PGCullTraverser::get_class_type())) {
218  PGCullTraverser *pg_trav;
219  DCAST_INTO_R(pg_trav, trav, true);
220 
221  const LMatrix4 &transform = data.get_net_transform(trav)->get_mat();
222 
223  // Consider the cull bin this object is in. Since the binning affects
224  // the render order, we want bins that render later to get higher sort
225  // values.
226  int bin_index = data._state->get_bin_index();
227  int sort;
228 
230  CullBinManager::BinType bin_type = bin_manager->get_bin_type(bin_index);
231  if (bin_type == CullBinManager::BT_fixed) {
232  // If the bin is a "fixed" type bin, our local sort is based on the
233  // fixed order.
234  sort = data._state->get_draw_order();
235 
236  } else if (bin_type == CullBinManager::BT_unsorted) {
237  // If the bin is an "unsorted" type bin, we base the local sort on the
238  // scene graph order.
239  sort = pg_trav->_sort_index;
240  pg_trav->_sort_index++;
241 
242  } else {
243  // Otherwise, the local sort is irrelevant.
244  sort = 0;
245  }
246 
247  // Now what order does this bin sort relative to the other bins? This
248  // becomes the high-order part of the final sort count.
249  int bin_sort = bin_manager->get_bin_sort(data._state->get_bin_index());
250 
251  // Combine the two sorts into a single int. This assumes we only need
252  // 16 bits for each sort number, possibly an erroneous assumption. We
253  // should really provide two separate sort values, both ints, in the
254  // MouseWatcherRegion; but in the interest of expediency we work within
255  // the existing interface which only provides one.
256  sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
257 
258  const ClipPlaneAttrib *clip = nullptr;
259  const ScissorAttrib *scissor = nullptr;
260  data._state->get_attrib(clip);
261  data._state->get_attrib(scissor);
262  if (activate_region(transform, sort, clip, scissor)) {
263  pg_trav->_top->add_region(region);
264  }
265  }
266  }
267 
268  if (state_def_root != nullptr) {
269  // This item has a current state definition that we should use to render
270  // the item.
271  CullTraverserData next_data(data, state_def_root);
272  trav->traverse(next_data);
273  }
274 
275  // Now continue to render everything else below this node.
276  return true;
277 }
278 
279 /**
280  * Returns true if there is some value to visiting this particular node during
281  * the cull traversal for any camera, false otherwise. This will be used to
282  * optimize the result of get_net_draw_show_mask(), so that any subtrees that
283  * contain only nodes for which is_renderable() is false need not be visited.
284  */
285 bool PGItem::
286 is_renderable() const {
287  return true;
288 }
289 
290 /**
291  * Called when needed to recompute the node's _internal_bound object. Nodes
292  * that contain anything of substance should redefine this to do the right
293  * thing.
294  */
295 void PGItem::
296 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
297  int &internal_vertices,
298  int pipeline_stage,
299  Thread *current_thread) const {
300  LightReMutexHolder holder(_lock, current_thread);
301  int num_vertices = 0;
302 
303  // First, get ourselves a fresh, empty bounding volume.
304  PT(BoundingVolume) bound;
305 
306  BoundingVolume::BoundsType btype = get_bounds_type();
307  if (btype == BoundingVolume::BT_default) {
308  btype = bounds_type;
309  }
310 
311  if (btype == BoundingVolume::BT_sphere) {
312  bound = new BoundingSphere;
313  } else {
314  bound = new BoundingBox;
315  }
316 
317  // Now actually compute the bounding volume by putting it around all of our
318  // states' bounding volumes.
319  pvector<const BoundingVolume *> child_volumes;
320 
321  // We walk through the list of state defs indirectly, calling
322  // get_state_def() on each one, to ensure that the frames are updated
323  // correctly before we measure their bounding volumes.
324  for (int i = 0; i < (int)_state_defs.size(); i++) {
325  NodePath &root = ((PGItem *)this)->do_get_state_def(i);
326  if (!root.is_empty()) {
327  PandaNode *node = root.node();
328  child_volumes.push_back(node->get_bounds(current_thread));
329  num_vertices += node->get_nested_vertices(current_thread);
330  }
331  }
332 
333  const BoundingVolume **child_begin = &child_volumes[0];
334  const BoundingVolume **child_end = child_begin + child_volumes.size();
335 
336  bound->around(child_begin, child_end);
337 
338  internal_bounds = bound;
339  internal_vertices = num_vertices;
340 }
341 
342 /**
343  * The recursive implementation of prepare_scene(). Don't call this directly;
344  * call PandaNode::prepare_scene() or NodePath::prepare_scene() instead.
345  */
346 void PGItem::
347 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
348  GeomTransformer &transformer, Thread *current_thread) {
349  LightReMutexHolder holder(_lock);
350  StateDefs::iterator di;
351  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
352  NodePath &root = (*di)._root;
353  if (!root.is_empty()) {
354  PandaNode *child = root.node();
355  CPT(RenderState) child_state = node_state->compose(child->get_state());
356  child->r_prepare_scene(gsg, child_state, transformer, current_thread);
357  }
358  }
359 
360  PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
361 }
362 
363 /**
364  * Transforms the contents of this node by the indicated matrix, if it means
365  * anything to do so. For most kinds of nodes, this does nothing.
366  */
367 void PGItem::
368 xform(const LMatrix4 &mat) {
369  LightReMutexHolder holder(_lock);
370  // Transform the frame.
371  LPoint3 ll(_frame[0], 0.0f, _frame[2]);
372  LPoint3 ur(_frame[1], 0.0f, _frame[3]);
373  ll = ll * mat;
374  ur = ur * mat;
375  _frame.set(ll[0], ur[0], ll[2], ur[2]);
376 
377  // Transform the individual states and their frame styles.
378  StateDefs::iterator di;
379  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
380  NodePath &root = (*di)._root;
381  // Apply the matrix to the previous transform.
382  root.set_transform(root.get_transform()->compose(TransformState::make_mat(mat)));
383 
384  // Now flatten the transform into the subgraph.
386  gr.apply_attribs(root.node());
387 
388  // Transform the frame style too.
389  if ((*di)._frame_style.xform(mat)) {
390  (*di)._frame_stale = true;
391  }
392  }
393  mark_internal_bounds_stale();
394 }
395 
396 /**
397  * Applies the indicated scene graph transform and order as determined by the
398  * traversal from PGTop.
399  *
400  * The return value is true if the region is valid, or false if it is empty or
401  * completely clipped.
402  */
403 bool PGItem::
404 activate_region(const LMatrix4 &transform, int sort,
405  const ClipPlaneAttrib *cpa,
406  const ScissorAttrib *sa) {
407  using std::min;
408  using std::max;
409 
410  LightReMutexHolder holder(_lock);
411  // Transform all four vertices, and get the new bounding box. This way the
412  // region works (mostly) even if has been rotated.
413  LPoint3 ll = LPoint3::rfu(_frame[0], 0.0f, _frame[2]) * transform;
414  LPoint3 lr = LPoint3::rfu(_frame[1], 0.0f, _frame[2]) * transform;
415  LPoint3 ul = LPoint3::rfu(_frame[0], 0.0f, _frame[3]) * transform;
416  LPoint3 ur = LPoint3::rfu(_frame[1], 0.0f, _frame[3]) * transform;
417  LVector3 up = LVector3::up();
418  int up_axis;
419  if (up[1]) {
420  up_axis = 1;
421  }
422  else if (up[2]) {
423  up_axis = 2;
424  }
425  else {
426  up_axis = 0;
427  }
428  LVector3 right = LVector3::right();
429  int right_axis;
430  if (right[0]) {
431  right_axis = 0;
432  }
433  else if (right[2]) {
434  right_axis = 2;
435  }
436  else {
437  right_axis = 1;
438  }
439 
440  LVecBase4 frame;
441  if (cpa != nullptr && cpa->get_num_on_planes() != 0) {
442  // Apply the clip plane(s) andor scissor region now that we are here in
443  // world space.
444 
445  ClipPoints points;
446  points.reserve(4);
447  points.push_back(LPoint2(ll[right_axis], ll[up_axis]));
448  points.push_back(LPoint2(lr[right_axis], lr[up_axis]));
449  points.push_back(LPoint2(ur[right_axis], ur[up_axis]));
450  points.push_back(LPoint2(ul[right_axis], ul[up_axis]));
451 
452  int num_on_planes = cpa->get_num_on_planes();
453  for (int i = 0; i < num_on_planes; ++i) {
454  NodePath plane_path = cpa->get_on_plane(i);
455  LPlane plane = DCAST(PlaneNode, plane_path.node())->get_plane();
456  plane.xform(plane_path.get_net_transform()->get_mat());
457 
458  // We ignore the forward axis, assuming the frame is still in the right-
459  // up plane after being transformed. Not sure if we really need to
460  // support general 3-D transforms on 2-D objects.
461  clip_frame(points, plane);
462  }
463 
464  if (points.empty()) {
465  // Turns out it's completely clipped after all.
466  return false;
467  }
468 
469  ClipPoints::iterator pi;
470  pi = points.begin();
471  frame.set((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
472  ++pi;
473  while (pi != points.end()) {
474  frame[0] = min(frame[0], (*pi)[0]);
475  frame[1] = max(frame[1], (*pi)[0]);
476  frame[2] = min(frame[2], (*pi)[1]);
477  frame[3] = max(frame[3], (*pi)[1]);
478  ++pi;
479  }
480  } else {
481  // Since there are no clip planes involved, just set the frame.
482  frame.set(min(min(ll[right_axis], lr[right_axis]), min(ul[right_axis], ur[right_axis])),
483  max(max(ll[right_axis], lr[right_axis]), max(ul[right_axis], ur[right_axis])),
484  min(min(ll[up_axis], lr[up_axis]), min(ul[up_axis], ur[up_axis])),
485  max(max(ll[up_axis], lr[up_axis]), max(ul[up_axis], ur[up_axis])));
486  }
487 
488  if (sa != nullptr) {
489  // Also restrict it to within the scissor region.
490  const LVecBase4 &sf = sa->get_frame();
491  // Expand sf from 0..1 to -1..1.
492  frame.set(max(frame[0], sf[0] * 2.0f - 1.0f),
493  min(frame[1], sf[1] * 2.0f - 1.0f),
494  max(frame[2], sf[2] * 2.0f - 1.0f),
495  min(frame[3], sf[3] * 2.0f - 1.0f));
496  if (frame[1] <= frame[0] || frame[3] <= frame[2]) {
497  // Completely outside the scissor region.
498  return false;
499  }
500  }
501 
502  _region->set_frame(frame);
503 
504  _region->set_sort(sort);
505  _region->set_active(true);
506 
507  // calculate the inverse of this transform, which is needed to go back to
508  // the frame space.
509  _frame_inv_xform.invert_from(transform);
510 
511  return true;
512 }
513 
514 /**
515  * This is a callback hook function, called whenever the mouse enters the
516  * region. The mouse is only considered to be "entered" in one region at a
517  * time; in the case of nested regions, it exits the outer region before
518  * entering the inner one.
519  */
520 void PGItem::
522  LightReMutexHolder holder(_lock);
523  if (pgui_cat.is_debug()) {
524  pgui_cat.debug()
525  << *this << "::enter_region(" << param << ")\n";
526  }
527 
529  string event = get_enter_event();
530  play_sound(event);
531  throw_event(event, EventParameter(ep));
532 
533  if (_notify != nullptr) {
534  _notify->item_enter(this, param);
535  }
536 }
537 
538 /**
539  * This is a callback hook function, called whenever the mouse exits the
540  * region. The mouse is only considered to be "entered" in one region at a
541  * time; in the case of nested regions, it exits the outer region before
542  * entering the inner one.
543  */
544 void PGItem::
546  LightReMutexHolder holder(_lock);
547  if (pgui_cat.is_debug()) {
548  pgui_cat.debug()
549  << *this << "::exit_region(" << param << ")\n";
550  }
551 
553  string event = get_exit_event();
554  play_sound(event);
555  throw_event(event, EventParameter(ep));
556 
557  if (_notify != nullptr) {
558  _notify->item_exit(this, param);
559  }
560 
561  // pgui_cat.info() << get_name() << "::exit()" << endl;
562 }
563 
564 /**
565  * This is a callback hook function, called whenever the mouse moves within
566  * the boundaries of the region, even if it is also within the boundaries of a
567  * nested region. This is different from "enter", which is only called
568  * whenever the mouse is within only that region.
569  */
570 void PGItem::
572  LightReMutexHolder holder(_lock);
573  if (pgui_cat.is_debug()) {
574  pgui_cat.debug()
575  << *this << "::within_region(" << param << ")\n";
576  }
577 
579  string event = get_within_event();
580  play_sound(event);
581  throw_event(event, EventParameter(ep));
582 
583  if (_notify != nullptr) {
584  _notify->item_within(this, param);
585  }
586 }
587 
588 /**
589  * This is a callback hook function, called whenever the mouse moves
590  * completely outside the boundaries of the region. See within().
591  */
592 void PGItem::
594  LightReMutexHolder holder(_lock);
595  if (pgui_cat.is_debug()) {
596  pgui_cat.debug()
597  << *this << "::without_region(" << param << ")\n";
598  }
599 
601  string event = get_without_event();
602  play_sound(event);
603  throw_event(event, EventParameter(ep));
604 
605  if (_notify != nullptr) {
606  _notify->item_without(this, param);
607  }
608 }
609 
610 /**
611  * This is a callback hook function, called whenever the widget gets the
612  * keyboard focus.
613  */
614 void PGItem::
616  LightReMutexHolder holder(_lock);
617  if (pgui_cat.is_debug()) {
618  pgui_cat.debug()
619  << *this << "::focus_in()\n";
620  }
621 
622  string event = get_focus_in_event();
623  play_sound(event);
624  throw_event(event);
625 
626  if (_notify != nullptr) {
627  _notify->item_focus_in(this);
628  }
629 }
630 
631 /**
632  * This is a callback hook function, called whenever the widget loses the
633  * keyboard focus.
634  */
635 void PGItem::
637  LightReMutexHolder holder(_lock);
638  if (pgui_cat.is_debug()) {
639  pgui_cat.debug()
640  << *this << "::focus_out()\n";
641  }
642 
643  string event = get_focus_out_event();
644  play_sound(event);
645  throw_event(event);
646 
647  if (_notify != nullptr) {
648  _notify->item_focus_out(this);
649  }
650 }
651 
652 /**
653  * This is a callback hook function, called whenever a mouse or keyboard
654  * button is depressed while the mouse is within the region.
655  */
656 void PGItem::
657 press(const MouseWatcherParameter &param, bool background) {
658  LightReMutexHolder holder(_lock);
659  if (pgui_cat.is_debug()) {
660  pgui_cat.debug()
661  << *this << "::press(" << param << ", " << background << ")\n";
662  }
663 
664  if (!background) {
666  string event;
667  if (param.is_keyrepeat()) {
668  event = get_repeat_event(param.get_button());
669  } else {
670  event = get_press_event(param.get_button());
671  }
672  play_sound(event);
673  throw_event(event, EventParameter(ep));
674  }
675 
676  if (_notify != nullptr) {
677  _notify->item_press(this, param);
678  }
679 }
680 
681 /**
682  * This is a callback hook function, called whenever a mouse or keyboard
683  * button previously depressed with press() is released.
684  */
685 void PGItem::
686 release(const MouseWatcherParameter &param, bool background) {
687  LightReMutexHolder holder(_lock);
688  if (pgui_cat.is_debug()) {
689  pgui_cat.debug()
690  << *this << "::release(" << param << ", " << background << ")\n";
691  }
692 
693  if (!background) {
695  string event = get_release_event(param.get_button());
696  play_sound(event);
697  throw_event(event, EventParameter(ep));
698  }
699 
700  if (_notify != nullptr) {
701  _notify->item_release(this, param);
702  }
703 }
704 
705 /**
706  * This is a callback hook function, called whenever the user presses a key.
707  */
708 void PGItem::
709 keystroke(const MouseWatcherParameter &param, bool background) {
710  LightReMutexHolder holder(_lock);
711  if (pgui_cat.is_debug()) {
712  pgui_cat.debug()
713  << *this << "::keystroke(" << param << ", " << background << ")\n";
714  }
715 
716  if (!background) {
718  string event = get_keystroke_event();
719  play_sound(event);
720  throw_event(event, EventParameter(ep));
721 
722  if (has_notify()) {
723  get_notify()->item_keystroke(this, param);
724  }
725  }
726 }
727 
728 /**
729  * This is a callback hook function, called whenever the user highlights an
730  * option in the IME window.
731  */
732 void PGItem::
733 candidate(const MouseWatcherParameter &param, bool background) {
734  LightReMutexHolder holder(_lock);
735  if (pgui_cat.is_debug()) {
736  pgui_cat.debug()
737  << *this << "::candidate(" << param << ", " << background << ")\n";
738  }
739 
740  // We don't throw sound events for candidate selections for now.
741  if (!background) {
742  if (has_notify()) {
743  get_notify()->item_candidate(this, param);
744  }
745  }
746 }
747 
748 /**
749  * This is a callback hook function, called whenever a mouse is moved while
750  * within the region.
751  */
752 void PGItem::
754  LightReMutexHolder holder(_lock);
755  if (pgui_cat.is_debug()) {
756  pgui_cat.debug()
757  << *this << "::move(" << param << ")\n";
758  }
759 
760  if (_notify != nullptr) {
761  _notify->item_move(this, param);
762  }
763 }
764 
765 /**
766  * Calls press() on all the PGItems with background focus.
767  */
768 void PGItem::
770  BackgroundFocus::const_iterator fi;
771  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
772  PGItem *item = *fi;
773  if (!item->get_focus()) {
774  item->press(param, true);
775  }
776  }
777 }
778 
779 /**
780  * Calls release() on all the PGItems with background focus.
781  */
782 void PGItem::
784  BackgroundFocus::const_iterator fi;
785  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
786  PGItem *item = *fi;
787  if (!item->get_focus()) {
788  item->release(param, true);
789  }
790  }
791 }
792 
793 /**
794  * Calls keystroke() on all the PGItems with background focus.
795  */
796 void PGItem::
798  BackgroundFocus::const_iterator fi;
799  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
800  PGItem *item = *fi;
801  if (!item->get_focus()) {
802  item->keystroke(param, true);
803  }
804  }
805 }
806 
807 /**
808  * Calls candidate() on all the PGItems with background focus.
809  */
810 void PGItem::
812  BackgroundFocus::const_iterator fi;
813  for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
814  PGItem *item = *fi;
815  if (!item->get_focus()) {
816  item->candidate(param, true);
817  }
818  }
819 }
820 
821 /**
822  * Sets whether the PGItem is active for mouse watching. This is not
823  * necessarily related to the active/inactive appearance of the item, which is
824  * controlled by set_state(), but it does affect whether it responds to mouse
825  * events.
826  */
827 void PGItem::
828 set_active(bool active) {
829  LightReMutexHolder holder(_lock);
830  if (active) {
831  _flags |= F_active;
832  } else {
833  _flags &= ~F_active;
834  // Deactivating the item automatically defocuses it too.
835  if (get_focus()) {
836  set_focus(false);
837  }
838  }
839 }
840 
841 /**
842  * Sets whether the PGItem currently has keyboard focus. This simply means
843  * that the item may respond to keyboard events as well as to mouse events;
844  * precisely what this means is up to the individual item.
845  *
846  * Only one PGItem in the world is allowed to have focus at any given time.
847  * Setting the focus on any other item automatically disables the focus from
848  * the previous item.
849  */
850 void PGItem::
851 set_focus(bool focus) {
852  LightReMutexHolder holder(_lock);
853  if (focus) {
854  if (!get_active()) {
855  // Cannot set focus on an inactive item.
856  return;
857  }
858 
859  // Set the keyboard focus to this item.
860  if (_focus_item != this) {
861  if (_focus_item != nullptr) {
862  // Clear the focus from whatever item currently has it.
863  _focus_item->set_focus(false);
864  }
865  _focus_item = this;
866  }
867  if (!get_focus()) {
868  focus_in();
869  _flags |= F_focus;
870  }
871 
872  } else {
873  if (_focus_item == this) {
874  // Remove this item from the focus.
875  _focus_item = nullptr;
876  }
877 
878  if (get_focus()) {
879  focus_out();
880  _flags &= ~F_focus;
881  }
882  }
883  _region->set_keyboard(focus);
884 }
885 
886 /**
887  * Sets the background_focus flag for this item. When background_focus is
888  * enabled, the item will receive keypress events even if it is not in focus;
889  * in fact, even if it is not onscreen. Unlike normal focus, many items may
890  * have background_focus simultaneously.
891  */
892 void PGItem::
893 set_background_focus(bool focus) {
894  LightReMutexHolder holder(_lock);
895  if (focus != get_background_focus()) {
896  if (focus) {
897  // Activate background focus.
898  _flags |= F_background_focus;
899  bool inserted = _background_focus.insert(this).second;
900  nassertv(inserted);
901 
902  } else {
903  // Deactivate background focus.
904  _flags &= ~F_background_focus;
905  size_t num_erased = _background_focus.erase(this);
906  nassertv(num_erased == 1);
907  }
908  }
909 }
910 
911 /**
912  * Returns one more than the highest-numbered state def that was ever assigned
913  * to the PGItem. The complete set of state defs assigned may then be
914  * retrieved by indexing from 0 to (get_num_state_defs() - 1).
915  *
916  * This is only an upper limit on the actual number of state defs, since there
917  * may be holes in the list.
918  */
919 int PGItem::
920 get_num_state_defs() const {
921  LightReMutexHolder holder(_lock);
922  return _state_defs.size();
923 }
924 
925 /**
926  * Returns true if get_state_def() has ever been called for the indicated
927  * state (thus defining a render subgraph for this state index), false
928  * otherwise.
929  */
930 bool PGItem::
931 has_state_def(int state) const {
932  LightReMutexHolder holder(_lock);
933  if (state < 0 || state >= (int)_state_defs.size()) {
934  return false;
935  }
936  return (!_state_defs[state]._root.is_empty());
937 }
938 
939 /**
940  * Resets the NodePath assigned to the indicated state to its initial default,
941  * with only a frame representation if appropriate.
942  */
943 void PGItem::
944 clear_state_def(int state) {
945  LightReMutexHolder holder(_lock);
946  if (state < 0 || state >= (int)_state_defs.size()) {
947  return;
948  }
949 
950  _state_defs[state]._root = NodePath();
951  _state_defs[state]._frame = NodePath();
952  _state_defs[state]._frame_stale = true;
953 
954  mark_internal_bounds_stale();
955 }
956 
957 /**
958  * Parents an instance of the bottom node of the indicated NodePath to the
959  * indicated state index.
960  */
962 instance_to_state_def(int state, const NodePath &path) {
963  LightReMutexHolder holder(_lock);
964  if (path.is_empty()) {
965  // If the source is empty, quietly do nothing.
966  return NodePath();
967  }
968 
969  mark_internal_bounds_stale();
970 
971  return path.instance_to(do_get_state_def(state));
972 }
973 
974 /**
975  * Returns the kind of frame that will be drawn behind the item when it is in
976  * the indicated state.
977  */
979 get_frame_style(int state) {
980  LightReMutexHolder holder(_lock);
981  if (state < 0 || state >= (int)_state_defs.size()) {
982  return PGFrameStyle();
983  }
984  return _state_defs[state]._frame_style;
985 }
986 
987 /**
988  * Changes the kind of frame that will be drawn behind the item when it is in
989  * the indicated state.
990  */
991 void PGItem::
992 set_frame_style(int state, const PGFrameStyle &style) {
993  LightReMutexHolder holder(_lock);
994  // Get the state def node, mainly to ensure that this state is slotted and
995  // listed as having been defined.
996  NodePath &root = do_get_state_def(state);
997  nassertv(!root.is_empty());
998 
999  _state_defs[state]._frame_style = style;
1000  _state_defs[state]._frame_stale = true;
1001 
1002  mark_internal_bounds_stale();
1003 }
1004 
1005 #ifdef HAVE_AUDIO
1006 /**
1007  * Sets the sound that will be played whenever the indicated event occurs.
1008  */
1009 void PGItem::
1010 set_sound(const string &event, AudioSound *sound) {
1011  LightReMutexHolder holder(_lock);
1012  _sounds[event] = sound;
1013 }
1014 
1015 /**
1016  * Removes the sound associated with the indicated event.
1017  */
1018 void PGItem::
1019 clear_sound(const string &event) {
1020  LightReMutexHolder holder(_lock);
1021  _sounds.erase(event);
1022 }
1023 
1024 /**
1025  * Returns the sound associated with the indicated event, or NULL if there is
1026  * no associated sound.
1027  */
1028 AudioSound *PGItem::
1029 get_sound(const string &event) const {
1030  LightReMutexHolder holder(_lock);
1031  Sounds::const_iterator si = _sounds.find(event);
1032  if (si != _sounds.end()) {
1033  return (*si).second;
1034  }
1035  return nullptr;
1036 }
1037 
1038 /**
1039  * Returns true if there is a sound associated with the indicated event, or
1040  * false otherwise.
1041  */
1042 bool PGItem::
1043 has_sound(const string &event) const {
1044  LightReMutexHolder holder(_lock);
1045  return (_sounds.count(event) != 0);
1046 }
1047 #endif // HAVE_AUDIO
1048 
1049 /**
1050  * Returns the TextNode object that will be used by all PGItems to generate
1051  * default labels given a string. This can be loaded with the default font,
1052  * etc.
1053  */
1056  if (_text_node == nullptr) {
1057  _text_node = new TextNode("pguiText");
1058  _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1059 
1060  // The default TextNode is aligned to the left, for the convenience of
1061  // PGEntry.
1062  _text_node->set_align(TextNode::A_left);
1063  }
1064  return _text_node;
1065 }
1066 
1067 /**
1068  * Plays the sound associated with the indicated event, if there is one.
1069  */
1070 void PGItem::
1071 play_sound(const string &event) {
1072 #ifdef HAVE_AUDIO
1073  LightReMutexHolder holder(_lock);
1074  Sounds::const_iterator si = _sounds.find(event);
1075  if (si != _sounds.end()) {
1076  AudioSound *sound = (*si).second;
1077  sound->play();
1078  }
1079 #endif // HAVE_AUDIO
1080 }
1081 
1082 /**
1083  * The frame parameter is an in/out parameter. This function adjusts frame so
1084  * that it represents the largest part of the rectangular region passed in,
1085  * that does not overlap with the rectangular region of the indicated
1086  * obscurer. If the obscurer is NULL, or is a hidden node, it is not
1087  * considered and the frame is left unchanged.
1088  *
1089  * This is used by slider bars and scroll frames, which have to automatically
1090  * figure out how much space they have to work with after allowing space for
1091  * scroll bars and buttons.
1092  */
1093 void PGItem::
1094 reduce_region(LVecBase4 &frame, PGItem *obscurer) const {
1095  using std::min;
1096  using std::max;
1097 
1098  if (obscurer != nullptr && !obscurer->is_overall_hidden()) {
1099  LVecBase4 oframe = get_relative_frame(obscurer);
1100 
1101  // Determine the four rectangular regions on the four sides of the
1102  // obscuring region.
1103  LVecBase4 right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
1104  LVecBase4 left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
1105  LVecBase4 above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
1106  LVecBase4 below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
1107 
1108  // Now choose the largest of those four.
1109  const LVecBase4 *largest = &right;
1110  PN_stdfloat largest_area = compute_area(*largest);
1111  compare_largest(largest, largest_area, &left);
1112  compare_largest(largest, largest_area, &above);
1113  compare_largest(largest, largest_area, &below);
1114 
1115  frame = *largest;
1116  }
1117 }
1118 
1119 /**
1120  * Returns the LVecBase4 frame of the indicated item, converted into this
1121  * item's coordinate space. Presumably, item is a child of this node.
1122  */
1123 LVecBase4 PGItem::
1124 get_relative_frame(PGItem *item) const {
1125  using std::min;
1126  using std::max;
1127 
1128  NodePath this_np = NodePath::any_path((PGItem *)this);
1129  NodePath item_np = this_np.find_path_to(item);
1130  if (item_np.is_empty()) {
1131  item_np = NodePath::any_path(item);
1132  }
1133  const LVecBase4 &orig_frame = item->get_frame();
1134  LMatrix4 transform = item_np.get_mat(this_np);
1135 
1136  // Transform the item's frame into the PGScrollFrame's coordinate space.
1137  // Transform all four vertices, and get the new bounding box. This way the
1138  // region works (mostly) even if has been rotated.
1139  LPoint3 ll(orig_frame[0], 0.0f, orig_frame[2]);
1140  LPoint3 lr(orig_frame[1], 0.0f, orig_frame[2]);
1141  LPoint3 ul(orig_frame[0], 0.0f, orig_frame[3]);
1142  LPoint3 ur(orig_frame[1], 0.0f, orig_frame[3]);
1143  ll = ll * transform;
1144  lr = lr * transform;
1145  ul = ul * transform;
1146  ur = ur * transform;
1147 
1148  return LVecBase4(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
1149  max(max(ll[0], lr[0]), max(ul[0], ur[0])),
1150  min(min(ll[2], lr[2]), min(ul[2], ur[2])),
1151  max(max(ll[2], lr[2]), max(ul[2], ur[2])));
1152 }
1153 
1154 /**
1155  * Converts from the 2-d mouse coordinates into the coordinate space of the
1156  * item.
1157  */
1158 LPoint3 PGItem::
1159 mouse_to_local(const LPoint2 &mouse_point) const {
1160  // This is ambiguous if the PGItem has multiple instances. Why would you do
1161  // that, anyway?
1162  NodePath this_np((PGItem *)this);
1163  CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
1164  return inv_transform->get_mat().xform_point(LVector3::rfu(mouse_point[0], 0, mouse_point[1]));
1165 }
1166 
1167 /**
1168  * Called when the user changes the frame size.
1169  */
1170 void PGItem::
1171 frame_changed() {
1172  LightReMutexHolder holder(_lock);
1173  mark_frames_stale();
1174  if (_notify != nullptr) {
1175  _notify->item_frame_changed(this);
1176  }
1177 }
1178 
1179 /**
1180  * Returns the Node that is the root of the subgraph that will be drawn when
1181  * the PGItem is in the indicated state. The first time this is called for a
1182  * particular state index, it may create the Node.
1183  *
1184  * Assumes the lock is already held.
1185  */
1186 NodePath &PGItem::
1187 do_get_state_def(int state) {
1188  slot_state_def(state);
1189 
1190  if (_state_defs[state]._root.is_empty()) {
1191  // Create a new node.
1192  _state_defs[state]._root = NodePath("state_" + format_string(state));
1193  _state_defs[state]._frame_stale = true;
1194  }
1195 
1196  if (_state_defs[state]._frame_stale) {
1197  update_frame(state);
1198  }
1199 
1200  return _state_defs[state]._root;
1201 }
1202 
1203 /**
1204  * Ensures there is a slot in the array for the given state definition.
1205  */
1206 void PGItem::
1207 slot_state_def(int state) {
1208  while (state >= (int)_state_defs.size()) {
1209  _state_defs.push_back(StateDef());
1210  }
1211 }
1212 
1213 /**
1214  * Generates a new instance of the frame geometry for the indicated state.
1215  */
1216 void PGItem::
1217 update_frame(int state) {
1218  // First, remove the old frame geometry, if any.
1219  if (state >= 0 && state < (int)_state_defs.size()) {
1220  _state_defs[state]._frame.remove_node();
1221  }
1222 
1223  // We must turn off the stale flag first, before we call get_state_def(), to
1224  // prevent get_state_def() from being a recursive call.
1225  _state_defs[state]._frame_stale = false;
1226 
1227  // Now create new frame geometry.
1228  if (has_frame()) {
1229  NodePath &root = do_get_state_def(state);
1230  _state_defs[state]._frame =
1231  _state_defs[state]._frame_style.generate_into(root, _frame);
1232  }
1233 }
1234 
1235 /**
1236  * Marks all the frames in all states stale, so that they will be regenerated
1237  * the next time each state is requested.
1238  */
1239 void PGItem::
1240 mark_frames_stale() {
1241  StateDefs::iterator di;
1242  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
1243  // Remove the old frame, if any.
1244  (*di)._frame.remove_node();
1245  (*di)._frame_stale = true;
1246  }
1247  mark_internal_bounds_stale();
1248 }
1249 
1250 /**
1251  * Clips the four corners of the item's frame by the indicated clipping plane,
1252  * and modifies the points to reflect the new set of clipped points.
1253  *
1254  * The return value is true if the set of points is unmodified (all points are
1255  * behind the clip plane), or false otherwise.
1256  */
1257 bool PGItem::
1258 clip_frame(ClipPoints &source_points, const LPlane &plane) const {
1259  if (source_points.empty()) {
1260  return true;
1261  }
1262 
1263  LPoint3 from3d;
1264  LVector3 delta3d;
1265  if (!plane.intersects_plane(from3d, delta3d, LPlane(LVector3(0, 1, 0), LPoint3::zero()))) {
1266  // The clipping plane is parallel to the polygon. The polygon is either
1267  // all in or all out.
1268  if (plane.dist_to_plane(LPoint3::zero()) < 0.0) {
1269  // A point within the polygon is behind the clipping plane: the polygon
1270  // is all in.
1271  return true;
1272  }
1273  return false;
1274  }
1275 
1276  // Project the line of intersection into the X-Z plane. Now we have a 2-d
1277  // clipping line.
1278  LPoint2 from2d(from3d[0], from3d[2]);
1279  LVector2 delta2d(delta3d[0], delta3d[2]);
1280 
1281  PN_stdfloat a = -delta2d[1];
1282  PN_stdfloat b = delta2d[0];
1283  PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
1284 
1285  // Now walk through the points. Any point on the left of our line gets
1286  // removed, and the line segment clipped at the point of intersection.
1287 
1288  // We might increase the number of vertices by as many as 1, if the plane
1289  // clips off exactly one corner. (We might also decrease the number of
1290  // vertices, or keep them the same number.)
1291  ClipPoints new_points;
1292  new_points.reserve(source_points.size() + 1);
1293 
1294  LPoint2 last_point(source_points.back());
1295  bool last_is_in = is_right(last_point - from2d, delta2d);
1296  bool all_in = last_is_in;
1297  ClipPoints::const_iterator pi;
1298  for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
1299  LPoint2 this_point(*pi);
1300  bool this_is_in = is_right(this_point - from2d, delta2d);
1301 
1302  // There appears to be a compiler bug in gcc 4.0: we need to extract this
1303  // comparison outside of the if statement.
1304  bool crossed_over = (this_is_in != last_is_in);
1305  if (crossed_over) {
1306  // We have just crossed over the clipping line. Find the point of
1307  // intersection.
1308  LVector2 d = this_point - last_point;
1309  PN_stdfloat denom = (a * d[0] + b * d[1]);
1310  if (denom != 0.0) {
1311  PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
1312  LPoint2 p = last_point + t * d;
1313 
1314  new_points.push_back(p);
1315  last_is_in = this_is_in;
1316  }
1317  }
1318 
1319  if (this_is_in) {
1320  // We are behind the clipping line. Keep the point.
1321  new_points.push_back(this_point);
1322  } else {
1323  all_in = false;
1324  }
1325 
1326  last_point = this_point;
1327  }
1328 
1329  source_points.swap(new_points);
1330  return all_in;
1331 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_background_focus() const
Returns whether background_focus is currently enabled.
Definition: pgItem.I:180
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This specialization on MouseWatcherParameter allows us to tag on additional elements to events for th...
An axis-aligned bounding box; that is, a minimum and maximum coordinate triple.
Definition: boundingBox.h:29
PGFrameStyle get_frame_style(int state)
Returns the kind of frame that will be drawn behind the item when it is in the indicated state.
Definition: pgItem.cxx:979
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class for all the various kinds of gui widget objects.
Definition: pgItem.h:53
An optional parameter associated with an event.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
static void background_release(const MouseWatcherParameter &param)
Calls release() on all the PGItems with background focus.
Definition: pgItem.cxx:783
bool get_active() const
Returns whether the PGItem is currently active for mouse events.
Definition: pgItem.I:160
std::string get_focus_in_event() const
Returns the event name that will be thrown when the item gets the keyboard focus.
Definition: pgItem.I:393
int get_bin_sort(int bin_index) const
Returns the sort order of the bin with the indicated bin_index (where bin_index was retrieved by get_...
bool has_state_def(int state) const
Returns true if get_state_def() has ever been called for the indicated state (thus defining a render ...
Definition: pgItem.cxx:931
is_overall_hidden
Returns true if the node has been hidden to all cameras by clearing its overall bit.
Definition: pandaNode.h:248
virtual void without_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse moves completely outside the boundaries o...
Definition: pgItem.cxx:593
This defines a bounding sphere, consisting of a center and a radius.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
virtual void enter_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse enters the region.
Definition: pgItem.cxx:521
virtual void focus_out()
This is a callback hook function, called whenever the widget loses the keyboard focus.
Definition: pgItem.cxx:636
This functions similarly to a LightAttrib.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath instance_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Adds the referenced node of the NodePath as a child of the referenced node of the indicated other Nod...
Definition: nodePath.cxx:471
std::string get_enter_event() const
Returns the event name that will be thrown when the item is active and the mouse enters its frame,...
Definition: pgItem.I:349
std::string get_press_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:414
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_mat
Returns the matrix that describes the transform.
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
Definition: pandaNode.cxx:2442
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
void set_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Changes the complete transform object on this node.
Definition: nodePath.I:565
static CullBinManager * get_global_ptr()
Returns the pointer to the global CullBinManager object.
static NodePath any_path(PandaNode *node, Thread *current_thread=Thread::get_current_thread())
Returns a new NodePath that represents any arbitrary path from the root to the indicated node.
Definition: nodePath.I:62
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
bool is_right(const LVector2 &v1, const LVector2 &v2)
Returns true if the 2-d v1 is to the right of v2.
Definition: pgItem.cxx:50
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
std::string get_exit_event() const
Returns the event name that will be thrown when the item is active and the mouse exits its frame,...
Definition: pgItem.I:359
bool has_notify() const
Returns true if there is an object configured to be notified when the PGItem changes,...
Definition: pgItem.I:59
std::string get_keystroke_event() const
Returns the event name that will be thrown when the item is active and any key is pressed by the user...
Definition: pgItem.I:446
bool is_keyrepeat() const
Returns true if the button-down even was generated due to keyrepeat, or false if it was an original b...
virtual void focus_in()
This is a callback hook function, called whenever the widget gets the keyboard focus.
Definition: pgItem.cxx:615
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
ButtonHandle get_button() const
Returns the mouse or keyboard button associated with this event.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void set_focus(bool focus)
Sets whether the PGItem currently has keyboard focus.
Definition: pgItem.cxx:851
std::string get_focus_out_event() const
Returns the event name that will be thrown when the item loses the keyboard focus.
Definition: pgItem.I:403
NodePath instance_to_state_def(int state, const NodePath &path)
Parents an instance of the bottom node of the indicated NodePath to the indicated state index.
Definition: pgItem.cxx:962
This is a specialization of CullTraverser for use within the pgui system.
get_num_on_planes
Returns the number of planes that are enabled by the attribute.
static void background_press(const MouseWatcherParameter &param)
Calls press() on all the PGItems with background focus.
Definition: pgItem.cxx:769
PGItemNotify * get_notify() const
Returns the object which will be notified when the PGItem changes, if any.
Definition: pgItem.I:69
virtual void release(const MouseWatcherParameter &param, bool background)
This is a callback hook function, called whenever a mouse or keyboard button previously depressed wit...
Definition: pgItem.cxx:686
get_bounds_type
Returns the bounding volume type set with set_bounds_type().
Definition: pandaNode.h:293
This is a specialization on MouseWatcherRegion, to add a bit more fields that are relevant to the PG ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
bool get_focus() const
Returns whether the PGItem currently has focus for keyboard events.
Definition: pgItem.I:170
virtual void within_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse moves within the boundaries of the region...
Definition: pgItem.cxx:571
Similar to MutexHolder, but for a light reentrant mutex.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
std::string get_repeat_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:425
get_frame
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.h:51
void apply_attribs(PandaNode *node, int attrib_types=~(TT_clip_plane|TT_cull_face|TT_apply_texture_color))
Walks the scene graph, accumulating attribs of the indicated types, applying them to the vertices,...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
virtual void move(const MouseWatcherParameter &param)
This is a callback hook function, called whenever a mouse is moved while within the region.
Definition: pgItem.cxx:753
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
get_on_plane
Returns the nth plane enabled by the attribute, sorted in render order.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A thread; that is, a lightweight process.
Definition: thread.h:46
void set_background_focus(bool focus)
Sets the background_focus flag for this item.
Definition: pgItem.cxx:893
The primary interface to this module.
Definition: textNode.h:48
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear_state_def(int state)
Resets the NodePath assigned to the indicated state to its initial default, with only a frame represe...
Definition: pgItem.cxx:944
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
bool has_frame() const
Returns true if the item has a bounding rectangle; see set_frame().
Definition: pgItem.I:116
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
NodePath find_path_to(PandaNode *node) const
Searches for the indicated node below this node and returns the shortest NodePath that connects them.
Definition: nodePath.cxx:332
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
void add_region(MouseWatcherRegion *region)
Adds the indicated region to the set of regions in the group.
Definition: pgTop.I:76
const DrawMask & get_camera_mask() const
Returns the visibility mask from the camera viewing the scene.
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
Definition: scissorAttrib.h:36
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const LVecBase4 & get_frame() const
Returns the bounding rectangle of the item.
Definition: pgItem.I:106
std::string get_release_event(const ButtonHandle &button) const
Returns the event name that will be thrown when the item is active and the indicated mouse or keyboar...
Definition: pgItem.I:436
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is sent along as a parameter to most events generated for a region to indicate the mouse and but...
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
This is a global object that maintains the collection of named CullBins in the world.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void exit_region(const MouseWatcherParameter &param)
This is a callback hook function, called whenever the mouse exits the region.
Definition: pgItem.cxx:545
std::string get_within_event() const
Returns the event name that will be thrown when the item is active and the mouse moves within the bou...
Definition: pgItem.I:371
std::string get_without_event() const
Returns the event name that will be thrown when the item is active and the mouse moves completely out...
Definition: pgItem.I:383
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
virtual void set_active(bool active)
Sets whether the PGItem is active for mouse watching.
Definition: pgItem.cxx:828
A node that contains a plane.
Definition: planeNode.h:36
static void background_candidate(const MouseWatcherParameter &param)
Calls candidate() on all the PGItems with background focus.
Definition: pgItem.cxx:811
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
static void background_keystroke(const MouseWatcherParameter &param)
Calls keystroke() on all the PGItems with background focus.
Definition: pgItem.cxx:797
BinType get_bin_type(int bin_index) const
Returns the type of the bin with the indicated bin_index (where bin_index was retrieved by get_bin() ...
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...