46 MouseWatcher(
const string &name) :
49 _pixel_xy_input = define_input(
"pixel_xy", EventStoreVec2::get_class_type());
50 _pixel_size_input = define_input(
"pixel_size", EventStoreVec2::get_class_type());
51 _xy_input = define_input(
"xy", EventStoreVec2::get_class_type());
52 _button_events_input = define_input(
"button_events", ButtonEventList::get_class_type());
53 _pointer_events_input = define_input(
"pointer_events", PointerEventList::get_class_type());
55 _pixel_xy_output = define_output(
"pixel_xy", EventStoreVec2::get_class_type());
56 _pixel_size_output = define_output(
"pixel_size", EventStoreVec2::get_class_type());
57 _xy_output = define_output(
"xy", EventStoreVec2::get_class_type());
58 _button_events_output = define_output(
"button_events", ButtonEventList::get_class_type());
66 _internal_suppress = 0;
67 _preferred_region =
nullptr;
68 _preferred_button_down_region =
nullptr;
71 _display_region =
nullptr;
72 _button_down_display_region =
nullptr;
74 _frame.set(-1.0f, 1.0f, -1.0f, 1.0f);
76 _inactivity_timeout = inactivity_timeout;
77 _has_inactivity_timeout = !IS_NEARLY_ZERO(_inactivity_timeout);
79 _num_trail_recent = 0;
80 _trail_log_duration = 0.0;
83 _inactivity_timeout_event =
"inactivity_timeout";
85 _inactivity_state = IS_active;
90 _enter_multiple =
false;
94 _implicit_click =
false;
112 remove_region_from(_current_regions, region);
113 if (region == _preferred_region) {
114 if (_preferred_region !=
nullptr) {
117 _preferred_region =
nullptr;
119 if (region == _preferred_button_down_region) {
120 _preferred_button_down_region =
nullptr;
123 return MouseWatcherBase::do_remove_region(region);
136 get_over_regions(regions, pos);
137 return get_preferred_region(regions);
158 Groups::const_iterator gi =
159 find(_groups.begin(), _groups.end(), pt);
160 if (gi != _groups.end()) {
166 if (!_show_regions_render2d.
is_empty()) {
167 group->
show_regions(_show_regions_render2d, _show_regions_bin_name,
168 _show_regions_draw_order);
173 _groups.push_back(pt);
187 group->do_sort_regions();
190 intersect_regions(only_a, only_b, both,
191 _current_regions, group->_regions);
192 set_current_regions(only_a);
194 if (has_region_in(both, _preferred_region)) {
195 if (_preferred_region !=
nullptr) {
198 _preferred_region =
nullptr;
200 if (has_region_in(both, _preferred_button_down_region)) {
201 _preferred_button_down_region =
nullptr;
205 if (!_show_regions_render2d.
is_empty()) {
206 group->do_hide_regions();
212 Groups::iterator gi =
213 find(_groups.begin(), _groups.end(), pt);
214 if (gi != _groups.end()) {
235 if (old_group == new_group) {
245 old_group->do_sort_regions();
246 new_group->do_sort_regions();
249 if (!_show_regions_render2d.
is_empty()) {
250 old_group->do_hide_regions();
251 new_group->do_show_regions(_show_regions_render2d, _show_regions_bin_name,
252 _show_regions_draw_order);
258 intersect_regions(remove, add, keep,
259 old_group->_regions, new_group->_regions);
262 bool any_new_current_regions =
false;
265 if (!remove.
empty()) {
267 intersect_regions(only_a, only_b, both,
268 _current_regions, remove);
269 new_current_regions.
swap(only_a);
270 any_new_current_regions =
true;
272 if (has_region_in(both, _preferred_region)) {
273 if (_preferred_region !=
nullptr) {
276 _preferred_region =
nullptr;
278 if (has_region_in(both, _preferred_button_down_region)) {
279 _preferred_button_down_region =
nullptr;
301 if (any_new_current_regions) {
302 set_current_regions(new_current_regions);
307 Groups::iterator gi =
308 find(_groups.begin(), _groups.end(), pt);
309 if (gi == _groups.end()) {
310 _groups.push_back(new_group);
314 if (!_show_regions_render2d.
is_empty()) {
315 new_group->do_update_regions();
321 gi = find(_groups.begin(), _groups.end(), pt);
322 if (gi != _groups.end()) {
339 return _groups.size();
348 nassertr(n >= 0 && n < (
int)_groups.size(),
nullptr);
359 if (duration < 0.0) {
362 _trail_log_duration = duration;
363 discard_excess_trail_log();
374 discard_excess_trail_log() {
375 if (_trail_log_duration == 0.0) {
378 if (_trail_log->get_num_events() > 2) {
380 while ((_trail_log->get_num_events() > 2)&&
381 (_trail_log->get_time(0) <= old)&&
382 (_trail_log->get_time(1) <= old)) {
383 _trail_log->pop_front();
402 if (_trail_node ==
nullptr) {
403 _trail_node =
new GeomNode(
"Mouse Trail Node");
417 _trail_node =
nullptr;
424 update_trail_node() {
425 if (_trail_node ==
nullptr) {
428 _trail_node->remove_all_geoms();
430 if (_trail_log->get_num_events() < 2) {
441 double xscale = 2.0 / _pixel_size->get_value().get_x();
442 double yscale = 2.0 / _pixel_size->get_value().get_y();
444 for (
int i=0; i<(int)_trail_log->get_num_events(); i++) {
445 double x = (_trail_log->get_xpos(i) * xscale) - 1.0;
446 double y = (_trail_log->get_ypos(i) * yscale) - 1.0;
447 vertex.add_data3(LVecBase3(x,0.0,-y));
448 lines->add_vertex(i);
450 lines->close_primitive();
453 l_geom->add_primitive(lines);
454 _trail_node->add_geom(l_geom);
470 switch (_inactivity_state) {
475 _inactivity_state = IS_inactive_to_active;
478 case IS_active_to_inactive:
479 _inactivity_state = IS_active;
482 case IS_inactive_to_active:
492 output(std::ostream &out)
const {
494 DataNode::output(out);
500 size_t count = _regions.size();
502 count += group->get_num_regions();
505 out <<
" (" << count <<
" regions)";
512 write(std::ostream &out,
int indent_level)
const {
514 <<
"MouseWatcher " << get_name() <<
":\n";
515 MouseWatcherBase::write(out, indent_level + 2);
519 indent(out, indent_level + 2)
521 group->write(out, indent_level + 4);
531 nassertv(_lock.debug_is_locked());
534 PN_stdfloat mx = (pos[0] + 1.0f) * 0.5f * (_frame[1] - _frame[0]) + _frame[0];
535 PN_stdfloat my = (pos[1] + 1.0f) * 0.5f * (_frame[3] - _frame[2]) + _frame[2];
549 const LVecBase4 &frame = region->get_frame();
551 if (region->get_active() &&
552 mx >= frame[0] && mx <= frame[1] &&
553 my >= frame[2] && my <= frame[3]) {
561 group->sort_regions();
564 const LVecBase4 &frame = region->get_frame();
567 mx >= frame[0] && mx <= frame[1] &&
568 my >= frame[2] && my <= frame[3]) {
578 sort(regions.
begin(), regions.
end());
588 if (regions.
empty()) {
592 Regions::const_iterator ri;
593 ri = regions.
begin();
596 while (ri != regions.
end()) {
599 if (*region < *preferred) {
617 nassertv(_lock.debug_is_locked());
625 Regions::const_iterator new_ri = regions.
begin();
626 Regions::const_iterator old_ri = _current_regions.
begin();
630 std::vector<MouseWatcherRegion *> new_regions;
632 bool any_changes =
false;
633 while (new_ri != regions.
end() && old_ri != _current_regions.
end()) {
634 if ((*new_ri) < (*old_ri)) {
637 new_regions.push_back(new_region);
641 }
else if ((*old_ri) < (*new_ri)) {
644 without_region(old_region, param);
655 while (new_ri != regions.
end()) {
658 new_regions.push_back(new_region);
663 while (old_ri != _current_regions.
end()) {
666 without_region(old_region, param);
674 _current_regions.
swap(regions);
677 std::vector<MouseWatcherRegion *>::const_iterator ri;
678 for (ri = new_regions.begin(); ri != new_regions.end(); ++ri) {
680 within_region(new_region, param);
684 if (!_enter_multiple) {
689 get_preferred_region(_current_regions);
691 if (_button_down && new_preferred_region != _preferred_button_down_region) {
694 new_preferred_region =
nullptr;
697 if (new_preferred_region != _preferred_region) {
698 if (_preferred_region !=
nullptr) {
699 exit_region(_preferred_region, param);
701 _preferred_region = new_preferred_region;
702 if (_preferred_region !=
nullptr) {
703 enter_region(_preferred_region, param);
713 clear_current_regions() {
714 nassertv(_lock.debug_is_locked());
716 if (!_current_regions.
empty()) {
722 Regions::const_iterator old_ri = _current_regions.
begin();
724 while (old_ri != _current_regions.
end()) {
728 throw_event_pattern(_leave_pattern, old_region, ButtonHandle::none());
729 if (_preferred_region == old_region) {
730 _preferred_region =
nullptr;
735 _current_regions.
clear();
737 if (_preferred_region !=
nullptr) {
738 _preferred_region->exit_region(param);
739 throw_event_pattern(_leave_pattern, _preferred_region, ButtonHandle::none());
740 _preferred_region =
nullptr;
750 do_show_regions(
const NodePath &render2d,
const string &bin_name,
753 MouseWatcherBase::do_show_regions(render2d, bin_name, draw_order);
754 _show_regions_render2d = render2d;
755 _show_regions_bin_name = bin_name;
756 _show_regions_draw_order = draw_order;
759 group->show_regions(render2d, bin_name, draw_order);
771 MouseWatcherBase::do_hide_regions();
772 _show_regions_render2d =
NodePath();
773 _show_regions_bin_name = string();
774 _show_regions_draw_order = 0;
777 group->hide_regions();
798 Regions::const_iterator a_ri = regions_a.
begin();
799 Regions::const_iterator b_ri = regions_b.
begin();
801 while (a_ri != regions_a.
end() && b_ri != regions_b.
end()) {
802 if ((*a_ri) < (*b_ri)) {
807 }
else if ((*b_ri) < (*a_ri)) {
830 Regions::iterator ri = lower_bound(regions.
begin(), regions.
end(), ptr);
831 if (ri != regions.
end() && (*ri) == ptr) {
848 Regions::const_iterator ri = lower_bound(regions.
begin(), regions.
end(), ptr);
849 return (ri != regions.
end() && (*ri) == ptr);
859 if (pattern.empty()) {
863 if (region !=
nullptr) {
869 if (button != ButtonHandle::none()) {
879 for (
size_t p = 0; p < pattern.size(); ++p) {
880 if (pattern[p] ==
'%') {
881 string cmd = pattern.substr(p + 1, 1);
884 if (region !=
nullptr) {
885 event += region->get_name();
888 }
else if (cmd ==
"b") {
893 <<
"Invalid symbol in event_pattern: %" << cmd <<
"\n";
900 if (!event.empty()) {
914 nassertv(_lock.debug_is_locked());
920 if (_preferred_button_down_region !=
nullptr) {
921 _preferred_button_down_region->move(param);
930 nassertv(_lock.debug_is_locked());
942 _preferred_button_down_region = _preferred_region;
946 if (_preferred_button_down_region !=
nullptr) {
947 _preferred_button_down_region->press(param);
949 throw_event_pattern(_button_repeat_pattern,
950 _preferred_button_down_region, button);
952 throw_event_pattern(_button_down_pattern,
953 _preferred_button_down_region, button);
960 if (_preferred_region !=
nullptr) {
963 _preferred_region->press(param);
964 consider_keyboard_suppress(_preferred_region);
967 if ((_internal_suppress & MouseWatcherRegion::SF_other_button) == 0) {
972 global_keyboard_press(param);
982 nassertv(_lock.debug_is_locked());
993 if (_preferred_button_down_region !=
nullptr) {
994 param.
set_outside(_preferred_button_down_region != _preferred_region);
995 _preferred_button_down_region->release(param);
996 throw_event_pattern(_button_up_pattern,
997 _preferred_button_down_region, button);
1003 bool has_button =
false;
1004 for (
size_t i = 0; i < MouseButton::num_mouse_buttons; ++i) {
1005 if (MouseButton::_buttons[i] != button &&
1006 _current_buttons_down.
get_bit(MouseButton::_buttons[i].get_index())) {
1013 _button_down =
false;
1014 _preferred_button_down_region =
nullptr;
1020 if (_preferred_region !=
nullptr) {
1021 _preferred_region->release(param);
1025 global_keyboard_release(param);
1033 keystroke(
int keycode) {
1034 nassertv(_lock.debug_is_locked());
1055 consider_keyboard_suppress(region);
1061 group->sort_regions();
1067 consider_keyboard_suppress(region);
1078 candidate(
const std::wstring &candidate_string,
size_t highlight_start,
1079 size_t highlight_end,
size_t cursor_pos) {
1080 nassertv(_lock.debug_is_locked());
1083 param.
set_candidate(candidate_string, highlight_start, highlight_end, cursor_pos);
1104 group->sort_regions();
1122 nassertv(_lock.debug_is_locked());
1130 if (region != _preferred_region && region->
get_keyboard()) {
1131 region->
press(param);
1132 consider_keyboard_suppress(region);
1138 group->sort_regions();
1141 if (region != _preferred_region && region->
get_keyboard()) {
1142 region->
press(param);
1143 consider_keyboard_suppress(region);
1155 nassertv(_lock.debug_is_locked());
1163 if (region != _preferred_region && region->
get_keyboard()) {
1170 group->sort_regions();
1173 if (region != _preferred_region && region->
get_keyboard()) {
1186 nassertv(_lock.debug_is_locked());
1189 throw_event_pattern(_enter_pattern, region, ButtonHandle::none());
1190 if (_implicit_click) {
1193 region->
press(param1);
1203 nassertv(_lock.debug_is_locked());
1205 if (_implicit_click) {
1211 throw_event_pattern(_leave_pattern, region, ButtonHandle::none());
1220 nassertv(_lock.debug_is_locked());
1224 if (!_geometry.is_null()) {
1225 _geometry->set_overall_hidden(
true);
1230 clear_current_regions();
1238 set_mouse(
const LVecBase2 &xy,
const LVecBase2 &pixel_xy) {
1239 nassertv(_lock.debug_is_locked());
1241 if (!_geometry.is_null()) {
1243 _geometry->set_transform(TransformState::make_pos(LVecBase3(xy[0], 0, xy[1])));
1246 _geometry->set_overall_hidden(
false);
1252 _mouse_pixel = pixel_xy;
1255 get_over_regions(regions, _mouse);
1256 set_current_regions(regions);
1270 _external_suppress |= MouseWatcherRegion::SF_other_button;
1288 bool activity =
false;
1292 _internal_suppress = 0;
1293 _external_suppress = 0;
1297 DCAST_INTO_V(pixel_size, input.
get_data(_pixel_size_input).
get_ptr());
1298 output.
set_data(_pixel_size_output, pixel_size);
1299 _pixel_size = pixel_size;
1311 const LVecBase2 &last_f = _xy->get_value();
1317 if (_display_region !=
nullptr) {
1319 if (constrain_display_region(_display_region, f, p, current_thread)) {
1328 _internal_suppress |= MouseWatcherRegion::SF_mouse_button;
1338 _num_trail_recent = 0;
1339 if (input.
has_data(_pointer_events_input) && (_trail_log_duration > 0.0)) {
1341 DCAST_INTO_V(this_pointer_events, input.
get_data(_pointer_events_input).
get_ptr());
1343 for (
size_t i = 0; i < _num_trail_recent; i++) {
1345 int xpos = this_pointer_events->
get_xpos(i);
1346 int ypos = this_pointer_events->
get_ypos(i);
1348 double time = this_pointer_events->
get_time(i);
1349 _trail_log->add_event(in_win, xpos, ypos, sequence, time);
1352 if (_trail_log->get_num_events() > 0) {
1353 discard_excess_trail_log();
1354 update_trail_node();
1356 if (_num_trail_recent > _trail_log->get_num_events()) {
1357 _num_trail_recent = _trail_log->get_num_events();
1363 if (_preferred_region !=
nullptr) {
1364 _internal_suppress |= _preferred_region->get_suppress_flags();
1370 if (input.
has_data(_button_events_input)) {
1372 DCAST_INTO_V(this_button_events, input.
get_data(_button_events_input).
get_ptr());
1374 for (
int i = 0; i < num_events; i++) {
1379 case ButtonEvent::T_down:
1385 press(be._button,
false);
1392 case ButtonEvent::T_repeat:
1394 press(be._button,
true);
1399 case ButtonEvent::T_up:
1402 release(be._button);
1406 case ButtonEvent::T_keystroke:
1409 keystroke(be._keycode);
1413 case ButtonEvent::T_candidate:
1415 candidate(be._candidate_string, be._highlight_start, be._highlight_end, be._cursor_pos);
1419 case ButtonEvent::T_resume_down:
1425 case ButtonEvent::T_move:
1429 case ButtonEvent::T_raw_down:
1430 case ButtonEvent::T_raw_up:
1447 if (_has_inactivity_timeout) {
1453 double elapsed = now - _last_activity;
1456 if (elapsed > _inactivity_timeout) {
1457 switch (_inactivity_state) {
1459 _inactivity_state = IS_active_to_inactive;
1465 case IS_active_to_inactive:
1468 case IS_inactive_to_active:
1469 _inactivity_state = IS_inactive;
1476 switch (_inactivity_state) {
1481 case IS_active_to_inactive:
1483 if (tform_cat.is_debug()) {
1485 <<
"MouseWatcher detected " << _inactivity_timeout
1486 <<
" seconds of inactivity; releasing held buttons.\n";
1489 for (
size_t i = 0; i < _current_buttons_down.
get_num_bits(); ++i) {
1490 if (_current_buttons_down.
get_bit(i)) {
1496 _inactivity_state = IS_inactive;
1497 throw_event(_inactivity_timeout_event);
1500 case IS_inactive_to_active:
1503 for (
size_t i = 0; i < _current_buttons_down.
get_num_bits(); ++i) {
1504 if (_current_buttons_down.
get_bit(i)) {
1510 _inactivity_state = IS_active;
1515 (_internal_suppress & MouseWatcherRegion::SF_mouse_position) == 0) {
1517 _xy->set_value(_mouse);
1519 _pixel_xy->set_value(_mouse_pixel);
1524 int suppress_buttons = ((_internal_suppress | _external_suppress) & MouseWatcherRegion::SF_any_button);
1526 _button_events->clear();
1529 for (
int i = 0; i < num_events; i++) {
1531 bool suppress =
true;
1533 if (be._type != ButtonEvent::T_keystroke &&
1535 suppress = ((suppress_buttons & MouseWatcherRegion::SF_mouse_button) != 0);
1537 suppress = ((suppress_buttons & MouseWatcherRegion::SF_other_button) != 0);
1540 if (!suppress || be._type == ButtonEvent::T_up) {
1542 _button_events->add_event(be);
1546 if (_button_events->get_num_events() != 0) {
1559 LVecBase2 &f, LVecBase2 &p,
1560 Thread *current_thread) {
1561 if (!_button_down) {
1562 _button_down_display_region =
nullptr;
1564 if (_button_down_display_region !=
nullptr) {
1568 display_region = _button_down_display_region;
1575 DCAST_INTO_R(stereo_display_region, display_region,
false);
1576 return constrain_display_region(stereo_display_region->
get_left_eye(), f, p, current_thread) ||
1577 constrain_display_region(stereo_display_region->
get_right_eye(), f, p, current_thread);
1582 PN_stdfloat left, right, bottom, top;
1583 dr_reader.get_dimensions(left, right, bottom, top);
1586 PN_stdfloat x = (f[0] + 1.0f) / 2.0f;
1587 PN_stdfloat y = (f[1] + 1.0f) / 2.0f;
1589 if (_button_down_display_region ==
nullptr &&
1590 (x < left || x >= right || y < bottom || y >= top)) {
1597 _button_down_display_region = display_region;
1601 PN_stdfloat xp = (x - left) / (right - left);
1603 PN_stdfloat xpp = (xp * 2.0f) - 1.0f;
1605 PN_stdfloat yp = (y - bottom) / (top - bottom);
1606 PN_stdfloat ypp = (yp * 2.0f) - 1.0f;
1612 p.set(p[0] - xo, p[1] - yo);