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