Panda3D
Loading...
Searching...
No Matches
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
39using std::string;
40
41TypeHandle PGItem::_type_handle;
42PT(TextNode) PGItem::_text_node;
43PGItem *PGItem::_focus_item = nullptr;
44PGItem::BackgroundFocus PGItem::_background_focus;
45
46
47/**
48 * Returns true if the 2-d v1 is to the right of v2.
49 */
50INLINE bool
51is_right(const LVector2 &v1, const LVector2 &v2) {
52 return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
53}
54
55/**
56 *
57 */
58PGItem::
59PGItem(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 */
75PGItem::
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
86 if (_focus_item == this) {
87 _focus_item = nullptr;
88 }
89}
90
91/**
92 *
93 */
94PGItem::
95PGItem(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 */
143PandaNode *PGItem::
144make_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 */
154void PGItem::
155transform_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 */
168void PGItem::
169draw_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 */
195bool PGItem::
196cull_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 */
295bool PGItem::
296is_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 */
305void PGItem::
306compute_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.
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 */
356void PGItem::
357r_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 */
376void PGItem::
377xform(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 */
420bool PGItem::
421activate_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 */
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 */
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 */
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 */
632focus_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 */
653focus_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 */
674press(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 */
703release(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 */
726keystroke(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 */
750candidate(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 */
770move(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 // We have to be careful, because objects may remove themselves from the set
788 // while we're iterating over it.
789 auto it = _background_focus.begin();
790 while (it != _background_focus.end()) {
791 PGItem *item = *it++;
792 if (!item->get_focus()) {
793 PT(PGItem) item_ref(item);
794 item_ref->press(param, true);
795 }
796 }
797}
798
799/**
800 * Calls release() on all the PGItems with background focus.
801 */
804 auto it = _background_focus.begin();
805 while (it != _background_focus.end()) {
806 PGItem *item = *it++;
807 if (!item->get_focus()) {
808 PT(PGItem) item_ref(item);
809 item_ref->release(param, true);
810 }
811 }
812}
813
814/**
815 * Calls keystroke() on all the PGItems with background focus.
816 */
819 auto it = _background_focus.begin();
820 while (it != _background_focus.end()) {
821 PGItem *item = *it++;
822 if (!item->get_focus()) {
823 PT(PGItem) item_ref(item);
824 item_ref->keystroke(param, true);
825 }
826 }
827}
828
829/**
830 * Calls candidate() on all the PGItems with background focus.
831 */
834 auto it = _background_focus.begin();
835 while (it != _background_focus.end()) {
836 PGItem *item = *it++;
837 if (!item->get_focus()) {
838 PT(PGItem) item_ref(item);
839 item_ref->candidate(param, true);
840 }
841 }
842}
843
844/**
845 * Sets whether the PGItem is active for mouse watching. This is not
846 * necessarily related to the active/inactive appearance of the item, which is
847 * controlled by set_state(), but it does affect whether it responds to mouse
848 * events.
849 */
851set_active(bool active) {
852 LightReMutexHolder holder(_lock);
853 if (active) {
854 _flags |= F_active;
855 } else {
856 _flags &= ~F_active;
857 // Deactivating the item automatically defocuses it too.
858 if (get_focus()) {
859 set_focus(false);
860 }
861 }
862}
863
864/**
865 * Sets whether the PGItem currently has keyboard focus. This simply means
866 * that the item may respond to keyboard events as well as to mouse events;
867 * precisely what this means is up to the individual item.
868 *
869 * Only one PGItem in the world is allowed to have focus at any given time.
870 * Setting the focus on any other item automatically disables the focus from
871 * the previous item.
872 */
874set_focus(bool focus) {
875 LightReMutexHolder holder(_lock);
876 if (focus) {
877 if (!get_active()) {
878 // Cannot set focus on an inactive item.
879 return;
880 }
881
882 // Set the keyboard focus to this item.
883 if (_focus_item != this) {
884 if (_focus_item != nullptr) {
885 // Clear the focus from whatever item currently has it.
886 _focus_item->set_focus(false);
887 }
888 _focus_item = this;
889 }
890 if (!get_focus()) {
891 focus_in();
892 _flags |= F_focus;
893 }
894
895 } else {
896 if (_focus_item == this) {
897 // Remove this item from the focus.
898 _focus_item = nullptr;
899 }
900
901 if (get_focus()) {
902 focus_out();
903 _flags &= ~F_focus;
904 }
905 }
906 _region->set_keyboard(focus);
907}
908
909/**
910 * Sets the background_focus flag for this item. When background_focus is
911 * enabled, the item will receive keypress events even if it is not in focus;
912 * in fact, even if it is not onscreen. Unlike normal focus, many items may
913 * have background_focus simultaneously.
914 */
916set_background_focus(bool focus) {
917 LightReMutexHolder holder(_lock);
918 if (focus != get_background_focus()) {
919 if (focus) {
920 // Activate background focus.
921 _flags |= F_background_focus;
922 bool inserted = _background_focus.insert(this).second;
923 nassertv(inserted);
924
925 } else {
926 // Deactivate background focus.
927 _flags &= ~F_background_focus;
928 size_t num_erased = _background_focus.erase(this);
929 nassertv(num_erased == 1);
930 }
931 }
932}
933
934/**
935 * Returns one more than the highest-numbered state def that was ever assigned
936 * to the PGItem. The complete set of state defs assigned may then be
937 * retrieved by indexing from 0 to (get_num_state_defs() - 1).
938 *
939 * This is only an upper limit on the actual number of state defs, since there
940 * may be holes in the list.
941 */
942int PGItem::
943get_num_state_defs() const {
944 LightReMutexHolder holder(_lock);
945 return _state_defs.size();
946}
947
948/**
949 * Returns true if get_state_def() has ever been called for the indicated
950 * state (thus defining a render subgraph for this state index), false
951 * otherwise.
952 */
954has_state_def(int state) const {
955 LightReMutexHolder holder(_lock);
956 if (state < 0 || state >= (int)_state_defs.size()) {
957 return false;
958 }
959 return (!_state_defs[state]._root.is_empty());
960}
961
962/**
963 * Resets the NodePath assigned to the indicated state to its initial default,
964 * with only a frame representation if appropriate.
965 */
967clear_state_def(int state) {
968 LightReMutexHolder holder(_lock);
969 if (state < 0 || state >= (int)_state_defs.size()) {
970 return;
971 }
972
973 _state_defs[state]._root = NodePath();
974 _state_defs[state]._frame = NodePath();
975 _state_defs[state]._frame_stale = true;
976
977 mark_internal_bounds_stale();
978
979#ifdef THREADED_PIPELINE
980 if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
981 update_frame(state);
982 }
983#endif
984}
985
986/**
987 * Parents an instance of the bottom node of the indicated NodePath to the
988 * indicated state index.
989 */
991instance_to_state_def(int state, const NodePath &path) {
992 LightReMutexHolder holder(_lock);
993 if (path.is_empty()) {
994 // If the source is empty, quietly do nothing.
995 return NodePath();
996 }
997
998 mark_internal_bounds_stale();
999
1000 return path.instance_to(do_get_state_def(state));
1001}
1002
1003/**
1004 * Returns the kind of frame that will be drawn behind the item when it is in
1005 * the indicated state.
1006 */
1008get_frame_style(int state) {
1009 LightReMutexHolder holder(_lock);
1010 if (state < 0 || state >= (int)_state_defs.size()) {
1011 return PGFrameStyle();
1012 }
1013 return _state_defs[state]._frame_style;
1014}
1015
1016/**
1017 * Changes the kind of frame that will be drawn behind the item when it is in
1018 * the indicated state.
1019 */
1021set_frame_style(int state, const PGFrameStyle &style) {
1022 LightReMutexHolder holder(_lock);
1023 // Get the state def node, mainly to ensure that this state is slotted and
1024 // listed as having been defined.
1025 NodePath &root = do_get_state_def(state);
1026 nassertv(!root.is_empty());
1027
1028 _state_defs[state]._frame_style = style;
1029 _state_defs[state]._frame_stale = true;
1030
1031 mark_internal_bounds_stale();
1032
1033#ifdef THREADED_PIPELINE
1034 if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
1035 update_frame(state);
1036 }
1037#endif
1038}
1039
1040#ifdef HAVE_AUDIO
1041/**
1042 * Sets the sound that will be played whenever the indicated event occurs.
1043 */
1044void PGItem::
1045set_sound(const string &event, AudioSound *sound) {
1046 LightReMutexHolder holder(_lock);
1047 _sounds[event] = sound;
1048}
1049
1050/**
1051 * Removes the sound associated with the indicated event.
1052 */
1053void PGItem::
1054clear_sound(const string &event) {
1055 LightReMutexHolder holder(_lock);
1056 _sounds.erase(event);
1057}
1058
1059/**
1060 * Returns the sound associated with the indicated event, or NULL if there is
1061 * no associated sound.
1062 */
1063AudioSound *PGItem::
1064get_sound(const string &event) const {
1065 LightReMutexHolder holder(_lock);
1066 Sounds::const_iterator si = _sounds.find(event);
1067 if (si != _sounds.end()) {
1068 return (*si).second;
1069 }
1070 return nullptr;
1071}
1072
1073/**
1074 * Returns true if there is a sound associated with the indicated event, or
1075 * false otherwise.
1076 */
1077bool PGItem::
1078has_sound(const string &event) const {
1079 LightReMutexHolder holder(_lock);
1080 return (_sounds.count(event) != 0);
1081}
1082#endif // HAVE_AUDIO
1083
1084/**
1085 * Returns the TextNode object that will be used by all PGItems to generate
1086 * default labels given a string. This can be loaded with the default font,
1087 * etc.
1088 */
1090get_text_node() {
1091 if (_text_node == nullptr) {
1092 _text_node = new TextNode("pguiText");
1093 _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1094
1095 // The default TextNode is aligned to the left, for the convenience of
1096 // PGEntry.
1097 _text_node->set_align(TextNode::A_left);
1098 }
1099 return _text_node;
1100}
1101
1102/**
1103 * Plays the sound associated with the indicated event, if there is one.
1104 */
1105void PGItem::
1106play_sound(const string &event) {
1107#ifdef HAVE_AUDIO
1108 LightReMutexHolder holder(_lock);
1109 Sounds::const_iterator si = _sounds.find(event);
1110 if (si != _sounds.end()) {
1111 AudioSound *sound = (*si).second;
1112 sound->play();
1113 }
1114#endif // HAVE_AUDIO
1115}
1116
1117/**
1118 * The frame parameter is an in/out parameter. This function adjusts frame so
1119 * that it represents the largest part of the rectangular region passed in,
1120 * that does not overlap with the rectangular region of the indicated
1121 * obscurer. If the obscurer is NULL, or is a hidden node, it is not
1122 * considered and the frame is left unchanged.
1123 *
1124 * This is used by slider bars and scroll frames, which have to automatically
1125 * figure out how much space they have to work with after allowing space for
1126 * scroll bars and buttons.
1127 */
1128void PGItem::
1129reduce_region(LVecBase4 &frame, PGItem *obscurer) const {
1130 using std::min;
1131 using std::max;
1132
1133 if (obscurer != nullptr && !obscurer->is_overall_hidden()) {
1134 LVecBase4 oframe = get_relative_frame(obscurer);
1135
1136 // Determine the four rectangular regions on the four sides of the
1137 // obscuring region.
1138 LVecBase4 right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
1139 LVecBase4 left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
1140 LVecBase4 above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
1141 LVecBase4 below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
1142
1143 // Now choose the largest of those four.
1144 const LVecBase4 *largest = &right;
1145 PN_stdfloat largest_area = compute_area(*largest);
1146 compare_largest(largest, largest_area, &left);
1147 compare_largest(largest, largest_area, &above);
1148 compare_largest(largest, largest_area, &below);
1149
1150 frame = *largest;
1151 }
1152}
1153
1154/**
1155 * Returns the LVecBase4 frame of the indicated item, converted into this
1156 * item's coordinate space. Presumably, item is a child of this node.
1157 */
1158LVecBase4 PGItem::
1159get_relative_frame(PGItem *item) const {
1160 using std::min;
1161 using std::max;
1162
1163 NodePath this_np = NodePath::any_path((PGItem *)this);
1164 NodePath item_np = this_np.find_path_to(item);
1165 if (item_np.is_empty()) {
1166 item_np = NodePath::any_path(item);
1167 }
1168 const LVecBase4 &orig_frame = item->get_frame();
1169 LMatrix4 transform = item_np.get_mat(this_np);
1170
1171 // Transform the item's frame into the PGScrollFrame's coordinate space.
1172 // Transform all four vertices, and get the new bounding box. This way the
1173 // region works (mostly) even if has been rotated.
1174 LPoint3 ll(orig_frame[0], 0.0f, orig_frame[2]);
1175 LPoint3 lr(orig_frame[1], 0.0f, orig_frame[2]);
1176 LPoint3 ul(orig_frame[0], 0.0f, orig_frame[3]);
1177 LPoint3 ur(orig_frame[1], 0.0f, orig_frame[3]);
1178 ll = ll * transform;
1179 lr = lr * transform;
1180 ul = ul * transform;
1181 ur = ur * transform;
1182
1183 return LVecBase4(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
1184 max(max(ll[0], lr[0]), max(ul[0], ur[0])),
1185 min(min(ll[2], lr[2]), min(ul[2], ur[2])),
1186 max(max(ll[2], lr[2]), max(ul[2], ur[2])));
1187}
1188
1189/**
1190 * Converts from the 2-d mouse coordinates into the coordinate space of the
1191 * item.
1192 */
1193LPoint3 PGItem::
1194mouse_to_local(const LPoint2 &mouse_point) const {
1195 // This is ambiguous if the PGItem has multiple instances. Why would you do
1196 // that, anyway?
1197 NodePath this_np((PGItem *)this);
1198 CPT(TransformState) inv_transform = NodePath().get_transform(this_np);
1199 return inv_transform->get_mat().xform_point(LVector3::rfu(mouse_point[0], 0, mouse_point[1]));
1200}
1201
1202/**
1203 * Called when the user changes the frame size. Assumes the lock is held.
1204 */
1205void PGItem::
1206frame_changed() {
1207 mark_frames_stale();
1208 if (_notify != nullptr) {
1209 _notify->item_frame_changed(this);
1210 }
1211}
1212
1213/**
1214 * Returns the Node that is the root of the subgraph that will be drawn when
1215 * the PGItem is in the indicated state. The first time this is called for a
1216 * particular state index, it may create the Node.
1217 *
1218 * Assumes the lock is already held.
1219 */
1220NodePath &PGItem::
1221do_get_state_def(int state) {
1222 slot_state_def(state);
1223
1224 if (_state_defs[state]._root.is_empty()) {
1225 // Create a new node.
1226 _state_defs[state]._root = NodePath("state_" + format_string(state));
1227 _state_defs[state]._frame_stale = true;
1228 }
1229
1230 if (_state_defs[state]._frame_stale) {
1231 update_frame(state);
1232 }
1233
1234 return _state_defs[state]._root;
1235}
1236
1237/**
1238 * Ensures there is a slot in the array for the given state definition.
1239 * Assumes the lock is already held.
1240 */
1241void PGItem::
1242slot_state_def(int state) {
1243 while (state >= (int)_state_defs.size()) {
1244 _state_defs.push_back(StateDef());
1245 }
1246}
1247
1248/**
1249 * Generates a new instance of the frame geometry for the indicated state.
1250 * Assumes the lock is already held.
1251 */
1252void PGItem::
1253update_frame(int state) {
1254 // First, remove the old frame geometry, if any.
1255 if (state >= 0 && state < (int)_state_defs.size()) {
1256 _state_defs[state]._frame.remove_node();
1257 }
1258
1259 // We must turn off the stale flag first, before we call get_state_def(), to
1260 // prevent get_state_def() from being a recursive call.
1261 _state_defs[state]._frame_stale = false;
1262
1263 // Now create new frame geometry.
1264 if (has_frame()) {
1265 NodePath &root = do_get_state_def(state);
1266 _state_defs[state]._frame =
1267 _state_defs[state]._frame_style.generate_into(root, _frame);
1268 }
1269}
1270
1271/**
1272 * Marks all the frames in all states stale, so that they will be regenerated
1273 * the next time each state is requested. Assumes the lock is already held.
1274 */
1275void PGItem::
1276mark_frames_stale() {
1277#ifdef THREADED_PIPELINE
1278 // If we are using the threaded pipeline, we must update the frame geometry
1279 // immediately on the App thread, since this class isn't pipeline-cycled.
1280 if (Pipeline::get_render_pipeline()->get_num_stages() > 1) {
1281 for (int state = 0; state < (int)_state_defs.size(); ++state) {
1282 update_frame(state);
1283 }
1284 }
1285 else
1286#endif
1287 {
1288 for (StateDef &def : _state_defs) {
1289 // Remove the old frame, if any.
1290 def._frame.remove_node();
1291 def._frame_stale = true;
1292 }
1293 }
1294 mark_internal_bounds_stale();
1295}
1296
1297/**
1298 * Clips the four corners of the item's frame by the indicated clipping plane,
1299 * and modifies the points to reflect the new set of clipped points.
1300 *
1301 * The return value is true if the set of points is unmodified (all points are
1302 * behind the clip plane), or false otherwise.
1303 */
1304bool PGItem::
1305clip_frame(ClipPoints &source_points, const LPlane &plane) const {
1306 if (source_points.empty()) {
1307 return true;
1308 }
1309
1310 LPoint3 from3d;
1311 LVector3 delta3d;
1312 if (!plane.intersects_plane(from3d, delta3d, LPlane(LVector3(0, 1, 0), LPoint3::zero()))) {
1313 // The clipping plane is parallel to the polygon. The polygon is either
1314 // all in or all out.
1315 if (plane.dist_to_plane(LPoint3::zero()) < 0.0) {
1316 // A point within the polygon is behind the clipping plane: the polygon
1317 // is all in.
1318 return true;
1319 }
1320 return false;
1321 }
1322
1323 // Project the line of intersection into the X-Z plane. Now we have a 2-d
1324 // clipping line.
1325 LPoint2 from2d(from3d[0], from3d[2]);
1326 LVector2 delta2d(delta3d[0], delta3d[2]);
1327
1328 PN_stdfloat a = -delta2d[1];
1329 PN_stdfloat b = delta2d[0];
1330 PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
1331
1332 // Now walk through the points. Any point on the left of our line gets
1333 // removed, and the line segment clipped at the point of intersection.
1334
1335 // We might increase the number of vertices by as many as 1, if the plane
1336 // clips off exactly one corner. (We might also decrease the number of
1337 // vertices, or keep them the same number.)
1338 ClipPoints new_points;
1339 new_points.reserve(source_points.size() + 1);
1340
1341 LPoint2 last_point(source_points.back());
1342 bool last_is_in = is_right(last_point - from2d, delta2d);
1343 bool all_in = last_is_in;
1344
1345 for (LPoint2 this_point : source_points) {
1346 bool this_is_in = is_right(this_point - from2d, delta2d);
1347
1348 // There appears to be a compiler bug in gcc 4.0: we need to extract this
1349 // comparison outside of the if statement.
1350 bool crossed_over = (this_is_in != last_is_in);
1351 if (crossed_over) {
1352 // We have just crossed over the clipping line. Find the point of
1353 // intersection.
1354 LVector2 d = this_point - last_point;
1355 PN_stdfloat denom = (a * d[0] + b * d[1]);
1356 if (denom != 0.0) {
1357 PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
1358 LPoint2 p = last_point + t * d;
1359
1360 new_points.push_back(p);
1361 last_is_in = this_is_in;
1362 }
1363 }
1364
1365 if (this_is_in) {
1366 // We are behind the clipping line. Keep the point.
1367 new_points.push_back(this_point);
1368 } else {
1369 all_in = false;
1370 }
1371
1372 last_point = this_point;
1373 }
1374
1375 source_points.swap(new_points);
1376 return all_in;
1377}
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,...
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:333
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:794
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:474
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:803
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:1021
static void background_candidate(const MouseWatcherParameter &param)
Calls candidate() on all the PGItems with background focus.
Definition pgItem.cxx:833
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:967
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:1090
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:954
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:1008
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:991
void set_background_focus(bool focus)
Sets the background_focus flag for this item.
Definition pgItem.cxx:916
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:874
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:818
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:851
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().
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...
get_frame
Returns the left, right, bottom, top coordinates of the scissor frame.
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.