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 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 */
837set_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 */
860set_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 */
902set_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 */
928int PGItem::
929get_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 */
940has_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 */
953clear_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 */
977instance_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 */
994get_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 */
1007set_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 */
1030void PGItem::
1031set_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 */
1039void PGItem::
1040clear_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 */
1049AudioSound *PGItem::
1050get_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 */
1063bool PGItem::
1064has_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 */
1076get_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 */
1091void PGItem::
1092play_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 */
1114void PGItem::
1115reduce_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 */
1144LVecBase4 PGItem::
1145get_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 */
1179LPoint3 PGItem::
1180mouse_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 */
1191void PGItem::
1192frame_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 */
1206NodePath &PGItem::
1207do_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 */
1227void PGItem::
1228slot_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 */
1238void PGItem::
1239update_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 */
1261void PGItem::
1262mark_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 */
1290bool PGItem::
1291clip_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,...
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: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().
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.