42 PGItem *PGItem::_focus_item =
nullptr;
50 is_right(
const LVector2 &v1,
const LVector2 &v2) {
51 return (v1[0] * v2[1] - v1[1] * v2[0]) > 0;
58 PGItem(
const string &name) :
76 if (_notify !=
nullptr) {
77 _notify->remove_item(
this);
81 nassertv(_region->_item ==
this);
82 _region->_item =
nullptr;
85 if (_focus_item ==
this) {
86 _focus_item =
nullptr;
94 PGItem(
const PGItem ©) :
97 _has_frame(copy._has_frame),
103 , _sounds(copy._sounds)
109 _region->set_name(copy._region->get_name());
112 size_t num_state_defs = copy._state_defs.size();
113 _state_defs.reserve(num_state_defs);
114 for (
size_t i = 0; i < num_state_defs; ++i) {
118 StateDef &old_sd = (StateDef &)(copy._state_defs[i]);
119 old_sd._frame.remove_node();
120 old_sd._frame_stale =
true;
123 new_sd._root = old_sd._root.copy_to(
NodePath());
124 new_sd._frame_style = old_sd._frame_style;
126 _state_defs.push_back(new_sd);
147 transform_changed() {
149 PandaNode::transform_changed();
150 if (_notify !=
nullptr) {
151 _notify->item_transform_changed(
this);
161 draw_mask_changed() {
163 PandaNode::draw_mask_changed();
164 if (_notify !=
nullptr) {
165 _notify->item_draw_mask_changed(
this);
195 has_frame = _has_frame && ((_flags & F_active) != 0);
199 if (state >= 0 && (
size_t)state < _state_defs.size()) {
200 StateDef &state_def = _state_defs[state];
201 if (!state_def._root.is_empty()) {
202 if (state_def._frame_stale) {
206 state_def_root = state_def._root.node();
217 if (trav->
is_exact_type(PGCullTraverser::get_class_type())) {
219 DCAST_INTO_R(pg_trav, trav,
true);
221 const LMatrix4 &transform = data.get_net_transform(trav)->get_mat();
226 int bin_index = data._state->get_bin_index();
230 CullBinManager::BinType bin_type = bin_manager->
get_bin_type(bin_index);
231 if (bin_type == CullBinManager::BT_fixed) {
234 sort = data._state->get_draw_order();
236 }
else if (bin_type == CullBinManager::BT_unsorted) {
239 sort = pg_trav->_sort_index;
240 pg_trav->_sort_index++;
249 int bin_sort = bin_manager->
get_bin_sort(data._state->get_bin_index());
256 sort = (bin_sort << 16) | ((sort + 0x8000) & 0xffff);
260 data._state->get_attrib(clip);
261 data._state->get_attrib(scissor);
262 if (activate_region(transform, sort, clip, scissor)) {
268 if (state_def_root !=
nullptr) {
286 is_renderable()
const {
297 int &internal_vertices,
299 Thread *current_thread)
const {
301 int num_vertices = 0;
307 if (btype == BoundingVolume::BT_default) {
311 if (btype == BoundingVolume::BT_sphere) {
324 for (
int i = 0; i < (int)_state_defs.size(); i++) {
328 child_volumes.push_back(node->get_bounds(current_thread));
329 num_vertices += node->get_nested_vertices(current_thread);
334 const BoundingVolume **child_end = child_begin + child_volumes.size();
336 bound->around(child_begin, child_end);
338 internal_bounds = bound;
339 internal_vertices = num_vertices;
350 StateDefs::iterator di;
351 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
355 CPT(
RenderState) child_state = node_state->compose(child->get_state());
368 xform(
const LMatrix4 &mat) {
371 LPoint3 ll(_frame[0], 0.0f, _frame[2]);
372 LPoint3 ur(_frame[1], 0.0f, _frame[3]);
375 _frame.set(ll[0], ur[0], ll[2], ur[2]);
378 StateDefs::iterator di;
379 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
389 if ((*di)._frame_style.xform(mat)) {
390 (*di)._frame_stale =
true;
393 mark_internal_bounds_stale();
404 activate_region(
const LMatrix4 &transform,
int sort,
413 LPoint3 ll = LPoint3::rfu(_frame[0], 0.0f, _frame[2]) * transform;
414 LPoint3 lr = LPoint3::rfu(_frame[1], 0.0f, _frame[2]) * transform;
415 LPoint3 ul = LPoint3::rfu(_frame[0], 0.0f, _frame[3]) * transform;
416 LPoint3 ur = LPoint3::rfu(_frame[1], 0.0f, _frame[3]) * transform;
417 LVector3 up = LVector3::up();
428 LVector3 right = LVector3::right();
447 points.push_back(LPoint2(ll[right_axis], ll[up_axis]));
448 points.push_back(LPoint2(lr[right_axis], lr[up_axis]));
449 points.push_back(LPoint2(ur[right_axis], ur[up_axis]));
450 points.push_back(LPoint2(ul[right_axis], ul[up_axis]));
453 for (
int i = 0; i < num_on_planes; ++i) {
455 LPlane plane = DCAST(
PlaneNode, plane_path.
node())->get_plane();
456 plane.xform(plane_path.get_net_transform()->
get_mat());
461 clip_frame(points, plane);
464 if (points.empty()) {
469 ClipPoints::iterator pi;
471 frame.set((*pi)[0], (*pi)[0], (*pi)[1], (*pi)[1]);
473 while (pi != points.end()) {
474 frame[0] = min(frame[0], (*pi)[0]);
475 frame[1] = max(frame[1], (*pi)[0]);
476 frame[2] = min(frame[2], (*pi)[1]);
477 frame[3] = max(frame[3], (*pi)[1]);
482 frame.set(min(min(ll[right_axis], lr[right_axis]), min(ul[right_axis], ur[right_axis])),
483 max(max(ll[right_axis], lr[right_axis]), max(ul[right_axis], ur[right_axis])),
484 min(min(ll[up_axis], lr[up_axis]), min(ul[up_axis], ur[up_axis])),
485 max(max(ll[up_axis], lr[up_axis]), max(ul[up_axis], ur[up_axis])));
492 frame.set(max(frame[0], sf[0] * 2.0f - 1.0f),
493 min(frame[1], sf[1] * 2.0f - 1.0f),
494 max(frame[2], sf[2] * 2.0f - 1.0f),
495 min(frame[3], sf[3] * 2.0f - 1.0f));
496 if (frame[1] <= frame[0] || frame[3] <= frame[2]) {
502 _region->set_frame(frame);
504 _region->set_sort(sort);
505 _region->set_active(
true);
509 _frame_inv_xform.invert_from(transform);
523 if (pgui_cat.is_debug()) {
525 << *
this <<
"::enter_region(" << param <<
")\n";
533 if (_notify !=
nullptr) {
534 _notify->item_enter(
this, param);
547 if (pgui_cat.is_debug()) {
549 << *
this <<
"::exit_region(" << param <<
")\n";
557 if (_notify !=
nullptr) {
558 _notify->item_exit(
this, param);
573 if (pgui_cat.is_debug()) {
575 << *
this <<
"::within_region(" << param <<
")\n";
583 if (_notify !=
nullptr) {
584 _notify->item_within(
this, param);
595 if (pgui_cat.is_debug()) {
597 << *
this <<
"::without_region(" << param <<
")\n";
605 if (_notify !=
nullptr) {
606 _notify->item_without(
this, param);
617 if (pgui_cat.is_debug()) {
619 << *
this <<
"::focus_in()\n";
626 if (_notify !=
nullptr) {
627 _notify->item_focus_in(
this);
638 if (pgui_cat.is_debug()) {
640 << *
this <<
"::focus_out()\n";
647 if (_notify !=
nullptr) {
648 _notify->item_focus_out(
this);
659 if (pgui_cat.is_debug()) {
661 << *
this <<
"::press(" << param <<
", " << background <<
")\n";
676 if (_notify !=
nullptr) {
677 _notify->item_press(
this, param);
688 if (pgui_cat.is_debug()) {
690 << *
this <<
"::release(" << param <<
", " << background <<
")\n";
700 if (_notify !=
nullptr) {
701 _notify->item_release(
this, param);
711 if (pgui_cat.is_debug()) {
713 << *
this <<
"::keystroke(" << param <<
", " << background <<
")\n";
735 if (pgui_cat.is_debug()) {
737 << *
this <<
"::candidate(" << param <<
", " << background <<
")\n";
755 if (pgui_cat.is_debug()) {
757 << *
this <<
"::move(" << param <<
")\n";
760 if (_notify !=
nullptr) {
761 _notify->item_move(
this, param);
770 BackgroundFocus::const_iterator fi;
771 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
774 item->
press(param,
true);
784 BackgroundFocus::const_iterator fi;
785 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
798 BackgroundFocus::const_iterator fi;
799 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
812 BackgroundFocus::const_iterator fi;
813 for (fi = _background_focus.begin(); fi != _background_focus.end(); ++fi) {
860 if (_focus_item !=
this) {
861 if (_focus_item !=
nullptr) {
873 if (_focus_item ==
this) {
875 _focus_item =
nullptr;
883 _region->set_keyboard(focus);
898 _flags |= F_background_focus;
899 bool inserted = _background_focus.insert(
this).second;
904 _flags &= ~F_background_focus;
905 size_t num_erased = _background_focus.erase(
this);
906 nassertv(num_erased == 1);
922 return _state_defs.size();
933 if (state < 0 || state >= (
int)_state_defs.size()) {
936 return (!_state_defs[state]._root.is_empty());
946 if (state < 0 || state >= (
int)_state_defs.size()) {
950 _state_defs[state]._root =
NodePath();
951 _state_defs[state]._frame =
NodePath();
952 _state_defs[state]._frame_stale =
true;
954 mark_internal_bounds_stale();
969 mark_internal_bounds_stale();
981 if (state < 0 || state >= (
int)_state_defs.size()) {
984 return _state_defs[state]._frame_style;
996 NodePath &root = do_get_state_def(state);
999 _state_defs[state]._frame_style = style;
1000 _state_defs[state]._frame_stale =
true;
1002 mark_internal_bounds_stale();
1010 set_sound(
const string &event,
AudioSound *sound) {
1012 _sounds[event] = sound;
1019 clear_sound(
const string &event) {
1021 _sounds.erase(event);
1029 get_sound(
const string &event)
const {
1031 Sounds::const_iterator si = _sounds.find(event);
1032 if (si != _sounds.end()) {
1033 return (*si).second;
1043 has_sound(
const string &event)
const {
1045 return (_sounds.count(event) != 0);
1047 #endif // HAVE_AUDIO
1056 if (_text_node ==
nullptr) {
1057 _text_node =
new TextNode(
"pguiText");
1058 _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
1062 _text_node->set_align(TextNode::A_left);
1071 play_sound(
const string &event) {
1074 Sounds::const_iterator si = _sounds.find(event);
1075 if (si != _sounds.end()) {
1079 #endif // HAVE_AUDIO
1094 reduce_region(LVecBase4 &frame,
PGItem *obscurer)
const {
1099 LVecBase4 oframe = get_relative_frame(obscurer);
1103 LVecBase4 right(max(frame[0], oframe[1]), frame[1], frame[2], frame[3]);
1104 LVecBase4 left(frame[0], min(frame[1], oframe[0]), frame[2], frame[3]);
1105 LVecBase4 above(frame[0], frame[1], max(frame[2], oframe[3]), frame[3]);
1106 LVecBase4 below(frame[0], frame[1], frame[2], min(frame[3], oframe[2]));
1109 const LVecBase4 *largest = &right;
1110 PN_stdfloat largest_area = compute_area(*largest);
1111 compare_largest(largest, largest_area, &left);
1112 compare_largest(largest, largest_area, &above);
1113 compare_largest(largest, largest_area, &below);
1124 get_relative_frame(
PGItem *item)
const {
1133 const LVecBase4 &orig_frame = item->
get_frame();
1134 LMatrix4 transform = item_np.
get_mat(this_np);
1139 LPoint3 ll(orig_frame[0], 0.0f, orig_frame[2]);
1140 LPoint3 lr(orig_frame[1], 0.0f, orig_frame[2]);
1141 LPoint3 ul(orig_frame[0], 0.0f, orig_frame[3]);
1142 LPoint3 ur(orig_frame[1], 0.0f, orig_frame[3]);
1143 ll = ll * transform;
1144 lr = lr * transform;
1145 ul = ul * transform;
1146 ur = ur * transform;
1148 return LVecBase4(min(min(ll[0], lr[0]), min(ul[0], ur[0])),
1149 max(max(ll[0], lr[0]), max(ul[0], ur[0])),
1150 min(min(ll[2], lr[2]), min(ul[2], ur[2])),
1151 max(max(ll[2], lr[2]), max(ul[2], ur[2])));
1159 mouse_to_local(
const LPoint2 &mouse_point)
const {
1164 return inv_transform->
get_mat().xform_point(LVector3::rfu(mouse_point[0], 0, mouse_point[1]));
1173 mark_frames_stale();
1174 if (_notify !=
nullptr) {
1175 _notify->item_frame_changed(
this);
1187 do_get_state_def(
int state) {
1188 slot_state_def(state);
1190 if (_state_defs[state]._root.is_empty()) {
1192 _state_defs[state]._root =
NodePath(
"state_" + format_string(state));
1193 _state_defs[state]._frame_stale =
true;
1196 if (_state_defs[state]._frame_stale) {
1197 update_frame(state);
1200 return _state_defs[state]._root;
1207 slot_state_def(
int state) {
1208 while (state >= (
int)_state_defs.size()) {
1209 _state_defs.push_back(StateDef());
1217 update_frame(
int state) {
1219 if (state >= 0 && state < (
int)_state_defs.size()) {
1220 _state_defs[state]._frame.remove_node();
1225 _state_defs[state]._frame_stale =
false;
1229 NodePath &root = do_get_state_def(state);
1230 _state_defs[state]._frame =
1231 _state_defs[state]._frame_style.generate_into(root, _frame);
1240 mark_frames_stale() {
1241 StateDefs::iterator di;
1242 for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
1244 (*di)._frame.remove_node();
1245 (*di)._frame_stale =
true;
1247 mark_internal_bounds_stale();
1258 clip_frame(ClipPoints &source_points,
const LPlane &plane)
const {
1259 if (source_points.empty()) {
1265 if (!plane.intersects_plane(from3d, delta3d, LPlane(LVector3(0, 1, 0), LPoint3::zero()))) {
1268 if (plane.dist_to_plane(LPoint3::zero()) < 0.0) {
1278 LPoint2 from2d(from3d[0], from3d[2]);
1279 LVector2 delta2d(delta3d[0], delta3d[2]);
1281 PN_stdfloat a = -delta2d[1];
1282 PN_stdfloat b = delta2d[0];
1283 PN_stdfloat c = from2d[0] * delta2d[1] - from2d[1] * delta2d[0];
1291 ClipPoints new_points;
1292 new_points.reserve(source_points.size() + 1);
1294 LPoint2 last_point(source_points.back());
1295 bool last_is_in =
is_right(last_point - from2d, delta2d);
1296 bool all_in = last_is_in;
1297 ClipPoints::const_iterator pi;
1298 for (pi = source_points.begin(); pi != source_points.end(); ++pi) {
1299 LPoint2 this_point(*pi);
1300 bool this_is_in =
is_right(this_point - from2d, delta2d);
1304 bool crossed_over = (this_is_in != last_is_in);
1308 LVector2 d = this_point - last_point;
1309 PN_stdfloat denom = (a * d[0] + b * d[1]);
1311 PN_stdfloat t = -(a * last_point[0] + b * last_point[1] + c) / denom;
1312 LPoint2 p = last_point + t * d;
1314 new_points.push_back(p);
1315 last_is_in = this_is_in;
1321 new_points.push_back(this_point);
1326 last_point = this_point;
1329 source_points.swap(new_points);